【推荐】【Java编程思想】【笔记】

工欲善其事必先利其器! java

再快不能快基础,再烂不能烂语言! 程序员

第一章 对象导论

"咱们之因此将天然界分解,组织成各类概念,并按其含义分类,主要是由于咱们是整个口语交流社会共同遵照的协定的参与者,这个协定以语言的形式固定下来......除非同意这个协定中规定的有关语言信息的组织和分类,不然咱们根本没法交谈。———Benjamin Lee Whorf( 1897~1941 )"

1.1 抽象过程

  • 面向对象的特性:
    1. 万物皆对象。 将对象视为奇特的变量,它能够存储数据,除此以外,你还能够要求它 在自身上执行操做。理论上讲,你能够抽取待求解问题的任何概念化构建(狗,建筑 物,服务等),将其表示为程序中的对象。
    2. 程序是对象的集合,他们经过发送消息来告知彼此所要作的。 要想请求一个对象,就 必须对该对象发送一条消息。更具体地说,能够把消息想象为对某个特定对象的方法的 调用请求。
    3. 每一个对象都有本身的由其余对象所构成的存储。 换句话说,能够经过建立包含现有对 象的包的方式来建立新类型的对象。所以,能够在程序中构建复杂的体系,同时将其复 杂性影藏在对象的简单性背后。
    4. 每一个对象都拥有其类型。 按照通用的说法,“每一个对象都是某个类(class)的一个实 例(instance)”,这里“类”就是“类型”的同义词。每一个类最重要的区别于其余类的特性 就是“能够发送什么样的消息给它”。
    5. 某一特定类型的全部对象均可以接收一样的消息。 这是一句意味深长的表述,你在稍 后便会看到。由于“圆形”类型的对象一样也是“几何形”类型的对象,因此一个“圆形”对象 一定可以接受发送给“几何形”对象的消息。这意味着能够编写与“几何形”交互并自动处理 全部与几何形性质相关的事务的代码。这种可替代性(substitutability)是OOP中最强 有力的概念之一。
  • 每个对象在内存中都有一个惟一的地址(每个对象均可以惟一地与其余对象区分开来)。

1.2 每一个对象都有一个接口

  • 建立抽象数据类型(类)是面向对象程序设计的基本概念之一。编程

    抽象数据的运行方式与内置(built-in)类型几乎彻底一致:你能够建立某一类型的变量(按 照面向对象的说法,称其为对象或者实例),而后操做这些变量(称其为发送消息或者请 求;发送消息,对象就知道要作什么)。数组

  • 每个对象都属于定义了特性和行为的某个特定的类。安全

  • 由于类描述了具备相同特性(数据元素)和行为(功能)的对象集合,因此一个类实际上就是一个数据类型。bash

  • 接口肯定了对某一特定对象所能发出的请求。并发

1.3 每一个对象都提供服务

  • 高内聚低耦合函数

    内聚: 是从功能角度来度量模块内的联系,一个好的内聚模块应当刚好作一件事。它描述 的是模块内的功能联系;工具

    耦合: 是软件结构中各模块之间相互链接的一种度量,耦合强弱取决于模块间接口的复杂 程度、进入或访问一个模块的点以及经过接口的数据。布局

    高内聚低耦合,是软件工程中的概念,是判断设计好坏的标准,主要是面向对象的设 计,主要是看类的内聚性是否高,耦合度是否低。

    耦合性: 也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模 块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模 块间接口的复杂性、调用的方式及传递的信息

    内聚性: 又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合 的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则 它的内聚性就越高。

    所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常 说的单一责任原则。

    耦合: 一个软件结构内不一样模块之间互连程度的度量。

    对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽量的使其独立存 在。 也就是说,让每一个模块,尽量的独立完成某个特定的子功能。模块与模块之间的 接口,尽可能的少而简单。若是某两个模块间的关系比较复杂的话,最好首先考虑进一步 的模块划分。这样有利于修改和组合。

  • 将对象看做是服务提供者还有一个附带的好处:它有助于提升对象的内聚性。高内聚是 软件设计的基本质量要求之一:这意味着一个软件构造(例如一个对象,固然它也有可能 是指一个方法或一个对象库)的各个方面“组合”得很好。人们在设计对象时所面临的一个 问题是,将过多的功能都塞在一个对象中。

  • 在良好的面向对象设计中,每一个对象均可以很好地完成一项任务,可是它并不试图作更多的事情。

  • 将对象做为服务提供者看待是一件伟大的简化工具,这不只在设计过程当中很是有用,而 且当其余人试图理解你的代码或重用某个对象时,若是他们看出了这个对象所能提供的 服务的价值,它会使调整对象以适应其设计的过程变得简单得多。

