Effective Java阅读笔记-item4、6
Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^
4、避免创建重复对象
我们知道构造函数每次被调用的时候都会创建一个新的对象,在有些情况下这样会很浪费。那我们能不能重复使用一个对象(这个对象应该是immutable的),而不是在每次需要的时候都要创建一个新的对象呢?这就是这个item4所要讲述的事情。
重用immutable对象
在item1时我们说过使用static factory method方法可以控制对象的创建,在这里我们将用到此种方法。
举个Boolean的例子,Boolean只有两个值true or false,而且Boolean是immutable的。Considering the following code:
private static final Boolean TRUE = new Boolean(true);
private static final Boolean FALSE = new Boolean(false);
public static final Boolean valueOf(final boolean b)
{
if( b )
return TRUE;
else
return FALSE;
}
可以看出immutable classes可以使用static factory method来避免产生重复对象。
重用已知不会被修改的可变对象
看看下面的例子:(改编自书中的例子,大部分摘取)
import java.util.*;
public class Person1 {
private final Date birthDate;
Person1(Date birthDate) {
this.birthDate = birthDate;
}
public boolean isEighties(){//判断是否是80年代的人
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1980, Calendar.JANUARY, 1, 0, 0, 0);
Date EIGHTIES_START = gmtCal.getTime();
gmtCal.set(1990, Calendar.JANUARY, 1, 0, 0, 0);
Date EIGHTIES_END = gmtCal.getTime();
return birthDate.compareTo(EIGHTIES_START) >= 0 &&
birthDate.compareTo(EIGHTIES_END) < 0;
}
public static void main(String[] args) {
Person1 p = new Person1(new Date());
long startTime = System.currentTimeMillis();
for (int i=0; i<1000000; i++){
p.isEighties();
}
long endTime = System.currentTimeMillis();
long time = endTime – startTime;
System.out.println(time+" ms.");
}
}
//output:
9250ms
可以看出上述代码每次调用isEighties函数时,Calendar,Date,TimeZone都要被实例化一次,这样的代价适合昂贵的。我们改进一下,将不变的Date对象的实例化的工作放在一个static初始化中,看看效果如何。
import java.util.*;
public class Person {
private final Date birthDate;
Person(Date birthDate) {
this.birthDate = birthDate;
}
private static final Date EIGHTIES_START;
private static final Date EIGHTIES_END;
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1980, Calendar.JANUARY, 1, 0, 0, 0);
EIGHTIES_START = gmtCal.getTime();
gmtCal.set(1990, Calendar.JANUARY, 1, 0, 0, 0);
EIGHTIES_END = gmtCal.getTime();
}
public boolean isEighties(){
return birthDate.compareTo(EIGHTIES_START) >= 0 &&
birthDate.compareTo(EIGHTIES_END) < 0;
}
public static void main(String[] args) {
Person p = new Person(new Date());
long startTime = System.currentTimeMillis();
for (int i=0; i<1000000; i++){
p.isEighties();
}
long endTime = System.currentTimeMillis();
long time = endTime – startTime;
System.out.println(time+" ms.");
}
}
//output:
62ms
哇! 9250 vs 62 ,速度得到飞快提升。
6、避免使用终结函数
在这一节中作者也写了很多,不过我最感兴趣的是“finalizer chaining”终结函数链。
其主要的内容就是“如果一个类(除了Object类)拥有一个finalize函数,并且它的子类override了finalize函数(java中所有类的member func都是virtual的,可以被override的),那么你应该在子类的finalize函数中手动调用父类的finalize函数”。
protected void finalize() throws Throwable {
try {
//finalize subclass state
…
}finally{
super.finalze();
}
}
如果你不手动调用父类的finalize函数,父类的finalize函数将永远不会被调用。那么父类的finalize函数中的关键资源将不能够被释放。但是程序员总是有些“粗心大意”的,我们不能指望程序员都能记住要手动调用父类的finalize,那么我们就要想办法了。书中提到了“finalizer guardian”的概念。书中的论点是:“对于所有带有finalize函数的非final的public class,都应该考虑finalizer guardian技术”这里举例说一下我的理解,以书中的例子为例:
从原来的
public class Foo {
protected void finalize() throws Throwable {
//finalize the class Foo object
…..
}
};
//…..
}
进化为
public class Foo {
private final Object finalizerGuardian = new Object() {
protected void finalize() throws Throwable {
//finalize the outer class Foo object
…..
}
};
//…..
//注意:该Foo类中并无finalize函数,所有关键资源的释放都由inner class override的finalize负责释放。
}
我们首先看看进化后类中的角色:
Foo类:拥有关键资源需要释放且可能作为父类被继承。
Inner class: 继承自Object类,override了Object的finalize方法,并在finalize方法中帮助释放其outer class Foo的关键性资源。
关键性资源释放的流程:由于inner class object和Foo object同生命周期,它们可以同时启动终结过程,inner class在终结过程中就会帮助Foo class释放关键性资源,保证关键性资源不会泄露。这样即使Foo类的子类在finalize中没有手动调用(如果父类使用了finalizer guardian技术,子类就无需手动调用父类的finalize了)父类的finalize,父类的关键资源也会被释放掉。That’s all!^_^
评论