避免创建不必要的对象--EffectiveJava小结(5)
尽量重用对象,而不是重新创建。
一.重用不可变对象
不可变对象,始终可以被重用。
如String
//尽量用,多次调用不会重复创建对象
String s = "";
//而不是,调多少次就创建多少次对象
String s2 = new String("");
另外调用静态工厂方法优于调用构造器。
如Boolean.valueOf(String); 优于new Boolean(String)
因为前者会返回Boolean中缓存的true和false对象,避免重复创建。
二.重用适配器/视图对象
适配器/视图对象是指这样一个对象:它把功能委托给一个后备对象,从而为后备对象提供一个可以替代的接口。
由于其除了后备对象外没有其他状态信息,所以针对某个给定对象的特定适配器而言,它不需要创建多个适配器实例。
这句话咋一看难以理解,没有代入感。下面以HashMap的keySet为例进行理解
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
这个KeySet类就是一个适配器/视图。
根据视图这个名字我们就可以初步理解为KeySet这个类本身并没有存储数据,其数据来源都是其后备对象即HashMap。
可以看到KeySet只有方法没有自己的属性,并且方法中使用的都是HashMap中的属性。程序调用KeySet的方法实际是访问了这个HashMap的方法或属性。
这样看来如果为同一个HashMap创建多个KeySet对象并没有意义。因为修改了任何一个KeySet其实都是修改的这个HashMap。完全没有必要创建用处完全相同的多个KeySet。
至此把上面那句话用HashMap,KeySet代入来说的话就是
HashMap中的KeySet对象把KeySet的功能委托给这个HashMap对象来完成,从而为HashMap提供一个可以替代的接口。由于这个KeySet除了HashMap这个后备对象外,没有其他状态信息,所以针对一个给定的HashMap的KeySet而言,不需要创建多个KeySet实例。
三.注意自动装箱导致的创建多余对象
四.不需要过于追求性能而尽可能不创建对象。
实际上小对象的创建和回收代价很小,如果创建新的对象可以增加程序的清晰性,简洁性和功能性,那通常是件好事。
如为了保护原有数据而拷贝对象导致的重复创建。这时候重用对象可能会付出更大的代价。