1.4 被影藏的具体实现

  • 将程序员开发人员按照角色分为类建立者(那些建立行数据类型的程序员)和客户端程序 员(那些在其应用中使用数据类型的类消费者)是大有裨益的。客户端程序员的目标是收 集各类用来实现快速应用开发的类。类建立者的目标是构建类,这种类只向客户端程序 员暴露必须的部分,而隐藏其余部分。
  • 访问控制存在的缘由
    • 第一个存在的缘由就是让客户端程序员没法触及他们不该该触及的部分。
    • 第二个存在的缘由就是容许库设计者能够改变类内部的工做方式而不用担忧会影响到客户端程序员。
  • JAVA边界值
    • public: 表示紧随其后的元素对任何人都是可用的。
    • private: 关键字表示除类型建立者和类型的内部方法之外的任何人都不能访问的元素。 private就像是你与客户端程序员之间的一堵砖墙,若是有人试图访问private成 就会在编译时获得错误信息。
    • protected: protected关键字与private做用至关,差异仅在于继承的类能够访问 protected成员,可是不能访问private成员。
    • 默认访问权限: 当没有使用前面提到的任何访问指定词时,它将发挥做用。这种权限一般被称为包访问权限,由于在这种权限下,类能够访问在同一个包(库构件)中的其余类的成员,可是在包以外,这些成员如同指定了private同样。

1.5 复用具体实现

  • 代码复用是面向对象程序设计语言锁提供的最了不得的优势之一。

  • 新的类型能够由任意数量,任意类型的其余对象以任意能够实现新的类中想要的功能的方式组成。

  • 由于是在使用现有的类合成新的其余对象的类,因此这种概念被称为组合(composition),若是组合是动态发生的,那么它一般被称为聚合(aggregation)。

  • 在创建新类时,应该首先考虑组合,由于它更加简单灵活。 若是采用这种方式,设计会变得更加清晰,一旦有了一些经验以后,便可以看出必须使用继承的场合了。

1.6 继承

  • 当源类(被称为基类,超类或父类)发生变更时,被修改的“副本”(被称为导出类,继承类或者子类)也会反应出变更。

  • 能够建立一个基类来表示系统中某些对象的核心概念,从基类型中导出其余类型,来表示此核心能够被实现的各类不一样方式。

  • 当继承现有类型时,也就建立了新的类型。 这个新的类型不只包括如今类型的全部成员(尽管private成员被隐藏起来,而且不可访问),并且更重要的是它复制了基类的接口。也就是说,全部能够发送给基类对象的消息同时也能够发送到导出类对象。

  • 经过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛!

  • 有两种方法可使基类与导出类产生差别

      1. 直接在导出类中添加新方法。这些新方法并非基类接口的一部分。这意味着基类并不能知足你的全部需求,所以必需添加更多的 方法。
      1. 使导出类和基类之间产生差别的方法是改变现有基类的方法的行为,这被称之为覆盖(overriding)那个方法。
  • 继承应该只覆盖基类的方法(而不添加在基类中没有的新方法),在某种意义上,这是一种处理继承的理想方式。咱们常常将这种状况下的基类与导出类之间的关系称为is-a(是一个)关系。

  • 有时必须在导出类型添加新的接口元素,这样也就扩展了接口。这种状况咱们能够描述为is-like-a(像是一个)关系。

