第一条:考虑使用静态工厂方法替代构造方法
静态工厂的优点:
- 有方法名字;
- 不需要每次创建新对象,有利于对象复用;
- 与构造方法不同,它们可以返回 其返回类型的任何子类型的对象;
- 可以根据输入参数的不同返回不同类的对象。如下图,在Services类写完时,Provider的具体实现类还没有。可以向Services注册不同的Provider,提供不同的Service类对象。

缺点:没有公共或受保护构造方法,不能扩展出子类。
第二条、当构造方法参数过多时使用 Builder 模式:
当设计的类,构造方法参数过多,如有4个或以上时,或有可选参数时,可选择用Builder模式。子->父 协变。父->子 逆变。
可选的解决方法:
- 重叠构造方法:参数多的时候,写多个构造函数很麻烦,调用的时候传参容易出错,且可读性差。
- JavaBean通过Setter方式设置属性值:创建对象容易,set属性可读性也好,可惜安全性差。构造过程被分为了几个调用,构造过程中JavaBean可能处于不一致的状态。必要参数得不到保障。
- Builder模式的优点:既保证了重叠构造的安全性,也保证了像JavaBean一样良好的可读性。缺点是,一定程度上增加了代码量。
第三条、使用私有构造器或枚举强化Singleton
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
这种方式类似于公共属性方法,但更简洁,无偿地提供了序列化机制,并提供了防止多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是 单一元素
枚举类通常是实现单例的最佳方式。注意:如果单例要继承 Enum 以外的父类,就不能使用这种方法。
第四条、通过构造器强化不可实例化的能力
class UtilClass {
private UtilClass() {
// 抛异常是为了,阻止内部方法初始化对象
throw new AssertionError();
}
static method1 method2...
}
第五条、避免创建不必要的对象
在大量循环的代码块中,谨慎创建对象。提前做好设计,降低对象创建数量,在内存敏感的场景下,有助于降低内存使用。
一般而言,优先使用基本类型,而不是装箱类型。当心无意识的自动装箱,如:Long a = 0L; long i = 1; Long b = a+i;
谨慎使用对象池设计,客观评估缓存对象是否高开销。
- 如果要缓存的对象是小对象,或是没有缓存价值,就不要用对象池。现代的Jvm拥有高效的回收机制,很容易超过对象池的表现。
- 当要缓存的对象创建开销很大时,或是代表珍贵资源,有存在的个数限制时,做对象池设计进行缓存是比较合适的,典型例子是数据库连接。
第六条、消除过期的对象引用
以数组为基础实现的Stack,在出栈时只做两个操作时:返回出栈对象,数组长度减一。此时数组最后一个元素是过期引用的状态,如果程序员不手动清除最后一个元素的对象引用,就会导致内存泄露。
对于Jvm来说,仅仅记录了数组的活动区是不够的,Jvm不知道哪些元素的引用是活动的,哪些是已经过期的,必须由程序员手动设置null告诉Jvm这个引用是过期的,这样在这个引用的对象执行完毕后,Jvm就可以清理它了。
一般而言,只要类是自己管理内存,就要小心内存泄露的问题。一旦元素被释放掉,所有元素代表的对象引用都应该被清除。
由于引用未清除导致内存泄露大概有以下几种情况:
- 自己管理内存,数组活动域变小时
- 对象引用放到缓存中,容易遗忘
- 注册监听器和回调,没有显式注销
第七条、避免使用 finalize 方法
finalize的缺点是不能保证会被及时执行,甚至不能保证会被执行。从一个对象变为unreachable状态开始,到最后执行finalize方法,这期间的时间是任意长的。 永远不应该用 Finalizer 和 Cleaner 机制做任何时间敏感(time-critical)的事情。 例如,依赖于 Finalizer 和 Cleaner 机制来关闭文件是严重的错误,因为打开的文件描述符是有限的资源。 如果由于系统迟迟没有运行 Finalizer 和 Cleaner 机制而导致许多文件被打开,程序可能会失败,因为它不能再打开文件了。
System.gc Runtime.runFinalization 两个方法只能增加执行机会,也不保证及时执行。
添加了finalize 方法的对象,创建和销毁的性能大大降低。
如果对象中封装的资源确实需要关闭,就要提供一个显式的关闭方法,并要求客户端在每个实例不再使用的时候,调用这个方法。例子:InputStream、OutputStream、Connection的close() 和 Time.cancel()
上面是 finalize 的缺点 与 解决方法。下面讲一下finalize 方法的使用场景:
- 在客户端没有调用关闭方法时,提供了一个保险的执行的机会。是一个次级的、补救的方式,通过jvm回收时触发,晚回收总比不回收强。
- 对象存在与之对应的 native 对象,其占有非关键的本地资源,需要释放时,可以在finalize方法中处理。其实也属于第一种补救的一个特殊情况。
本文探讨了在编程中如何优化对象创建和管理,包括使用静态工厂方法代替构造器、应对构造方法参数过多时采用Builder模式、实现Singleton的最佳方式、私有构造器强化不可实例化、避免不必要的对象创建、消除过期对象引用以及慎用finalize方法。这些最佳实践旨在提高代码效率,减少内存消耗和潜在的内存泄露问题。

被折叠的 条评论
为什么被折叠?



