【Java语言特性】编程规范每日一学--20130911

语言特性

序列化

序列化对象中的HashMapHashSetHashTable等集合不能包含对象自身的引用
说明:如果一个被序列化的对象中,包含有HashMap、HashSet或HashTable集合,则这些集合中不允许保存当前被序列化对象的直接或间接引用。
因为,这些集合类型在反序列化的时候,会调用到当前序列化对象的hashCode方法,而此时(序列化对象还未完全加载)计算出的hashCode有可能不正确,从而导致对象放置位置错误,破坏反序列化的实例。
示例: 

class Super implements Serializable 
{ 
        final Set<Super> set = new HashSet<Super>(); 
} 

final class Sub extends Super 
{ 
        private int id; 

        public Sub(int id) 
       { 
                this.id = id; 
                set.add(this); // 集合中引用了当前对象 
        } 

        public void checkInvariant() 
        { 
                if (!set.contains(this)) 
              { 
                        throw new AssertionError("invariant violated"); 
              } 
        } 

        public int hashCode() 
       { 
                return id; 
        } 

        public boolean equals(Object o) 
       { 
                return (o instanceof Sub) && (id == ((Sub) o).id); 
        } 
} 

  


这个例子中,将当前对象(Sub对象)放入了对象中的HashSet中,在反序列化set时,因为id属性还未完成初始化,导致hashCode的结果为0,从而导致Sub对象在set中的位置放置错误,对象被破坏。

实现Serializable接口的可序列化类应该显式声明 serialVersionUID
说明: 如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。
不过,强烈建议所有可序列化类都显式声明 serialVersionUID 值,原因计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException
。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修改器显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于立即声明类 –
serialVersionUID 字段作为继承成员没有用处。
示例:
public class BeanType implements Serializable

{    

private static final long serialVersionUID = -2589766491699675794L;   

 

}

泛型

在集合中使用泛型(v1.5+

说明:Java 1.5版本中增加了泛型,在没有泛型之前,从集合中读取到的每一个对象都必须进行转换。如果有人不小心插入类型错误的对象,在运行时的转换处理就会出错。
有了泛型之后,可以告诉编译器每个集合中接受哪些对象类型。编译器自动地为你的插入进行转化,并在编译时告知是否插入了类型错误的对象。
示例:
不好:错误的将Coin对象插入到samps集合中,直到从集合中获取coin时才收到错误提示
private final Collection stamps = ...;
stamps.add(new Coin(...));


推荐:使用泛型,在编译时会提示类型错误
private final Collection<Stamp> stamps = ...;
stamps.add(new Coin(...));


还有个好处是,从集合中获取元素时,不再需要进行手工类型转换。如下所示:
for(Stamp s : stamps)

{        

...

}

 

类的设计可优先考虑泛型(v1.5+
说明:使用泛型类型,比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。
示例:如下所示,一个设计为泛型的stack类,在使用时,无需对栈中元素进行类型转换。

public static void main(String[] args) 
{ 
                Stack<String> stack = new Stack<String>(); 
                for (String arg : args) 
              { 
                        stack.push(arg); 
              } 

                while(!stack.isEmpty()) 
              { 
                        System.out.println(stack.pop().toUpperCase()); 
              } 
  } 

  方法的设计可优先考虑泛型(v1.5+
说明:就如类可以从泛型中受益一般,方法也一样。静态工具方法尤其适合于泛型化。
示例:如下所示,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易。

public static <E> Set<E> union(Set<E> s1, Set<E> s2) 
{ 
        Set<E> result = new HashSet<E>(s1); 
        result.addAll(s2); 
        return result; 
} 
        
public static void main(String[] args) 
{ 
        Set<String> guys = new HashSet<String>(Arrays.asList("Tom", "Dick", "Harry")); 
        Set<String> stooges = new HashSet<String>(Arrays.asList("Larry", "Moe", "Curly")); 
        Set<String> aflCio = union(guys, stooges); 
        System.out.println(aflCio); 
} 

  优先使用泛型集合,而不是数组(v1.5+

说明:数组与泛型集合相比,有两个重要不同点。首先,数组是协变的(covariant),即SubSuper的子类型,则Sub[]也是Super[]的子类型。

相反,泛型则是不可变的(invariant),对于任意两个类型Type1Type2List<Type1>既不是List<Type2>的子类型,也不是其超类型。其次,数组是具体化的,因此数组在运行时才知道并检查它们的元素类型约束。

示例:如下代码是合法的,但执行时会报错


Object[] objectArray = new
Long[1];

objectArray[0] =
"I don't fit in"; //Throws ArrayStoreException


而如下代码则会在编译时报错


List<Object> objectList = new ArrayList<Long>(); //Incompatible types

objectList.add("I don't fit
in"
);

 

 

 

 

posted @ 2013-09-11 10:26  quietStrength  阅读(914)  评论(0编辑  收藏  举报