1.7 伴随多态的可互换对象

  • 经过导出新的子类型而轻松扩展设计的能力是对改动进行封装的基本方式之一。

    这种能力能够极大地改善咱们的设计,同时也下降软件维护的代价。

  • 面向对象程序设计的最重要妙诀:编译器不可能产生传统意义上的函数调用。 一个非面向对象编程的编译器产生的函数调用会引发所谓的前期绑定 ,这个术语你可能之前从未据说过,可能从未想过函数调用的其余方式。这么作意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。然而在OOP(面向对象编程)中,程序直到运行时才可以肯定代码的地址,因此当消息发送到一个泛化对象时,必须采用其余的机制。

  • 当向对象发送消息时,被调用的代码直到运行时才能肯定。 编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(没法提供此类保证的语言被称为是弱类型的),可是并不知道将被执行的确切代码。

  • 在java中,动态绑定是默认行为,不须要添加额外的关键字来实现多态。

  • 把将被导出类看作是它的基类的过程称为向上转型。

    转型这个名称的灵感来自于模型铸造的塑模动做;而向上(up)这个词来源于继承图的典型布局方式;一般基类在顶部,而导出类在其下部散开。所以,转型为一个基类就是在继承图中向上移动,即“向上转型”。

  • 正是由于多态才使得事情老是可以被正确处理。编译器和运行系统会处理相关的细节,你须要立刻知道的只是事情会发生,更重要的是怎样经过它来设计。当向一个对象发送消息时,即便涉及向上转型,该对象也知道要执行什么样的正确行为。

1.8 单根继承结构

  • 在OOP中,是否全部的类最终都继承自单一的基类?答案是yes,这个终极基类的名字就是Object。 事实证实,单根继承结构带来了不少好处。
  • 在单根继承结构中的全部对象都具备一个共用的接口,因此它们归根到底都是相同的基本类型。
  • 单根继承结构保证全部对象都具有某些功能。
  • 单根继承结构使垃圾回收器的实现变得容易不少,而垃圾回收器正是Java相对C++的重要改进之一。 因为全部对象都保证具备其类型信息,所以不会因没法肯定对象的类型而陷入僵局。这对于系统级操做(如异常处理)显得尤为重要,而且给编程带来了更大的灵活性。

1.9 容器(集合)

  • 容器(也称为集合,不过java类库以不一样的含义使用“集合”这个术语,因此本书使用“容器”这个词),在任何须要时均可扩充本身以容纳你置于其中的全部东西。所以不须要知道未来会把多少个对象置于容器中,只须要建立一个容器对象,而后让它处理全部细节。

  • Java容器

    • List(用于存储序列)
    • Map(也被称为关联数组,是用来创建对象之间的关联)
    • Set(每种对象类型只持有一个)
    • 诸多队列,树,堆栈等更多的构件。
  • 使用时仍是要对容器有所选择,缘由以下:

    • 不一样容器提供了不一样类型的接口和外部行为。堆栈相比于队列就具有不一样的接口和行为,也不一样于集合列表的接口和行为。它们之中的某些容器提供的解决方案可能要比其余容器灵活得多。
    • 不一样的容器对于某些操做具备不一样的效率。最好的例子就是两种List的比较:ArrayList和LinkedList。它们都是具备相同接口和外部行为的简单的序列,可是它们对于某些操做所花费的代价却有天壤之别。在ArrayList中,随机访问元素是一个花费固定时间的操做;可是,对于LinkedList来讲,随机选取元素须要在列表中移动,这种代价是高昂的,访问越靠近表尾的元素,花费的时间越长。而另外一方面,若是想在序列中间插入一个元素,LinkedList的开销却比ArrayList要小。
  • 咱们能够在一开始使用LinkedList构建程序,而在优化系统性能时改用ArrayList。接口List所带来的抽象,把在容器之间进行转换时对代码产生的影响下降到最小限度。

  • 参数化类型(范型) 在JavaSE5以前,容器存储的对象都只具备Java中的通用类型:Object。

    单根继承结构意味着全部东西都是Object类型,因此能够存储Object的容器能够存储任何东西。这使得容器很容易被复用。

  • 向上转型是安全的,例如Circle是一种Shape类型;可是不知道某个Object是Circle仍是Shape,因此除非确切知道所要处理的对象的类型,不然向下转型几乎是不安全的。

  • 若是向下转型错误的话,就会获得被称为异常的运行时错误。

  • 参数化类型(范型)就是一个编译器能够自动定制用于特定类型上的类。 例如,经过使用参数化类型,编译器能够定制一个只接纳和取出Shape对象的容器。

  • 为了利用泛型的优势,不少标准类库构建都已经进行了修改。就像咱们将要看到的那样,范型对本书中的许多代码都产生了重要的影响。

