Effective Java(三)

一、泛型

1、请不要在新代码中使用原生态类型

(1)、JavaSE5增加泛型后,应该避免使用原生类型,如List。允许原生编译通过是为了兼容老版本。

(2)、泛型增强了编译器检查异常的能力。

(3)、使用原生态类型,会丢失泛型在安全性和表述性方面的所有优势。

(4)、泛型信息可以在运行时被擦除。

(5)、总结

原生态类型                              List

泛型                                         List<E>

无限制通配符类型                   List<?>

(显示的)参数化的类型        List<String>

有限制类型参数                      <E extends Number>

递归类型限制                          <T extends Comparable<T>>

有限制通配符类型                   List<? extends Number>

泛型方法                                 static <E> List<E> asList(E[ ] a)

类型令牌                                 String.class

 

2、消除非受检警告

(1)、如果无法消除警告,同时可以证明引起警告的代码是类型安全的,此时可以用@SuppressWarnings("unchecked")注解来禁止这条警告。

 

3、列表优先于数组

(1)、泛型是不可变的(invariant),例如:List<Type1>既不是List<Type2>的子类型,也不是List<Type2>的超类。

(2)、数组是具体化的,数组在运行时才知道并检查它们的元素类型约束。泛型在编译时检查类型信息,并在运行时擦除元素的类型信息。擦除的目的是使泛型可以与没有使用泛型的代码随意进行互用。

(3)、数组和泛型不要混合使用。

 

4、优先考虑泛型

(1)、在新建类型时,为了确保它们不需要各种装换,把类做成泛型。

 

5、优先考虑泛型方法

(1)、静态工具类尤其适合泛型化。

 

6、利用有限制通配符来提升API的灵活性

(1)、在表示生产者或者消费者的输入参数上使用通配符类型,可以获取最大限度的灵活性。

(2)、PECS表示producer-extends,consumer-super。如果参数化类型表示一个T生产者,使用<? extends T>;表示一个T消费者,使用<? super T>。

(3)、不要用通配符类型作为返回类型。

 

7、优先考虑类型安全的异构容器

(1)、用key自身的class 类型作为key。因为Class 是参数化的类型,它可以确保我们使Context方法是类型安全的,而无需诉诸于一个未经检查的强制转换为T。这种形式的一个Class 对象称之为类型令牌(type token)。

public class Context {
  private final Map<Class<?>, Object> values = new HashMap<>();
 
  public <T> void put( Class<T> key, T value ) {
    values.put( key, value );
  }
 
  public <T> T get( Class<T> key ) {
    return key.cast( values.get( key ) );
  }
}

 

二、枚举和注解

1、用enum代替int常量

(1)、int枚举是编译时常量,被编译到使用它们的客户端中,如果枚举常量关联的int发生变化,客户端必须重新编译。因此不安全。

(2)、枚举类型易读、安全,在装载和初始化枚举时会有空间和时间的成本。

(3)、多个枚举常量同时共享相同的行为,考虑使用策略枚举。

 

2、用标记接口定义类型

(1)、标记接口(marker interface):无方法声明的接口。

(2)、标记接口定义的类型是由被标记类的实例实现的。

(3)、如果想要定义类型,一定要使用接口。

 

三、序列序

序列化(serializing):将一个对象编码成一个字节流。反之为反序列化(deserializing)。

序列化技术为远程通信提供了标准的线路级(wire-level)对象表示法,也为JavaBeans组件结构提供了标准的持久化数据格式。

1、谨慎地实现Serializable接口

(1)、一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性。

(2)、代表活动实体的类,如线程池(thread pool),一般不应该实现Serializable。

(3)、为了继承而设计的类应该尽可能少地去实现Serializable接口,用户的接口也应该尽可能少地继承Serializable接口。

例外有,Throwable类实现了Serializable接口,所以RMI的异常可以从服务器端传到客户端。Component类实现了Serializable接口,因此GUI可以被发送、保存和恢复。HttpServlet类实现了Serializable接口,因此会话状态(session state)可以被缓存。

(4)、http请求时,controller层接收对象需要提供无参构造函数,方可序列化。

(5)、内部类的默认序列化形式是定义不清楚的,不应该实现Serializable。

 

2、考虑使用自定义的序列化形式

(1)、如果一个对象的物理表示法等同于它的逻辑内容,可能就适合于使用默认的序列化形式。例如:表示人名的类。

(2)、默认序列化形式缺点

  • 它使这个类的导出API永远地束缚在该类的内部表示法上。
  • 它会消耗过多的空间和时间。
  • 它会引起栈溢出。

(3)、如果在读取整个对象状态的任何其他方法上强制任何同步,则也必须在对象序列化上强制这种同步。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值