对象相关

本文探讨了Java中对象设计与管理的关键策略,包括使用静态工厂方法替代构造器、实现单例模式的不同方法、避免不必要的类创建、消除过期对象引用、合理使用终结方法与清除方法、正确实现equals与hashcode方法、以及覆盖toString方法的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对象相关

1.用静态工厂方法代替构造器

优势:

  • 有名称。当一个类需要多个带有相同签名的构造器时,使用静态工厂代替,并且仔细选择名称。
  • 不必在每次调用的时候都创建一个新对象。有助于类总能严格控制在某个时刻那些实例应该存在。
  • 可以返回原返回类型的任何子类型的对象。灵活应用:api可以返回对象,同时又不会使对象的类变成公有的。
  • 所返回的对象的类随着每次调用而产生变化,取决于静态工厂方法的参数值。
  • 方法返回的对象所属的类,在编写包含该静态工厂方法的类是可以不存在。例:服务提供者框架:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多实现中解耦出来。

缺点:

  • 类如果不含公有的或者受保护的构造器,就不能被子类化。
  • 很难被程序员发现。因为在api文档中,它们没有像构造器那样在api文档中明确标识出来。

2.遇到多个构造器参数时要考虑使用构造器

重叠构造器模式可行,但当有许多参数的时候,客户端代码会很难编写,并且仍然难以阅读。

在构造过程中JavaBean可能处于不一致的状态。JavaBean模式使得把类做成不可变的可能性不存在。

替代方式:建造者模式的一种形式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象。然后客户端在builder对象上调用类似setter的方法,来设置每个相关的可选参数

public class A{
	private final int a;
	private final int b;
	
	public static class Builder{
		private final int a;
		private int b = 0;
		
		public Builder(int a){
			this.a = a;
		}
		
		public Builder b(int val){
			b=val;
			return this;
		}
		
		public A build(){
			return new A(this);
		}
	}
	
	private A(Builder builder){
		a = builder.a;
		b = builder.b;
	}
}

A a = new A.Builder(12).b(2).build();


builder 模式也适用于类层次结构。

3.用私有构造器/枚举类强化Singleton属性

1.公有静态成员是一个final域

public class Elvis{
	public static final Elvis INSTANCE = new Elvis();
	public Elvis(){...}
	public void leaveTheBuilding(){...}
}

私有构造器近被调用一次来实例化公有的静态final域

但有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射调用私有构造器。如果需要防御,则修改构造器,在创建第二个实例的时候抛出异常。

优势在于api清楚表明这个类是一个单例模式,更简单。

2.公有的成员是个静态工厂方法

public class Elvis{
	public static final Elvis INSTANCE = new Elvis();
	public Elvis(){...}
    public static Elvis getInstance(){return INSTANCE;}
	public void leaveTheBuilding(){...}
}

对于静态方法getInstance的调用都会返回同一个对象引用,所以不会创建其他实例(但仍会被反射调用)

优势

  • 提供灵活性。
  • 可以编写一个泛型Singleton工厂。
  • 可以通过方法引用作为提供者。

3.声明一个包含单个元素的枚举类型

public enum Elvis{
	INSTANCE;
	public void leaveTheBuilding(){...}
}

更加简洁无偿提供序列化机制,绝对防止多次实例化,防止反射攻击。

4.避免创建不必要的类

有些对象创建的成本比其他对象要高得多。

5.消除过期的对象引用

程序中发生内存泄漏的情况:

  • 栈内部维护着对象的过期引用(永远也不会再被接触的引用。)
  • 一个对象引用被无意识的保留写来,则jvm不仅不会处理这个对象,也不会处理被这个对象所引用的所有其他对象。 修复方式:在一个对象引用过期后,清空对应引用。 清空对象引用应该是一种例外,而不是一种规范行为。
  • 缓存,一旦把对象放入缓存中,它就很容易被遗忘。解决方式:由一个后台线程清除缓存中没用的项。
  • 监听器以及其他回调。

只要类是自己管理内存,就应该警惕内存泄漏问题。

6.避免使用终结方法和清除方法

终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的。

清除方法没有终结方法那么危险,但仍然是不可预测、运行缓慢,一般情况下也是不必要的。

两者的缺点:

  • 不能保证会被及时执行。从一个对象变得不可到达开始,到他的终结方法被执行,所花费的时间是任意长的,意味着注重实践的任务不应该由终结方法或清除方法来完成。
  • 不应该依赖终结方法或者清除方法来更新重要的持久状态。
  • 有严重的性能损失
  • 有严重的安全问题:为终结方法攻击打开了类的大门。从构造器抛出的异常,应该足以防止对象继续存在,有了终结方法的存在,这一点就无法做到了。为了防止非final类受到终结方法攻击,要编写一个空的final的finalize方法。

两者的优点:

  • 充当安全网,当资源所有者忘记调用close方法时。

7.try-with-resources优先于try-finally

try-finally根据经验是确保资源会被适时关闭的最佳方法,就算发生异常或者返回时也一样,但如果在添加第二个资源就会一团糟。

而使用try-with-resource时就会解决一切问题。要使用这个必须实现AutoCloseable接口,其中包含了单个返回void的close方法。

8.覆盖equals时遵守通用约定

  • 类的每个实例本质上都是唯一的。
  • 类没必要提供逻辑对等的测试功能。
  • 超类已经覆盖了equals,超类的行为对于这个类也是合适的。
  • 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用

equals方法实现了等价关系

  1. 自反性:对象必须等于其自身。
  2. 对称性:任何两个对象对于它们是否相等的问题必须保持一致。
  3. 传递性:如果一个对象等于第二个对象,第二个对象又等于第三个对象,则第一个对象等于第三个对象。
  4. 一致性:如果两个对象相等,他们就必须保持相等,除了它们中有一个对象(或两个)都被修改了。无论类是否可变,都不要用equals方法依赖于不可靠的资源
  5. 非空性:所有的对象都不能等于null

提高equals方法

  • 使用==操作符检查 参数是否为这个对象的引用
  • 使用instanceof检查参数是否为正确的类型
  • 把参数转换成正确的类型
  • 对于类中每个关键域,检查参数中的域是否与该对象中对应的域向匹配。
  • 在编写完equals方法后,问自己3个问题:它是否对称,传递,一致。

要点:

  • 覆盖equals时要覆盖hashcode
  • 不要让equals过于智能
  • 不要将equals声明中的Object对象换成其他类型。

9.覆盖equals是总要覆盖hashcode

在每个覆盖了equals方法的类中,都必须覆盖hashcode方法。如果不这样做就会违反hashcode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作。

要确保相等的对象必须具有相等的散列码(hash code)。不能从散列码计算中排除掉任何一个对象的关键域来提高性能(散列码应有所有关键域组成)

10.始终要覆盖toString

提供好的toString实现可以让类用起来更加舒适,使用这个类的系统也便于调试。应用中toString方法应该返回对象中包含的所有值得关注的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值