1.10 对象的建立和生命期

  • 在使用对象时,最关键的问题之一即是它们的生成和销毁方式。

    每一个对象为了生存都须要资源,尤为是内存。当咱们再也不须要一个对象时,它必须被清理掉。使其占有的资源能够被释放和重用。

  • 为了追求最大的执行速度,对象的存储空间和生命周期能够在编写程序时肯定。

    • 这能够经过将对象置于堆栈(它们有时被称为自动变量)或限域变量或静态存储区域内来实现。

      这种方式将存储空间分配和释放置于优先考虑的位置,某些状况下这样的控制很是有价值。可是,也牺牲了灵活性,由于必须在编写程序时知道对象确切的数量,生命周期和类型。

    • 第二种方式是在被称为堆(heap)的内存池中动态地建立对象。

      在这种方式中,直到运行时才知道须要多少对象,它们的生命周期如何,以及它们的具体类型是什么。

  • 若是须要一个新对象,能够在须要的时刻直接在堆中建立。由于存储空间是在运行时被动态管理的,因此须要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中建立存储空间的时间。

  • 建立堆栈存储空间的时间依赖于存储机制的设计

  • 动态方式有这样一个通常性的逻辑假设:对象趋向于变得复杂。因此查找和释放存储空间的开销不会对对象的建立形成重大冲击。动态方式所带来的更大的灵活性正是解决通常化编程问题的要点所在。

  • java彻底采用动态内存分配方式。每当想要建立新对象时,就要使用new关键字来构建此对象的动态实例。

  • 还有一个议题,就是生命周期。对于容许在堆栈上建立对象的语言,编译器能够肯定对象存活的时间,并能够自动销毁它。

  • java提供了被称为“垃圾回收器”的机制,它能够自动发现对象什么时候再也不被使用,并继而销毁它。

    垃圾回收器很是有用,由于它减小了所必须考虑的议题和必须编写的代码。更重要的是,垃圾回收器提供了更高层的保障,能够避免暗藏的内存泄漏问题。

  • java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其余方面)。垃圾回收器“知道”对象什么时候再也不被使用,并自动释放对象占用的内存。

1.11 异常处理:处理错误

  • 异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。异常处理就像是与程序正常执行路径并行的、在错误发生时执行的另外一条路径。由于它是另外一条彻底分离的执行路径,因此它不会干扰正常的执行代码。
  • 被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样能够被忽略,异常不能被忽略,因此它保证必定会在某处获得处理。
  • 异常提供了一种从错误情况进行可靠恢复的途径。如今再也不是只能退出程序,你能够常常进行校订,并恢复程序的执行,这些都有助于编写出更健壮的程序。

