前言:随着计算机革命的发展,不安全的编程方式已逐渐成为编程代价高昂的主要原因之一。不正确的初始化将导致大量的错误,并且这种错误很难发现。同时,不正确的清理也会导致类似的问题。在Java中使用构造器来保证初始化,使用垃圾回收机制来进行垃圾的清理。
一、 构造器
构造器是一个方法,在你使用这个对象之前,必须调用这个方法来保证每个对象都进行了初始化。构造器是由Java编译器进行调用的,因此在得到对象之前,编译器会自动调用该方法从而保证了初始化的进行。
构造器是一种特殊类型的方法,因为其没有返回值。这与返回值为void不同。(实验好像没有什么不同的)
名称: 构造器的名称要解决两个问题, 一是构造器的名称和成员名称冲突问题,二是让编译器识别构造器的名称。 因此Java采用了和C++语言中一样的解决方案,使用类名来作为构造器的名称。当方法使用类名时,编译器会给出警告, 不过方法的命名规则是首字母小写。
默认构造器: 当编写一个类时,如果你的类中没有构造器,那么编译器会自动帮你创建一个构造器。这个构造器也称为默认构造器。 相当于在类中创建了一个无参的构造器如:
class Rock{
Rock(){}
}
如果你在类中创建了一个构造器,那么编译其不会再帮你创建一个默认的构造器了,因此你必须使用你自己创建的构造器进行构建对象。
重载: 由于构造器的名称是固定的,但是如果你想要以不同的方式创建一个对象,那么一个构造器显然是不够的。 因此要对构造器进行重载,或者说,因为构造器,所以有了重载。 对方法重载的区分是通过方法名和参数列表进行的,这里构造器名称相同,因此构造器只能通过参数列表进行重载。
this关键字: 可以为一个类创建多个对象,那么在一个类中进行方法调用是如何区分不同的对象的呢? 这就是this关键字起作用的地方。在类的方法中,编译器会将当前对象的引用传递给方法。this关键字也可以作用在构造器中来进行构造器的调用。在构造器中,使用this() 必须置到方法的第一行。
二、 清理: 终结处理和垃圾回收
终结处理: Java中的对象通常是由垃圾回收机制进行的,但是也有例外。 如果你的对象获得到一个特殊的内存(通常是有本地方法调用,使用Java调用非java代码如C/C++。 开辟出特殊的内存),Java的垃圾回收机制无法处理此特殊内存。 因此, Java允许在类中定义一个名为finalize()方法。其工作原理: 当垃圾回收器准备回收内存时,会先调用finalize()方法,在下一次垃圾回收动作发生时,进行真正的内存回收。
注意: 无论是垃圾回收还是finalize方法都不保证一定会运行。如果Java虚拟机未面临内存耗尽的情况是不会浪费时间去执行垃圾回收的。
垃圾回收机制: 垃圾回收机制对于提高对象的创建速度有明显的效果,因为垃圾回收机制在回收内存的同时会对内存中的碎片进行整理,使得内存更加紧凑,在分配内存时更加快速。 这就使得Java从堆空间中分配的速度可以和其它语言从栈上分配空间的速度相媲美。
下面讨论集中垃圾回收机制:
1. 引用计数机制: 通过对每一个对象进行引用计数,当有引用指向对象时, 计数器加一,离开时计数器减一来实现。这是最简单的机制,但是存在引用循环。
2. 引用遍历机制(个人取的): 通过从堆栈和静态存储区中,对存活的对象进行遍历,找出所有存活的对象。
基于第二种机制,Java有两种方式:
1. 停止-复制方式: 此机制首先暂停程序的运行,然后将所有存活的对象从当前堆中复制到另一个堆,没有复制的对象都是垃圾。 当对象被复制时,其保持新堆的紧凑排列。在复制过程中需要对指向对象的引用进行修正。
问题: 维护两个堆,在程序进入稳定状态时只产生少量的垃圾,复制回收不是很值得。
2. 标记-清扫: 为了针对以上两个问题,sun公司提出了标记-清扫模式,其思路同样时基于第二种机制,从堆栈和静态存储区出发,遍历所有引用,找出所有存活的对象并进行标记。当标记完成时,开始清理。清理过程中释放没有标记的对象,不会发生复制行为,因此剩下的空间不是连续的。标记-清扫也必须在程序暂停的情况下进行。
Java 虚拟机会对内存进行监视,如果所有对象稳定,垃圾回收器的效率很低的话,就会切换到标记-清扫方式,要是堆空间出现很多碎片的话,就会切换到停止-复制方式,这就是自适应的垃圾回收技术。