我以前发布了关于java.lang.Object类及其方法的一系列文章。在介绍了Object以后,咱们又探究了clone()和euqals()方法。在这篇文章中,咱们将继续讨论Object中的finalize()、getClass()和hashCode()方法。 html
问: finalize()方法是用来作什么的? java
答: finalize()方法能够被子类对象所覆盖,而后做为一个终结者,当GC被调用的时候完成最后的清理工做(例如释放系统资源之类)。这就是终止。默认的finalize()方法什么也不作,当被调用时直接返回。 数组
对于任何一个对象,它的finalize()方法都不会被JVM执行两次。若是你想让一个对象可以被再次调用的话(例如,分配它的引用给一个静态变量),注意当这个对象已经被GC回收的时候,finalize()方法不会被调用第二次。 安全
问: 有人说要避免使用finalize()方法,这是真的吗? ide
答: 一般来说,你应该尽可能避免使用finalize()。相对于其余JVM实现,终结器被调用的状况较少——多是由于终结器线程的优先级别较低的缘由。若是你依靠终结器来关闭文件或者其余系统资源,可能会将资源耗尽,当程序试图打开一个新的文件或者新的系统资源的时候可能会崩溃,就由于这个缓慢的终结器。 ui
问: 应该使用什么来替代终结器? this
答: 提供一个明确的用来销毁这个对象的方法(例如,java.io.FileInputStream的void close()方法),而且在代码中使用try - finally结构来调用这个方法,以确保不管有没有异常从try中抛出,都会销毁这个对象。参考下面释放锁的代码: spa
1
2
3
4
5
6
7
8
9
10
|
Lock l = ...;// ... is a placeholder for the actual lock-acquisition code
l.lock();
try
{
// access the resource protected by this lock
}
finally
{
l.unlock();
}
|
这段代码保证了不管try是正常结束仍是抛出异常都会释放锁。 线程
问: 什么状况下适合使用终结器? code
答: 终结器能够做为一个安全保障,以防止声明的终结方法(像是java.io.FileOutputStream对象的close()方法或者java.util.concurrent.Lock对象的Lock()方法)没有被调用。万一这种状况出现,终结器能够在最后被调用,释放临街资源。
问: 怎么写finalize()?
答: 能够遵循下面这个模式写finalize()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Override
protectedvoidfinalize()throwsThrowable
{
try
{
// Finalize the subclass state.
// ...
}
finally
{
super.finalize();
}
}
|
子类终结器通常会经过调用父类的终结器来实现。当被调用时,先执行try模块,而后再在对应的finally中调用super.finalize();这就保证了不管try会不会抛出异常父类都会被销毁。
问: 若是finalize()抛出异常会怎样?
答: 当finalize()抛出异常的时候会被忽略。并且,对象的终结将在此中止,致使对象处在一种不肯定的状态。若是另外一个进程试图使用这个对象的话,将产生不肯定的结果。一般抛出异常将会致使线程终止并产生一个提示信息,可是从finalize()中抛出异常就不会。
问: 我想实践一下finalize()方法,能提供一个范例吗?
答: 参考代码清单1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
classLargeObject
{
byte[] memory =newbyte[1024*1024*4];
@Override
protectedvoidfinalize()throwsException
{
System.out.println("finalized");
}
}
publicclassFinalizeDemo
{
publicstaticvoidmain(String[] args)
{
while(true)
newLargeObject();
}
}
|
代码清单1中的代码写了一个FinalizeDemo程序,重复地对largeObject类实例化。每个Largeobject对象将产生4M的数组。在这种状况下,因为没有指向该对象的引用,因此LargeObject对象将被GC回收。
GC会调用对象的finalize()方法来回收对象。LargeObject重载的finalize()方法被调用的时候会想标准输出流打印一条信息。它没有调用父类的finalize()方法,由于它的父类是Object,即父类的finalize()方法什么也不作。
编译(javac FinalizeDemo.java)并运行(java FinalizeDemo)代码清单1.当我在个人环境下(64位win7平台)使用JDK7u6来编译运行的时候,我看到一列finalized的信息。可是在JDK8的环境下时,在几行finalized以后抛出了java.lang.OutOfMemoryError。
由于finalize()方法对于虚拟机来讲不是轻量级的程序,因此不能保证你必定会在你的环境下观察到输出信息。
问: getClass()方法是用来作什么的?
答: 经过getClass()方法能够获得一个和这个类有关的java.lang.Class对象。返回的Class对象是一个被static synchronized方法封装的表明这个类的对象;例如,static sychronized void foo(){}。这也是指向反射API。由于调用getClass()的对象的类是在内存中的,保证了类型安全。
问: 还有其余方法获得Class对象吗?
答: 获取Class对象的方法有两种。可使用类字面常量,它的名字和类型相同,后缀为.class;例如,Account.class。另一种就是调用Class的forName()方法。类字面常量更加简洁,而且编译器强制类型安全;若是找不到指定的类编译就不会经过。经过forname()能够动态地经过指定包名载入任意类型地引用。可是,不能保证类型安全,可能会致使Runtime异常。
问: 实现equals()方法的时候,getClass()和instanceof哪个更好?
答: 使用getClass()仍是instanceof的话题一直都是Java社区争论的热点,Angelika Langer的Secrets of equals – Part 1这片文章能够帮助你作出选择。关于正确覆盖equals()方法(例如保证对称性)的讨论,Lang的这篇文章能够做为一个很好的参考手册。