1.12 并发编程

  • 在计算机编程中有一个基本的概念,就是在同一时刻处理多个任务的思想。
  • 在程序中,彼此独立运行的部分称之为线程。
  • 对于大量的问题,把问题切分红多个可独立运行的部分(任务),从而提升程序的响应能力。称之为“并发”。
  • 线程只是一种为单一处理器分配执行时间的手段。
  • 若是有多个并行任务都要访问同一项资源,那么就会出问题。例如,两个进程不能同时向一台打印机发送信息。为了解决这个问题,能够共享的资源,例如打印机,必须在使用期间被锁定。所以,整个过程是:某个任务锁定某项资源,完成其任务,而后释放资源锁,使其余任务可使用这项资源。

第2章 一切都是对象

"若是咱们说另外一种不一样的语言,那么咱们就会发觉一个有些不一样的世界。——Luduing Wittgerstein( 1889~1951 )"

2.1 用引用操纵对象

  • java中,一切都被视为对象,所以可采用单一的语法。尽管一切都看做是对象,但操做的标识符其实是对象的一个“引用”。

    例:遥控器(引用)操做电视机(对象)。

    • java操做的标识符其实是对象的一个“引用”
    • java操做的标识符其实是对象的一个“引用”
    • java操做的标识符其实是对象的一个“引用”

2.2 必须由你建立全部对象

  • new 关键字的意思是“给我一个新对象”。
  • 存储数据的地方:
    • 寄存器: 最快的存储区,但因为寄存器的数量极其有限,因此根据需求进行分配。你不能直接控制,在程序中也感受不到寄存器存在的任何迹象。

    • 堆栈: 位于通用RAM(随机访问存储器)中。堆栈指针若往下移则分配新内存,若往上移动则释放内存。

      堆栈:就是STACK。其实是只有一个出入口的队列,即后进先出(First In Last Out),先分配的内存一定后释放。通常由系统自动分配,存放函数的参数值,局部变量等,自动清除。

      堆栈是每一个函数进入的时候分一小块,函数返回的时候就释放了

      局部变量放在堆栈中,因此函数返回,局部变量就全没了。

    • 堆: 一种通用的内存池(位于RAM区),用来存放全部的Java对象。

      当须要一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。

      用堆进行存储分配和清理可能比用堆栈进行存储分配须要更多的时间。

    • 常量存储: 常量值一般是直接存放在程序代码内部。

    • 非RAM存储: 若是数据彻底存活于程序以外,那么它能够不受程序的任何控制,在程序没有运行也能够存在。例如:流对象和持久化对象。

  • 基本数据类型
    • 基本数据类型不用new来建立对象,而是建立一个并不是是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,所以更加高效。
    • 全部数据类型都有正负号,没有无符号的数值类型。
    • 基本类型具备的包装器类,使得能够在堆中建立一个非基本对象,用来表示对应的及本类型。
    • java SE5的自动包装功能将自动地将基本数据类型转换成包装器类型。
    • java提供了两个用于高精度计算的类:BigInteger(支持任意精度的整数)和BigDecimal(支持任何精度的定点数)。

2.3 永远不须要销毁对象

  • 做用域
    • 做用域由花括号的位置决定。
    • 做用域里定义的变量只可用于做用域结束以前。
  • 对象的做用域
    • 当new建立一个java对象时,它能够存活于做用域以外。
    • java有一个垃圾回收器,用来监视用new建立的全部对象,并辨别那些不会再被引用的对象。随后,释放这些对象的内存空间,以便提供其余新的对象使用。

2.4 建立新的数据类型:类

  • class这个关键字以后紧跟的是新类型的名称。
  • 在java中你所作的所有工做就是定义类,产生那些类的对象,以及发送消息给这些对象。
  • 字段能够是任何类型,能够经过其引用与其进行通讯,也能够是基本类型的一种。
  • 若是字段是对某个对象的引用,那么必须初始化该引用,以便使其与一个实际的对象相关联。

