一、创建对象

本文探讨了在编程中如何优化对象创建和管理,包括使用静态工厂方法代替构造器、应对构造方法参数过多时采用Builder模式、实现Singleton的最佳方式、私有构造器强化不可实例化、避免不必要的对象创建、消除过期对象引用以及慎用finalize方法。这些最佳实践旨在提高代码效率,减少内存消耗和潜在的内存泄露问题。
摘要由CSDN通过智能技术生成

第一条:考虑使用静态工厂方法替代构造方法

静态工厂的优点:

  1. 有方法名字;
  2. 不需要每次创建新对象,有利于对象复用;
  3. 与构造方法不同,它们可以返回 其返回类型的任何子类型的对象;
  4. 可以根据输入参数的不同返回不同类的对象。如下图,在Services类写完时,Provider的具体实现类还没有。可以向Services注册不同的Provider,提供不同的Service类对象。

缺点:没有公共或受保护构造方法,不能扩展出子类。

第二条、当构造方法参数过多时使用 Builder 模式:

当设计的类,构造方法参数过多,如有4个或以上时,或有可选参数时,可选择用Builder模式。子->父 协变。父->子 逆变。

可选的解决方法:

  1. 重叠构造方法:参数多的时候,写多个构造函数很麻烦,调用的时候传参容易出错,且可读性差。
  2. JavaBean通过Setter方式设置属性值:创建对象容易,set属性可读性也好,可惜安全性差。构造过程被分为了几个调用,构造过程中JavaBean可能处于不一致的状态。必要参数得不到保障。
  3. 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 方法的使用场景:

  1. 在客户端没有调用关闭方法时,提供了一个保险的执行的机会。是一个次级的、补救的方式,通过jvm回收时触发,晚回收总比不回收强。
  2. 对象存在与之对应的 native 对象,其占有非关键的本地资源,需要释放时,可以在finalize方法中处理。其实也属于第一种补救的一个特殊情况。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洛克Lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值