本节主要涉及Object中通用的一些方法,比如equals
,hashCode
,toString
,clone
,finalize
等等
本节主要涉及Object中通用的一些方法,比如
equals
,hashCode
,toString
,clone
,finalize
等等
覆盖equals时请遵守通用约定
equals方法实现的等价关系:
- 自反性:对于所有的非null,a=a
- 对称性:a=b,则b=a
- 传递性:a=b,b=c,则a=c
- 一致性:对于没有被修改的ab,如果a=b,则一直a=b
- 非空性:对于任何非Null,a!=null
高质量equals方法的诀窍:
- 使用==操作符检查——“参数是否为这个对象的引用”
- 使用instanceof检查——“参数是否为正确的类型”
- 参数转换成正确的类型
- 对于每个关键域,检查参数中的域是否与对象的域相匹配
- 写完后,问自己:是否对称、是否传递、是否一致
忠告:
- 覆盖equlas时,总是覆盖hashCode
- 不要企图让equals过于智能
- 不要将equals中的Object对象换成其他的类型
1 | @Override public boolean equals(Object o){ |
覆盖equals时总是要覆盖hashCode
hashCode主要用于放在HashMap,HashSet,HashTable中时,计算hash值。
1 | @Override public int hashCode(){ |
其中17是随便写的,31是有说道的,因为31是个奇素数,并且:
1 | 31*i==(i<<5)-i |
这样JVM在优化的时候效率会更高。
始终覆盖toString()
因为toString方法应该返回对象的关键信息
谨慎的覆盖clone
应该满足:
1 | x.clone() != x |
另外clone方法相当于另一种构建器,不应该对原来的对象产生影响。
考虑实现Comparable接口
常用的List等等可以通过实现Comparable接口进行排序:
1 | public interface Comparable<T{ |
当对象小于,等于或者大于时,应该返回一个负整数,零或者正整数。
考虑用静态工厂方法代替构造器
使用静态工厂的优势:
- 有名称
- 不必每次调用的时候都创建一个新的对象
- 返回原返回类型的任何子类型对象
- 在创建参数化类型实例时,代码更加简洁。
使用静态工厂的缺点:
- 类如果不包含公有的或者受保护的构造器,就不能被子类化
- 与其他的静态方法实际上没有任何区别
举个例子:
1 | public class Apple{ |
常用的静态工厂名称:valueOf
,of
,getInstance
,newInstance
,getType
,newType
.
遇到多个构造器参数时考虑使用构建器
如果参数很多,在使用的时候也会很繁琐。比如
1 | new Person(name,age,address,tel,gender); |
当参数大于三个的时候,就可以考虑使用构建器了,特别实在参数可选的时候。
1 | public class Person{ |
使用构建器的缺点就是代码会比较冗长。
用私有构造器或者枚举类型强化Singleton属性
第一种方式,是饿汉模式:
1 | publci class Elvis{ |
另一种方式,属于懒汉模式:
1 | public class Elvis{ |
通过私有构造器强化不可实例化的能力
有一些比如工具类,是不想被实例化的,可以使用私有的构造方法,来避免实例化:
1 | public class XXUtil{ |
缺点是这样的类无法被子类化。
避免创建不必要的对象
尽量应该避免创建不必要的对象,比如:
1 | String s = new String("xxx"); |
其实创建了两个字符串对象。
尽量优先使用long这种基本类型。
消除过期的对象引用
常见的是在栈操作,出栈操作并不意味着释放对象。
1 | public Object pop(){ |
避免使用终结方法
finalizer方法是再垃圾回收的时候触发的,因此不能准确的知道终结方法调用的时间。因此最好是在try catch finally的时候释放对象。
这种释放对象的时机,叫做显示终止方法。