2.5 方法、参数和返回值

  • java的方法决定了一个对象可以接收什么样的消息,组成(名称、参数、返回值、方法)。
  • 方法名和参数列表惟一地标识出某个方法。
  • 调用方法的行为一般被称为发送消息给对象。
  • 面向对象的程序设计一般简单地概括为“向对象发送消息”。
  • 一般,尽管传递的是对象,而实际上传递的是对象的引用。

2.6 构建一个java程序

  • java经过域名来保证每个类都是独一无二的。
  • static关键字
    • 执行new来建立对象时,数据存储空间才被分配,其方法才供外界调用。
    • new建立对象知足不了的两种场景:
        1. 只想为某特定域分配单一存储空间,而不去考虑究竟要建立多少对象,甚至根本就不建立任何对象。
        1. 但愿某个方法不与包含它的类的任何对象关联在一块儿。也就是说没有建立对象,也可以调用这个方法。
      • 【解决】当声明一个事物是static时,就意味着这个域或方法不会与包含它的类的任何对象实例关联在一块儿。
    • 使用static方法(或域)前不须要建立任何对象。
    • 一个static字段对每一个类来讲只有一份存储空间,而非static字段则是对每一个对象都有一个存储空间

第3章 操做符

3.7 关系操做符

  • new出来的两个Integer,尽管对象内容相同,但对象的引用倒是不一样的,== 和 != 比较的就是对象的引用。
  • 若是想比较两个对象实际内容是否相同,必须使用全部对象都适用的equals()
  • 基本类型直接使用== 和 !=

第4章 控制执行流程

  • java不容许咱们将一个数字做为布尔值使用!
  • return关键字有两方面的用途:
    • 一方面指定一个方法返回什么值
    • 另外一方面它会致使当前的方法退出,并返回那个值。
  • break用于强行退出循环,不执行循环中剩余的语句。
  • 而continue则中止执行当前的迭代,而后退回循环起止处,开始下一次迭代。

第5章 初始化与清理

5.1 用构造器确保初始化

  • 构造器采用与类相同的名称,在建立对象时,new XXX()将会为对象分配存储空间,并调用相应的构造器。这就确保了在操做对象以前,它已经初始化了。
  • 每一类都会有一个默认构造器,java文档中称为“无参构造器”。
  • 构造器是一种特殊类型的方法,没有返回值。
  • 在java中,“初始化”和“建立”捆绑在一块儿,二者不能分离!

5.2 方法重载

  • 重载:方法名相同而形式参数不一样。
  • 每一个重载的方法都必须有一个独一无二的参数类型列表。
  • 不能够根据返回值来区分重载方法。
  • 重载最多见的应用场景就是构造器重载!

5.3 默认构造器

  • 默认构造器(又名“无参”构造器)是没有形式参数的——它的做用是建立一个“默认对象”。
  • 假如类中没有构造器的话,编译器会自动帮你建立一个默认构造器。但假如你已写了一个构造器(有参构造),则会自动覆盖掉默认构造器(也就是说此时类里面没有默认的构造器(无参构造),若是你还须要无参构造这时你要本身手动添加一个无参构造)。

5.4 this关键字

class Banana { void peel(int i) {/*...*/} }

public class BananaPeel{
    public static void main(String[] args) {
        Banana a = new Banana();
        Banana b = new Banana();
        a.peel(1);
        b.peel(2);
    }
}
复制代码

【问题】peel()如何知道是被a仍是被b调用的呢?

【答】:为了能简便面向对象的语法来编写代码,编译器暗自把“操做对象的引用”做为第一个参数传递给peel(),
       a.peel(a,1);这是内部表现形式。
