第二天和第三天(第4-6条)
?
把第二天和第三天的阅读内容补上《Effective java》中的第4-6条
?
第4条:避免创建重复的对象
反面的例子:
?
String s = new String("anson");?传递给String的实参“anson”本身就是一个String的实例,如果这个语句出现在循环语句中或者是会被频繁的调用,那么会创建出很多不必要的对象。
一个改进的版本:
?
String s = "anson";
?这个版本只使用一个S t r i n g实例,而不是每次被执行的时候创建一个新的实例。而且,它可以保证,对于所有在同一个虚拟机中运行的代码,只要它们包含相同的字符串字面常量,则该对象就会被重用[JLS, 3.10.5]。
?
对于同时提供静态工厂方法和构造函数的非可变类,通常是利用静态工厂方法而不是构造函数,以避免创建重复的对象。例如Boolean.valueOf(String),一般要优先于Boolean(String).
?
不要错误地认为本条目所介绍的内容暗示着“创建对象的代价是非常昂贵的,我们应该要尽可能地避免创建对象”。相反,由于小对象的构造函数只做很少量的工作,所以,小对象的创建和回收动作是非常廉价的,特别是在现代的J V M实现上更是如此。通过创建附加的对象,以使得一个程序更加清晰、简洁、功能强大,这往往也是一件好事。
?
通常自己维护对象池不是一种好选择,除非池中的对象是非常重量级的,例如数据库连接池,IO流等。现代的jvm有高度优化的垃圾回收器,性能很容易超过轻量级对象池的性能。
?
第5条:消除过期的对象引用
一个简单的栈实现例子:
?
public class stack { private Object[] elements; private int size = 0; public Stack(int initialCapacity) { this.elements = new Object[initialCapacity]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if (elements.length == size) { Object[] oldElements = elements; elements = new Object[2 * elements.length +1]; System.arraycopy(oldElements,0,elements,0,size); } }}这段程序严格来讲存在”内存泄露“:如果一个栈先是增长,然后再收缩,那么从栈弹出来的对象将不会被当做垃圾回收,即使使用栈的客户程序不再引用这些对象,它们也不会被回收。因为,栈内部维护者对这些对象的国旗引用。所谓国旗引用,是指永远不会再被解除的引用。
想修复这类问题也很简单:一旦对象引用已经过期,只需清空这些引用即可。在上诉的例子中,只要一个单元被弹出栈,指向它的引用就过期了。pop方法的修订版本如下:
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result;}?第6条:避免使用终结函数
终结函数(finalizer)通常是不可预测的,常常也是很危险的,一般情况下是不必要的。
终结函数并不能保证会被及时地执行[JLS,12.6]。从一个对象变得不可到达开始,到它的终结函数被执行,这段时间可以使任意的、不确定的。当我们在终结函数中释放关键资源例如文件操作,可能会造成很严重的后果。
JLS不仅不保证终结函数会被及时地执行,而且根本就不保证它们会被执行。
不要被System.gc和System.runFinalization这两个方法迷惑,它们确实能增加终结函数被执行的机会,但是它们并不保证终结函数一定会被执行。
如果一个类封装的资源确实需要回收,我们需要提供一个显示的终止方法,并要求类的客户在每个实例不再有用的时候调用这个方法。典型的例子是InputStream和OutputStram的close方法。