??xml version="1.0" encoding="utf-8" standalone="yes"?>
׃水^有限Q难免有不少错误Q还请大家指正!
首先我们剔除所有的Ҏ(gu)和静态变量,Boolean的核心代码如下:
public final class Boolean implements java.io.Serializable,Comparable
{
private final boolean value;
}
很明显,凡是成员变量都是finalcd的,一定是immutable classQ这个Boolean和String一P一旦构造函数执行完毕,实例的状态就不能再改变了?BR>
Boolean的构造函数有两个Q?/P>
public Boolean(boolean value) {
this.value = value;
}
public Boolean(String s) {
this(toBoolean(s));
}
都很单就不多说了?BR>
另外注意到Booleancd际上只有两种不同状态的实例Q一个包装trueQ一个包装f(xi)alseQBoolean又是immutable classQ所以在内存中相同状态的Boolean实例完全可以׃nQ不必用new创徏很多实例。因此Boolean classq提供两个静态变量:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
q两个变量在Class Loader装蝲时就被实例化Qƈ且申明ؓfinalQ不能再指向其他实例?BR>
提供q两个静态变量是Z让开发者直接用这两个变量而不是每ơ都new一个BooleanQ这h节省内存又避免了创徏一个新实例的时间开销?BR>
因此Q用
Boolean b = Boolean.TRUE;
?BR> Boolean b = new Boolean(true);
要好得多?BR>
如果遇到下面的情况:
Boolean b = new Boolean(var);
一定要Ҏ(gu)一个boolean变量来创建Boolean实例怎么办?
推荐你用Boolean提供的静态工厂方法:
Boolean b = Boolean.valueOf(var);
q样可以避免创建新的实例,不信看看valueOf()静态方法:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
q个静态工厂方法返回的仍然是两个静态变量TRUE和FALSE之一Q而不是new一个Boolean出来。虽然Boolean非常单,占用的内存也很少Q但是一个复杂的cȝnew创徏实例的开销可能非常大,而且Q用工厂方法可以方便的实现~存实例Q这对客L(fng)是透明的。所以,能用工厂Ҏ(gu)׃要用new?BR>
和Boolean只有两种状态不同,Integer也是immutable classQ但是状态上亿种Q不可能用静态实例缓存所有状态。不q,SUN的工E师q是作了一点优化,Integercȝ存了-128?27q?56个状态的IntegerQ如果用Integer.valueOf(int i)Q传入的int范围正好在此内,p回静态实例?BR>
hashCode()Ҏ(gu)很奇怪,两种Boolean的hash code分别?231?237。估计写Boolean.java的h对这两个数字有特别偏好:
public int hashCode() {
return value ? 1231 : 1237;
}
equals()Ҏ(gu)也很单,只有Booleancd的Objectq且value相等才返trueQ?/P>
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
Z提一句:很多人写equals()L在第一行写Q?BR> if (obj==null) return false;
其实完全没有必要Q因为如果obj==nullQ下一行的
if (obj instanceof Type)
p定返回falseQ因?null instanceof AnyType) = false?BR>详细内容请参考《Effective Java》第7条:Obey the general contract when overriding equals?BR>
其他的方法如toString()更单了Q只要稍微熟(zhn)java的程序员怿都能写出来,我就不多说了?BR>
?/FONT> ȝ ?/FONT>
1.如果一个类只有有限的几U状态,考虑用几个final的静态变量来表示不同状态的实例?BR>例如~写一个Weekdayc,状态只?个,׃要让用户写new Weekday(1)Q直接提供Weekday.MONDAY卛_?BR>
2.要防止用户用new生成实例Q就取消public构造函敎ͼ用户要获得静态实例的引用有两个方法:如果xpublic static var可以直接访问,比如Boolean.TRUEQ?BR>W二个方法是通过静态工厂方法:Boolean.valueOf(?)
3.如果不提供public构造函敎ͼ让用户只能通过上面的方法获得静态变量的引用Q还可以大大化equals()Ҏ(gu)Q?BR> public boolean equals(Object obj) {
return this==obj;
}
可以直接?=比较引用Q绝Ҏ(gu)有问题,而且效率最高?BR>
4.Z么JDK的Boolean没有实现上面W?点?因ؓ那两个static变量TRUE和FALSE是在jdk 1.2以后才有的,׃前面的版本已l把构造函数申明ؓpublicQ所以ؓ了保持客L(fng)代码能够不修改也在后面的版本中运行,只好l箋提供public构造函数?/P>
// q程接口l承?/SPAN>Remote
// q程Ҏ(gu)的传入参数和q回值必L自然cdQ?/SPAN>intQ?/SPAN>floatQ?/SPAN>boolean{)
// 或者实CSerializable?/SPAN>Remote接口的对象?/SPAN>
public interface Time extends java.rmi.Remote {
// q程Ҏ(gu)必须抛出RemoteExceptionQ?/SPAN>
public String getTime() throws RemoteException;
}
2. 定义实现c?/SPAN>
// 注意Q实现类l承?/SPAN>UnicastRemoteObject和自定义的远E接?/SPAN>TimeQ?/SPAN>
public class TimeImpl extends java.rmi.server.UnicastRemoteObject implements Time {
// 注意Q由?/SPAN>RemoteObject构造函数要抛出RemoteExceptionQ?/SPAN>
// 因此务必定义构造函数ƈ抛出RemoteExceptionQ?/SPAN>
public TimeImpl() throws RemoteException { super(); }
// q里是远E方法:
public String getTime() throws RemoteException {
return "
}
// 启动服务Q?/SPAN>
public static void main(String[] args) throws Exception {
// 可以手动启动RMI RegistryQ也可以在程序中启动Q?/SPAN>
java.rmi.registry.LocateRegistry.createRegistry(1099);
// l定名字服务Q地址是本地计机名或本机IPQ默认端口是1099Q?/SPAN>
java.rmi.Naming.bind("http://localhost:1099/servicename", new TimeImpl());
// 如果没有异常抛出Q则l定成功?/SPAN>
// 如果名字已经被绑定,可以?/SPAN>Naming.rebind()替换掉已l定的服务?/SPAN>
}
}
3. ~译生成桩和框架
q行rmic TimeImplQ生?/SPAN>TimeImpl_Skel.class?/SPAN>TimeImpl_Stub.class?/SPAN>
4. 客户?/SPAN>
// 客户端文件包含客L(fng)代码Client.classQ远E接?/SPAN>Time.classQ?/SPAN>
// ?/SPAN>rmic生成的支持类TimeImpl_Skel.class?/SPAN>TimeImpl_Stub.classQ?/SPAN>
public static void main(String[] args) throws Exception {
// 客户端通过IP引用服务器端的远E对象,因此可以动态选择服务器?/SPAN>
// 如果不指定端口,默认端口h1099Q?/SPAN>
Time time = (Time)java.rmi.Naming.lookup("http://localhost:1099/servicename");
System.out.println(time.getTime());
}
Collection
?EM>List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
?EM>Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口,一个Collection代表一lObjectQ即Collection的元素(ElementsQ。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自Collection的类QJava SDK提供的类都是l承自Collection的“子接口”如List和Set?BR> 所有实现Collection接口的类都必L供两个标准的构造函敎ͼ无参数的构造函数用于创Z个空的CollectionQ有一个Collection参数的构造函数用于创Z个新的CollectionQ这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection?BR> 如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个P代子Q用该q代子即可逐一讉KCollection中每一个元素。典型的用法如下Q?BR> Iterator it = collection.iterator(); // 获得一个P代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元?BR> }
由Collection接口z的两个接口是List和Set?/P>
List接口
List是有序的CollectionQ用此接口能够_的控制每个元素插入的位置。用戯够用烦引(元素在List中的位置Q类g数组下标Q来讉KList中的元素Q这cM于Java的数l?BR>和下面要提到的Set不同QList允许有相同的元素?BR> 除了hCollection接口必备的iterator()Ҏ(gu)外,Listq提供一个listIterator()Ҏ(gu)Q返回一个ListIterator接口Q和标准的Iterator接口相比QListIterator多了一些add()之类的方法,允许dQ删除,讑֮元素Q还能向前或向后遍历?BR> 实现List接口的常用类有LinkedListQArrayListQVector和Stack?/P>
LinkedListc?/STRONG>
LinkedList实现了List接口Q允许null元素。此外LinkedList提供额外的getQremoveQinsertҎ(gu)在LinkedList的首部或N。这些操作LinkedList可被用作堆栈QstackQ,队列QqueueQ或双向队列QdequeQ?BR> 注意LinkedList没有同步Ҏ(gu)。如果多个线E同时访问一个ListQ则必须自己实现讉K同步。一U解x法是在创建List时构造一个同步的ListQ?BR> List list = Collections.synchronizedList(new LinkedList(...));
ArrayListc?/STRONG>
ArrayList实现了可变大的数组。它允许所有元素,包括null。ArrayList没有同步?BR>sizeQisEmptyQgetQsetҎ(gu)q行旉为常数。但是addҎ(gu)开销为分摊的常数Q添加n个元素需要O(n)的时间。其他的Ҏ(gu)q行旉为线性?BR> 每个ArrayList实例都有一个容量(CapacityQ,即用于存储元素的数组的大。这个容量可随着不断d新元素而自动增加,但是增长法q没有定义。当需要插入大量元素时Q在插入前可以调用ensureCapacityҎ(gu)来增加ArrayList的容量以提高插入效率?BR> 和LinkedList一PArrayList也是非同步的QunsynchronizedQ?/P>
Vectorc?/STRONG>
Vector非常cMArrayListQ但是Vector是同步的。由Vector创徏的IteratorQ虽然和ArrayList创徏的Iterator是同一接口Q但是,因ؓVector是同步的Q当一个Iterator被创且正在被用,另一个线E改变了Vector的状态(例如Q添加或删除了一些元素)Q这时调用Iterator的方法时抛出ConcurrentModificationExceptionQ因此必L莯异常?/P>
Stack c?/FONT>
Stackl承自VectorQ实C个后q先出的堆栈。Stack提供5个额外的Ҏ(gu)使得Vector得以被当作堆栈用。基本的push和popҎ(gu)Q还有peekҎ(gu)得到栈顶的元素,emptyҎ(gu)试堆栈是否为空QsearchҎ(gu)一个元素在堆栈中的位置。Stack刚创建后是空栈?/P>
Set接口
Set是一U不包含重复的元素的CollectionQ即L的两个元素e1和e2都有e1.equals(e2)=falseQSet最多有一个null元素?BR> 很明显,Set的构造函数有一个约束条Ӟ传入的Collection参数不能包含重复的元素?BR> h意:必须心操作可变对象QMutable ObjectQ。如果一个Set中的可变元素改变了自w状态导致Object.equals(Object)=true导致一些问题?/P>
Map接口
h意,Map没有l承Collection接口QMap提供key到value的映。一个Map中不能包含相同的keyQ每个key只能映射一个value。Map接口提供3U集合的视图QMap的内容可以被当作一lkey集合Q一lvalue集合Q或者一lkey-value映射?/P>
Hashtablec?/STRONG>
Hashtablel承Map接口Q实C个key-value映射的哈希表。Q何非I(non-nullQ的对象都可作ؓkey或者value?BR> d数据使用put(key, value)Q取出数据用get(key)Q这两个基本操作的时间开销为常数?BR>Hashtable通过initial capacity和load factor两个参数调整性能。通常~省的load factor 0.75较好地实C旉和空间的均衡。增大load factor可以节省I间但相应的查找旉增大,q会影响像get和putq样的操作?BR>使用Hashtable的简单示例如下,?Q?Q?攑ֈHashtable中,他们的key分别是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one? new Integer(1));
numbers.put(“two? new Integer(2));
numbers.put(“three? new Integer(3));
要取Z个数Q比?Q用相应的keyQ?BR> Integer n = (Integer)numbers.get(“two?;
System.out.println(“two = ?+ n);
׃作ؓkey的对象将通过计算其散列函数来定与之对应的value的位|,因此M作ؓkey的对象都必须实现hashCode和equalsҎ(gu)。hashCode和equalsҎ(gu)l承自根cObjectQ如果你用自定义的类当作key的话Q要相当心Q按照散列函数的定义Q如果两个对象相同,即obj1.equals(obj2)=trueQ则它们的hashCode必须相同Q但如果两个对象不同Q则它们的hashCode不一定不同,如果两个不同对象的hashCode相同Q这U现象称为冲H,冲突会导致操作哈希表的时间开销增大Q所以尽量定义好的hashCode()Ҏ(gu)Q能加快哈希表的操作?BR> 如果相同的对象有不同的hashCodeQ对哈希表的操作会出现意想不到的l果Q期待的getҎ(gu)q回nullQ,要避免这U问题,只需要牢C条:要同时复写equalsҎ(gu)和hashCodeҎ(gu)Q而不要只写其中一个?BR> Hashtable是同步的?/P>
HashMapc?/FONT>
HashMap和HashtablecMQ不同之处在于HashMap是非同步的,q且允许nullQ即null value和null key。,但是HashMap视ؓCollectionӞvalues()Ҏ(gu)可返回CollectionQ,其P代子操作旉开销和HashMap的容量成比例。因此,如果q代操作的性能相当重要的话Q不要将HashMap的初始化定w讑־q高Q或者load factorq低?/P>
WeakHashMapc?/STRONG>
WeakHashMap是一U改q的HashMapQ它对key实行“弱引用”,如果一个key不再被外部所引用Q那么该key可以被GC回收?/P>
ȝ
如果涉及到堆栈,队列{操作,应该考虑用ListQ对于需要快速插入,删除元素Q应该用LinkedListQ如果需要快速随问元素,应该使用ArrayList?BR> 如果E序在单U程环境中,或者访问仅仅在一个线E中q行Q考虑非同步的c,其效率较高,如果多个U程可能同时操作一个类Q应该用同步的cR?BR> 要特别注意对哈希表的操作Q作为key的对象要正确复写equals和hashCodeҎ(gu)?BR> 量q回接口而非实际的类型,如返回List而非ArrayListQ这样如果以后需要将ArrayList换成LinkedListӞ客户端代码不用改变。这是针对抽象~程?/P>
Q参考:Sun JDK1.4.1 API DOCQ?/P>