复制代码
  • 若是在方法内部的时候须要得到当前对象的引用,可使用this关键字!
  • this关键字只能在方法的内部调用!
  • 一般写 this 的时候,都是指“这个对象”或者“当前对象”,并且它自己表示对当前对象的引用。
  • 能够用this调用一个构造器,但却不能调用两个,并且必须将构造器置于最起始处,不然会报错。
  • 除构造器之外,编译器禁止在其余任何方法中调用构造器!
  • static 就是没有this的方法
  • static方法的内部不能调用非静态方法
  • static能够在没有建立对象的前提下,仅仅经过类自己来调用static方法。

5.5 清理:终结处理与垃圾回收

  • java中的垃圾回收器负责回收无用对象占据的内存资源。

  • 垃圾回收器只知道释放由new分配的内存。

  • finalize():在垃圾回收器准备好释放回收对象占用的存储空间以前调用的方法,能够自定义作一些清理工做。

    【问题】finalize()和析构函数的区别:

    析构函数(C++中销毁对象必须用这个函数):“析构”函数与构造函数相反,当对象结束其生命周期,
      如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数每每用来作“清理善后”的工做。
    复制代码
  • Java对象并不是老是被垃圾回收:

    1. 对象可能不被垃圾回收。
    2. 垃圾回收并不等于“析构”。
    3. 垃圾回收只与内存有关。
  • 在java中,只要程序没有濒临存储用完的那一刻,对象占用的空间就总也得不到释放。

  • 使用垃圾回收器的惟一缘由是为了回收程序再也不使用的内存。

  • finalize()的用途之——释放空间:

    1. 不管对象是如何建立的,垃圾回收器都会负责释放对象占据的全部内存。

    当代码中使用“本地方法”的状况下,好比调用C的malloc()函数系列来分配存储空间时,须要在finalize中调用free()来释放内存空间。

    注:本地方法是一种在java中调用非java代码,通常用“native”关键字修饰。

  • finalize()的用途之——终结条件验证:

    在对象资源被释放以前验证对象的某个状态,将验证内容放在finalize()的方法中,能够避免一些因为代码书写问题致使的缺陷。

    注:【Effective Java】中“避免使用终结方法和清楚方法”一节中提到:终结方法(finalize)一般是不可预测的,也是很危险的,通常状况下是没必要要的。使用终结方法会致使行为不稳定,性能下降,以及可移植性问题,根据经验,应该避免使用终结方法。

  • 垃圾回收器如何工做

    在堆上分配对象代价是十分高昂的,所以java中全部对象的分配方式也是很是高昂的。然而垃圾回收器对于提升对象的建立速度,却具备明显的效果。

    • 在某些java虚拟机中,堆的实现就至关与一个传送带,每分配一个对象,它就往前移动一格,这意味着对象存储的分配速度很是快。
    • java的堆并未彻底像传送带那样工做。
    • 当它工做时,一面回收空间,一面使堆中的对象紧凑排列,这样“堆指针”就能够很容易移动到更靠近传送带的开始处,也就避免来页面错误。经过垃圾回收器对对象重写排列,实现了一种高速的,有无限空间可供分配的堆模型。
  • 垃圾回收器的实现:

    垃圾回收器依据的思想是:对任何“活”的对象,必定能最终追溯到其存在在堆栈或静态存储区之中的引用。

    • 引用计数器: 是一种简单但速度很慢的垃圾回收技术。

      每一个对象都含有一个引用计数器,当有引用链接至对象时,引用计数加1。当引用离开做用域或设置为null的时候,引用计数器减1。垃圾回收器会在含有所有对象的列表上便利,当发现某个对象的引用计数为0时,就释放其占用的空间。但循环引用会致使“对象应该被回收,计数却不为0”。

      引用计数未被应用于任何一种java虚拟机实现中。

    • 中止,复制:

      先暂停程序的运行(不属于后台回收模式),而后将全部存活的对象从当前堆复制到另外一个堆,没有被复制的所有都是垃圾。当把对象从一处搬到另外一处时,全部指向它的那些引用都必须修正了。位于堆或静态存储的引用能够直接被修改,但还有其余指向的引用,在遍历的过程才能被找到。

      这种方式,效率会很低。一方面须要在两个分离的堆之间来回倒腾;另外一方面是程序稳定后,可能只会产生少许垃圾,甚至没有垃圾,但仍然会将全部的内存自一处复制到另外一处。

      垃圾回收动做不是在后台进行的,垃圾回收动做发生时,程序会被暂停。

      “中止-复制”要求在释放旧的对象以前,必须把全部存活的对象从旧堆里复制到新堆。这将致使大量内存复制行为。

    • 标记,清扫

      从堆栈和静态存储区出发,遍历全部的引用,进而找出全部存活的对象。每当找到一个存活对象,就会给对象设一个标记,但这时不回收任何对象。只有所有标记工做完成,才开始清理。没有标记的将被释放,有标记才会进行复制动做。必须在程序暂停的状况下才能进行。

    • “自适应的,分代的,中止-复制,标记-清扫”式垃圾回收器

      • 内存分配以较大的“块”为单位,若是对象大,它会占用独立的块。使用块之后,垃圾回收器在回收的时候就能够往废弃的块里拷贝对象了。
      • 每一个块都有相应的 代数(generation count) 来记录它是否还存活。
      • 若是块在某处被引用,其代数会增长,垃圾回收器将对上次回收动做以后新分配的块进行整理(对于处理大量短命的临时对象颇有益处)。
      • 垃圾回收器会按期进行完整的清理动做——大型对象仍然不会被复制(只是其代数会增长),内含小型对象的那些快则被复制并整理。
      • java虚拟机会进行监视,若是全部对象都很稳定,垃圾回收器效率下降的话,就切换到 “标记-清扫” 方式;
      • java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现不少碎片,就会切换到 “中止-复制” 方式; 根据不一样的状况切换不一样的方式,称为“自适应”技术。

