EffectiveJava读书笔记
less, but is more.
- 创建和销毁对象
- 避免创建不必要对象
- 消除过期的对象引用
- 使可变性最小
- 泛型
- 用标记接口定义类型
- 检查参数有效性
- 返回零长度的数组或集合,而不是null
- 需要精确答案时,避免使用float和double
- 接口优先于反射机制
- 可恢复情况使用受检异常,编程错误使用运行时异常
- 慎用延迟初始化及双重检查
创建和销毁对象
- 静态工厂方法代替构造器 - 优点 - 有名称 - 重用对象,考虑Flyweight模式 - 可返回子类型,多态 - 参数化类型实例,代码更简洁。右侧类型推导 Java7支持,Java优化 - 缺点 - 如果不含public或protected构造器,不能子类化 - 是个静态方法,doc中不会明确标识其起到了构造器作用避免创建不必要对象
- 不可变对象,尽量重用而不是创建 - 优先使用基本类型,而不是装箱类型 - 衡量重用与new,小对象创建和回收毕竟是廉价的消除过期的对象引用
- 过期对象,null值清空,如数组元素 - 缓存、监听器/回调时,尽量使用弱引用,如WeakHashMap,LinkedHashMap的removeEldestEntry使可变性最小
不可变类,实例不可修改,创建时初始化,lifetime内固定不变,如String类,原则: - 不提供修改对象状态的方法 - 类不可扩展,final - 所有的域final - 所有的域私有 - 任何可变组件均互斥访问泛型
- 不要在新代码中使用原生态类型 - 原生态类型,失掉了泛型在安全性和表述性方面的所有优势。之所以Java支持原生态类型,也是保持对已有Java代码的兼容性(移植兼容性) - 可以使用@SuppressWarnings("unchecked")消除非受检警告 - 泛型优先使用List而不是数组,数组是协变的 - 优先考虑泛型方法 - 利用有限制通配符提升API灵活性 - PECS(producer-extends,consumer-super)读 ?extends,写 ?super - 所有的comparable和comparator都是消费者用标记接口定义类型
- 标记接口(marker interface)无方法接口声明 - 标记接口,只应用给类和接口,可以用接口作为相关方法的参数类型,优点: - 标记接口可以在编译时捕捉异常 - 更加精确的锁定 - 标记注解,单标记应用于任何程序元素时,考虑用此,优点: - 可以添加一个或多个注解类型元素 - 支持注解作为编程元素之一的框架中具有一致性检查参数有效性
编写方法或构造器时,应该考虑下参数有哪些限制,显式的检查来实施这些限制。慎用重载
- 重载方法(overloaded method),重写/覆盖方法(overridden method) - 参数数目相同时,尽量避免重载方法,尤其是参数类型有着关系(如继承)返回零长度的数组或集合,而不是null
需要精确答案时,避免使用float和double
数值超过18位数字时,可以考虑BigDecimal字符串连接的性能
- 当两个字符串通过+连接在一起时,他们的内容都要被拷贝,使用StringBuilder代替String,通过append方法进行操作。 - StringBuilder预先定义足够容量时,要比默认大小的StringBuilder快。【深有体会..】接口优先于反射机制
- 反射缺点: - 失去编译时的类型检查 - 反射访问的代码笨拙而冗长 - 性能损失(2~50倍,不确定) - System.exit会终止整个VM....可恢复情况使用受检异常,编程错误使用运行时异常
三种可抛出(throwable)结构: - 受检的异常(checked exception) - 运行时异常(runtime exception) - 错误(error)如果期望调用者能够适当的恢复,使用受检的异常。通过抛出每个受检异常,强迫调用者处理该异常,或者继续传播。
运行时异常和错误,不需要也不应该被捕获,毕竟往往是不可回复的情形。
慎用延迟初始化及双重检查
- 实例域,使用双重检查(double-check)需要配合volatile - 静态域,使用lazy initialization holder class,(静态内部类=..=) - 可重复初始化实例域,使用单重检查模式
I am a slow walker, but I never walk backwards.