转行也快一年了,作为一个小菜鸡,磨磨唧唧读了大半年终于快看完,写点自己平时看的笔记吧,就当记录一下了,第一章是引言,就从第二章开始吧,XDM,let's begin!
CHAPTER 2 创建和销毁对象
1. 用静态工厂方法代替构造器
优势
- 静态工厂方法有名字,可以表达出方法的作用
- 静态工厂在调用的时候,不用每次都创建新对象(单例模式)
- 与构造器不同,静态工厂方法可以返回return 类型的任意子类
- 静态工厂方法可以根据不同参数返回不同对象(这里指的不同不是个数不同,而是值不同)
- 静态工厂方法的返回对象在编写工厂类的时候可以不存在 缺点:
- 只要静态工厂方法没有public或protected构造器的类不能被子类化(应该就是被继承的意思)
- 程序员很难找到他们 总结: 静态工厂方法和构造器都有他们的用处,并且有必要了解他们的相对优点,通常来说静态工厂方法更可取,所以在使用构造器的时候要第一时间考虑一下静态工厂方法
2. 当构造参数过多时,可以考虑使用建造者模式
- 建造者模式适合多个可选参数对象的创建
- 建造者模式在继承关系中也适用 建造者与构造器对比
- 优势:
- 建造者模式可以有更多的可变参数,因为每个参数都有自己指定的方法
- 建造者可以将传递到对方法的多个调用中的参数聚合到单个字段中,放在集合中
- 劣势:
- 会耗费额外的资源,不适合在性能要求高的系统中
- 建造者模式也更繁琐,最好在可选参数在四个或四个以上时使用 总结:
- 在参数比较多的情况下,建造者与静态工厂方法和构造器相比会是个更好的选择
- 在参数变多的情况下由静态工厂方法和构造器改成建造者会比较麻烦,所有我们要优先考虑建造者模式
3. 使用私有构造函数或枚举类型来实现单例模式
- 直接通过私有化构造函数,来实现单例。public static final 修饰的实例,私有构造器有可能通过AccessibleObject.setAccessible 方法被调用,可以通过在判断是第二次创建抛出异常来避免这种攻击
- 使用静态工厂实现单例可以清楚的表明这个类是个单例
- 第二个优点就是简单,谁的优点呢,当然是静态工厂了
- 使用静态工厂方法实现,也会出现第一点的问题
- 静态工厂方法可以在不改变调用接口的情况下,灵活的改变返回值(改成非单例等)
- 可以写个公共的单例工厂
- 可以作为一个方法的引用 Supplier<Object>
- 这俩种方法在序列化的时候不仅仅是要实现Serializzable接口,还需要实现readResolve方法来避免单例的重复创建
- 使用枚举类型实现单例,单元素枚举类通常是最好实现单例的方式,不过缺点就是单例只能是继承Enum类型,即使你能通过实现接口的方式拓展
4. 当对象不可实例化时,私有化其构造函数
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
... // Remainder omitted
}
- 这种写法容易让人产生误解,所以最好写上注释
- 这种写法还会带来一个副作用就是不能被继承,因为子类初始化的时候要调用父类的构造函数,而构造函数私有化导致其不能被继承.
5. 使用依赖注入,而不是直接创建
// Dependency injection provides flexibility and testability public class SpellChecker {private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }}
- 不要使用单例或静态工具类来实现依赖于一个或多个基础资源的类,这些资源的行为会影响类的行为
- 因为会被不同的资源所影响,所以也不适合直接创建这些资源,而是使用工厂去创建这些资源,通过依赖注入的方式传递给类(这里使用的是构造器注入)
6. 避免创建不必要的对象
永远不要new String("xxx"),使用String s="xxx"; 自动装箱拆箱模糊了原始数据类型和包装类型的区别,但并没有消除它们,我们要注意包装类的使用,避免频繁创建不必要的包装对象 虽然重用对象有很多优点,但是保护性拷贝(defensive copying)的对象,如果重用所产生的代价远比创建一个不必要的新对象大
7. 消除过时对象的引用
- 消除过时对象的引用应该是例外,而不是常态 内存泄漏的几种常见情况
- 当一个类自己管理自己的内存时,程序员应该警惕内存泄漏
- 缓存导致的内存泄漏也是一种常见的情况
- 第三个常见的来源就是监听器和回调函数
- 内存泄漏造成的影响往往不会马上体现出来,因此我们在平时的编码中要时刻警惕
8.避免使用终结器(finalizers)和清理器(cleaners)
- 终结器是不可预测的,通常是危险的,不必要的
- 清理器虽然没终结器危险,但还是不可预测和不必要的
- 不要在终结器和清理器上 做任何时间紧迫的事情,因为它们不会立即执行,就比如打开文件的操作,因为打开文件描述符是一个受限的资源,如果打开多个文件没有及时关闭可能会导致程序错误。
- 永远不要依赖终结器或清理程序来更新持久状态
- 使用终结器和清理器将会带来严重影响性能
- 终结器会带来严重的安全问题
- 当从构造器抛出异常了,该对象就不该被创建,但当终结器存在时,结果却不是如此
- 为了保护非 final 类不受终结器攻击,可以编写一个不执行任何操作的 final finalize 方法
- 当我们编写的类中确实要释放资源时,只需要实现AutoCloseable接口即可 何时使用呢?
- 作为安全网,以防止资源的拥有者忘记去调用关闭方法,比如FileInputStream,FileOutputStream,ThreadPoolExcutor等
- 清理本地对象,由本地方法创建的不属于java对象,所以无法被垃圾收集器清理。
总结:
不要使用清理器,和java9以前的终结器,除非用于保证安全或者清理一个非临界的本地资源
9.使用try-with-resources而不是try-finally
当资源必须关闭时,使用try-with-resources而不是try-finally。可以使得代码清晰简洁,并且可以保证代码更加有效,而且代码容易编写。