5.7 构造器初始化

  • 类中的变量初始化在构造器以前

  • 不管建立多少个对象,静态数据都只占一份存储区域。

  • static关键不能应用于局部变量,只能做用于域。

  • 若是是一个静态的基本类型域,且没有初始化,那么它就会得到基本类型的标准初值。

    若是它是一个对象引用,那么它的默认初始值就是null。

  • 静态存储初始化时间:

    类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!

    类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!

    类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!

  • 初始化的顺序是先静态对象,然后“非静态”对象。

  • 对象的建立过程

    1. 若没有显示地使用static方法,构造器实际上也是static方法,当对象被建立或者类的静态方法/静态域被首次访问时,java解释器要找到类路径,而后定位.class文件。
    2. 载入.class文件,有关静态初始化的全部动做都会执行。所以,静态初始文件只在Class对象首次加载的时候进行一次。
    3. 当使用new()建立的时候,将在堆上为对象分配足够的空间。
    4. 这块存储空间会被清零,将全部基本数据都设置为默认值,引用被设置为null。
    5. 执行全部出现于字段定义处的初始化动做。
    6. 执行构造器。

5.10 总结

  • 构造器能保证正确的初始化和清理(没有正确的构造器调用,编译器就不容许建立对象),因此有了彻底的控制,也很安全。
  • 垃圾回收器会自动为对象释放内存,因此在不少场合下,相似的清理方法在java中就不太须要了。
  • java的垃圾回收器能够极大地简化编程工做,并且在处理内存的时候也更安全。

【推荐篇】- 书籍内容整理笔记 连接地址
【推荐】【Java编程思想】【笔记】 juejin.im/post/5dbb7a…
【推荐】【Java核心技术 卷Ⅰ】【笔记】 juejin.im/post/5dbb7b…

后期持续更新中。。。。。。

如有书写错误的地方,欢迎留言,但愿咱们能够一块儿进步,一块儿加油!😜😜

相关文章
相关标签/搜索