[Java编程思想] 第五章 初始化与清理
第五章 初始化与清理
5.1 用构建器确保初始化
可以想象为每个类都定义一个initialize()方法,与类同名,让编译器在初始化期间自动调用。确保在你能操作对象之前,它已经被恰当地初始化。
从概念上讲“初始化”和“创建”是彼此独立的,在Java中,“初始化”和“创建”捆绑在一起,两者不能分离。
构造器是一种特殊类型的方法,因为它没有返回值。(new表达式确实返回了对新建对象的引用,但构造器本身并没有返回任何值)。
5.2 方法重载
每个重载的方法都必须有一个独一无二的参数类型列表。甚至参数顺序不同也足以区分两个方法。
如果传入的数据类型小于方法中声明的形式参数类型,实际数据类型就会被提升。
5.3 默认构造器
如果你的类中没有构造器,则编译器会自动帮你创建一个默认构造器,但是如果已经定义一个构造器,无论是否有参数,编译器就不会帮你自动创建默认构造器。
5.4 this关键字
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。当需要返回当前对象(class类)的引用时,可以这样返回return this;
有时为了将自身传递给外部方法,可以使用this关键字。另外,如果参数s的名称和数据成员s的名称相同,可以使用this.s来代表数据成员(类属性)就能解决。
class Apple{
Apple getPeeled(){
return Peeler.peel(this);
}
}
class Peeler{
static Apple peel(Apple apple){
// 这个apple就是上面Apple的this
return apple;
}
}
5.5 在构造器中调用构造器
除构造器外,编译器禁止在其他任何方法中调用构造器。
尽管可以用this调用一个构造器,但却不能调用两个,此外,必须将构造器调用置于最起始处,否则编译器报错。
5.6 清理:终结处理和垃圾回收
一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一垃圾回收动作发生时,才会真正回收对象占用的内存。
- 对象可能不被垃圾回收。
- 垃圾回收不等于“析构”。
- 垃圾回收只与内存有关。
记住,无论是“垃圾回收”还是终结,都不保证一定发生。如果Java虚拟机并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存。
5.7 垃圾回收器如何工作
引用计数常用来说明垃圾收集器的工作方式,但似乎从未被应用于任何一种Java虚拟机实现中。引用计数是一种简单但速度很慢的垃圾回收技术,每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加1,当引用离开作用域或被置为null时,引用计数减1。如果对象之间存在循环引用,则会出现“对象应该被回收,但引用计数却不为零”。
在一些更快的模式,并非基于引用计数。它们的依据思想是:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。
Java虚拟机将采用一种自适应垃圾回收技术,至于如何处理存活对象,取决于不同的Java虚拟机实现。
“停止-复制”,显然这意味着,先暂停程序运行,然后将所有存活对象从当前堆复制到另一个堆,没有被复制的全是垃圾。当到新堆时,它们是一个挨着一个的。效率低,一是因为复制堆需要多一倍的空间,二是垃圾少时仍要复制。
所以一些Java会进行检查:要是没有新垃圾产生,就会转到另一种工作模式“标记-清扫”,一般而言,标记-清扫速度相当慢,当垃圾少时,速度就很快了。
这里讨论的虚拟机中,内存分配一较大的“块”为单位。每个块都用相应的“代数”来记录是否存活,停止-复制方法就会往废弃的块中拷贝对象,当块在某处被引用时,代数增加。垃圾回收器定期清理,大型对象仍然不会被复制(只是代数增加),小对象的块则被清理。如果对象很稳定,就会切换到标记-清扫;同样如果清扫效果不理想,出现很多碎片,就会切换回停止-复制。
自适应的、分代的、停止-复制、标记-清扫。
《深入理解Java虚拟机》中分为:分代、标记-清除、标记-复制、标记-整理算法。
5.8 成员初始化
局部变量必须初始化;成员变量可以不初始化。
5.9 构造器初始化
所有成员变量早有构造器初始化。初始化的顺序是先静态对象(如果它们尚未因为前面对象创建时被初始化过),而后是“非静态”对象。main()方法是静态方法。
假设有个名为Dog的类,创建过程如下:
- 即使没有显示地使用static关键字 ,构造器实际上也是静态方法。因此,当首次创建类型为Dog地对象时,或者Dog类地静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
- 然后载入Dog.class,有关静态初始化的动作都会执行。因此静态初始化只是在Class对象首次加载的时候进行一次。
- 当用new Dog()创建对象地时候,首先将在堆上为Dog对象分配足够地存储空间。
- 这块存储空间将被清零,这就自动地将Dog对象中所有基本数据类型设成默认值,而引用被设置成null。
- 执行所有出现于字段定义出的初始化动作。
- 执行构造器。
这可能会牵涉到很多动作,尤其是涉及继承的时候,父类静态-子类静态-父类成员-父类构造-子类成员-子类构造。如果存在子类重写父类方法要进一步分析,本质上还是子类在调用super(),执行的是子类重写过的方法。