本章要点
1.何时以及如何创建对象;
2.何时以及如何避免创建对象;
3.如何保证对象能够适时地销毁;
4.如何管理对象销毁之前必须进行的各种清理动作。
第1条:用静态工厂方法代替构造器
把常用的构造逻辑封装成对应名字的方法,然后通过这些方法来实例化而非构造函数。
构造函数: Customer customer = new Customer(name, age, sex);
静态工厂方法: Customer customer = Customer.getCommonCustomer(name, age, sex);
第2条:遇到多个构造器参数时要考虑使用构建器
使用Builder模式实例化对象,尤其是多构造参数时(@Builder注解即可实现功能)。
普通构造: Customer customer = new Customer(name, age, sex);
Builder模式: Customer customer = Customer.builder().name(name).age(age).sex(sex).build();
第3条:用私有构造器或者枚举类型强化Singleton属性
构造函数设为私有且返回自身,或者直接将该class转换为enum来实现功能,以强调当前类是个“单例”。
私有构造函数: private Customer() {return INSTANCE;}
枚举类: public enum Customer {INSTANCE;}
第4条:通过私有构造器强化不可实例化的能力
所有构造函数设为私有且返回为空甚至抛出异常,强调当前类“不可被实例”(通常针对各种Utils类)。
private Customer(){
throw new AssertionError();
}
第5条:优先使用依赖注入来引用资源
创建实例A时将该实例的具体依赖B和C注入,而不是直接写死,不过书中还是主推Spring依赖注入框架来引入各种需要重复使用的资源。
private final Name name;
private final Age age;
public Customer(Name name, Age age){
this.name = name;
this.age = age;
}
第6条:避免创建不必要的对象
1.通用的对象不要重复创建,最典型的为常量字符串的String;
2.优先使用基本类型而非装箱基本类型,当心无意识的自动装箱。
错误示范1 new常量String: String customerName = new String("Jack");
错误示范2 装箱类型使用不当: Long num = 1000000; for(...) {num += 2}
第7条:消除过期的对象引用
一个容器比如栈,对象先弹出再收缩,被弹出的对象不会被垃圾回收,因为栈内部维护着对这些已弹出对象的“过期引用”,需要对容器中的这些过期引用进行消除。
public Object pop(){
Object result = elements[--size];
elements[size] = null; //清除对应数据
return result;
}
第8条:避免使用终结方法和清除方法
终结方法finalizer和Java9的清除方法cleaner的缺点如下,所以如果需要两方法的功能效果,最好是让调用资源的类实现AutoCloseable方法后用try-with-resources来实现。
1.在于“不能保证会被及时执行”,所以最好“永远不应该依赖终结方法或者清除方法来更新重要的持久状态”;
2.存在非常严重的性能损失,因为两者阻止了有效的垃圾回收;
3.为终结方法攻击打开了类型的大门。
第9条:try-with-resource优先于try-finally
try-finally中try和finally语句块均会抛出异常,而finally中的异常会完全抹除try中的异常,故现在均要求使用资源的类必须实现AutoCloseable接口,然后通过try-with-resource来实现相关功能。
//try-finally多资源嵌套时代码很乱,并且存在异常覆盖问题(out.close的异常会完全抹除out.wrire的异常):
InputStream in = new FileInputStream(src);
try{
OutputStream out = new FileOutputStream(dst);
try{
out.wirte(...)
} finally {
out.close();
}
} finally {
in.close();
}
//try-with-resource通用情况(FileInputStream与FileOutputStream均实现了AutoCloseable接口):
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);){
out.wirte(...);
}