[置顶] 学习JDK源码:编程习惯和设计模式
编程习惯
1、用工厂方法替代构造函数
-
Boolean.valueOf()
通过一个boolean简单类型,构造Boolean对象引用。
优点:无需每次被调用时都创建一个新对象。同时使得类可以严格控制在哪个时刻有哪些实例存在 >>实例受控的类
public static Boolean valueOf(boolean b){ return b ? Boolean.TRUE : Boolean.FALSE; }
静态工厂方法Boolean.valueOf(String)几乎总是比构造函数Boolean(String)更可取。构造函数每次被调用时都会创建一个新对象,而静态工厂方法则从来不要求这样做,实际上也不会这么做。
-
BigInteger.probablePrime()
构造方法BigInteger(int, int, Random)返回一个可能为素数的BigInteger,而用一个名为BigInteger.probablePrime()的静态工厂方法就更好。(JDK1.4最终增加了这个方法。)
优点:方法名对客户端更友好
public class BigInteger extends Number implements Comparable<BigInteger> { public static BigInteger probablePrime(int bitLength, Random rnd) { if (bitLength < 2) throw new ArithmeticException("bitLength < 2"); // The cutoff of 95 was chosen empirically for best performance return (bitLength < SMALL_PRIME_THRESHOLD ? smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) : largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd)); }
-
EnumSet
JDK1.5引入的java.util.EnumSet类没有public构造函数,只有静态工厂方法。根据底层枚举类型的大小,这些工厂方法可以返回两种实现:
如果拥有64个或更少的元素(大多数枚举类型都是这样),静态工厂方法返回一个RegularEnumSet实例,用单个long来支持;
如果枚举类型拥有65个或更多的元素,静态工厂方法则返回JumboEnumSet实例,用long数组来支持。
优点:静态工厂方法能返回任意子类型的对象。可以根据参数的不同,而返回不同的类型。
/** * Creates an empty enum set with the specified element type. * * @param elementType the class object of the element type for this enum set * @throws NullPointerException if <tt>elementType</tt> is null */ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable { public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<E>(elementType, universe); else return new JumboEnumSet<E>(elementType, universe); } } class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> { } class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> { }
-
Collections.unmodifiableMap(Map)
Java集合框架中有32个集合接口的便利实现,提供不可修改的集合、同步集合等等。几乎所有的实现都通过一个不可实例化类(java.util.Collections)中的静态工厂方法导出,返回对象的类都是非public的。
优点:静态工厂方法能返回任意子类型的对象。可以返回一个对象而无需使相应的类public。用这种方式隐藏实现类能够产生一个非常紧凑的API
public class Collections { public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) { return new UnmodifiableMap<K,V>(m); } private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable { } }
2、私有化构造函数,使类不能被子类化
-
Arrays
这种工具类设计出来并不是为了实例化它。然而,如果不显式地编写构造函数,编译器则会提供一个公共的无参数的默认构造方法。
所以将构造函数私有化:
public class Arrays { // Suppresses default constructor, ensuring non-instantiability. private Arrays() { } }
当然,还可以在这个私有构造器内部加上 throw new AssertionError(),可以确保该方法不会再类内部被意外调用
这种习惯用法的副作用是类不能被子类化了。子类的所有构造函数必须首先隐式或显式地调用父类构造函数,而在这种用法下,子类就没有可访问的父类构造函数可调用了。
3、避免创建不必要的对象
-
Map.keySet()
Map接口的keySet()方法返回Map对象的一个Set视图,包含该Map的所有key。
看起来好像每次调用keySet()都需要创建一个新的Set实例。而实际上,虽然返回的Set通常是可变的,但返回的对象在功能上是等同的:如果其中一个返回对象改变,其他对象也会改变,因为他们的底层都是同一个Map实例。虽然创建多个KeySet视图对象并没有害处,但也没有必要。
public abstract class AbstractMap<K,V> implements Map<K,V> transient volatile Set<K> keySet = null; // volatile!! public Set<K> keySet() { if (keySet == null) { keySet = new AbstractSet<K>() { 。。。 }; } return keySet; } }
4、消除无用的对象引用
-
LinkedHashMap.removeEldestEntry()
缓存实体的生命周期不容易确定,随着时间推移,实体的价值越来越低。在这种情况下,缓存应该不定期地清理无用的实体。可以通过一个后台线程来清理(可能是Timer或ScheduledThreadPoolExecutor),也可以在给缓存添加新实体时进行清理。
LikedHashMap可利用其removeEldestEntry,删除较老的实体:
public class LinkedHashMap...{ void addEntry(int hash, K key, V value, int bucketIndex) { createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } else { if (size >= threshold) resize(2 * table.length); } } protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; } }
——可以继承LinkedHashMap,覆盖其removeEldestEntry方法。
注:如果想要缓存中的对象只要不被引用,就自动清理;则可以用WeakHashMap
5、如果指定了toString返回值的格式,则应该提供一个对应的静态工厂方法或构造函数
-
BigInteger.toString()
/** * Returns the String representation of this BigInteger in the * given radix. If the radix is outside the range from {@link * Character#MIN_RADIX} to {@link Character#MAX_RADIX} inclusive, * it will default to 10 (as is the case for * {@code Integer.toString}). The digit-to-character mapping * provided by {@code Character.forDigit} is used, and a minus * sign is prepended if appropriate. (This representation is * compatible with the {@link #BigInteger(String, int) (String, * int)} constructor.) * * @param radix radix of the String representation. * @return String representation of this BigInteger in the given radix. * @see Integer#toString * @see Character#forDigit * @see #BigInteger(java.lang.String, int) */ public String toString(int radix) { } /** * Returns the decimal String representation of this BigInteger. * The digit-to-character mapping provided by * {@code Character.forDigit} is used, and a minus sign is * prepended if appropriate. (This representation is compatible * with the {@link #BigInteger(String) (String)} constructor, and * allows for String concatenation with Java's + operator.) * * @return decimal String representation of this BigInteger. * @see Character#forDigit * @see #BigInteger(java.lang.String) */ public String toString() { return toString(10); }
对应的构造函数如下。这样程序员能容易地在对象及其字符串表示之间来回转换
/** * Translates the decimal String representation of a BigInteger into a * BigInteger. The String representation consists of an optional minus * sign followed by a sequence of one or more decimal digits. The * character-to-digit mapping is provided by {@code Character.digit}. * The String may not contain any extraneous characters (whitespace, for * example). * * @param val decimal String representation of BigInteger. * @throws NumberFormatException {@code val} is not a valid representation * of a BigInteger. * @see Character#digit */ public BigInteger(String val) { this(val, 10); }
/** * Translates the String representation of a BigInteger in the specified * radix into a BigInteger. The String representation consists of an * optional minus sign followed by a sequence of one or more digits in the * specified radix. The character-to-digit mapping is provided by * {@code Character.digit}. The String may not contain any extraneous * characters (whitespace, for example). * * @param val String representation of BigInteger. * @param radix radix to be used in interpreting {@code val}. * @throws NumberFormatException {@code val} is not a valid representation * of a BigInteger in the specified radix, or {@code radix} is * outside the range from {@link Character#MIN_RADIX} to * {@link Character#MAX_RADIX}, inclusive. * @see Character#digit */ public BigInteger(String val, int radix) {}
迭代器模式
1、Collection.iterator()
未完待续。。