Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
2017-09-20 13:42
483 查看
避免使用最终方法
对于Java来说垃圾回收是自动的,或者称之为不可预知或不可控,尽管finalize方法所代表的也是在垃圾回收前所做的一些动作,但对于GC的时间你不能掌握,也就是说不能保证finalize方法会被及时执行,这是很危险的,一般情况下这个方法不会被用到。
终结方法的两点用途:
第一种用途就是充当一个“安全网”,终结方法“本该”是在GC前做一些清理动作,但GC的时间未知,也就是终结方法执行时间未知,对于FileInputStream类我们都知道应该在try-finally中对其调用close方法,但也许我们会忘记编写此方法,在FileInputStream源代码中就实现了终结方法目的就在于如果忘记了close方法,至少还有终结方法,虽然可能不能得到及时执行,但晚执行总比不执行好吧。
第二个用途可能使用的场景就可能比较少,JVM只回收普通对象,对于本地对象(也就是不是Java实现的对象),JVM并不会对它进行回收,此时我们就可以在终结方法中对本地对象进行一些清理操作,但一定记住一定要是“不拥有关键资源的前提”,且在子类中重写了终结方法一定要现实调用super.finalize(),否则父类的终结方法不会被调用。
覆盖equals时请遵守通用约定
对于equals方法,在编码中最常用的可能就是比较两个字符串是否是值相等的。需要自己重写equals方法的场景可能并不是人人都能有幸碰到,而如果碰到了该怎么办,本条目下书中说明了重写equals方法时需要遵守的一些通用约定,如果不遵守这些约定可能导致无法和其他类配合使用。
equals方法来自于Object类:
public boolean equals(Object obj){
return(this == obj)
}
顶级类只提供了引用是否相等,如果你需要自己实现一个逻辑是否相等,此时则需要重写equals方法,例如String类,但当在重写equals方法时,应该遵守以下约定:
1、自反性:对于任何非null的引用值x,x.equals(x)必须返回true。也就是说一个类的实例一定是与它本身相等的,不管你怎么实现它的逻辑判断,但它的“本”不能忘。
2、对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。这条比较好理解,x=1,y=1,你不能y=x而x!=y吧。
3、传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。显而易见。
4、一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中的所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。这条显然,你不能多调用判断几次它的结果就产生变化了吧。
对于任何非null的引用值x,x.equals(null)必须返回null。
一定要反复检查测试自己重写的equals方法是否遵守以上约定,否则程序可能会变得不正常,因为许多类,包括所有的集合类都依赖于是否遵守了equals约定。书中举了详细的例子来说明上述约定,这里不再叙述。
我们来分析下String类中重写的equals方法:
//String.equals
public boolean equals(Object anObject) {
if (this == anObject) { //是否等于自身
return true;
}
if (anObject instanceof String) { //类型是否相等
String anotherString = (String) anObject; //转换类型
int n = value.length;
if (n == anotherString.value.length) { //先判断长度是否相等
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) { //一个一个字符判断值是否相等
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String中equals方法的实现实际上就是书中给我们的重写equals的一些诀窍:
1、使用==操作检查“对象是否为这个对象的引用”,这不是必须的,只是作为一种性能优化,例如Integer类中并无此项判断。
2、使用instanceof操作符检查“参数是否为正确的类型”。
3、把参数转换成正确的类型。
4、对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域想匹配。
对于Java来说垃圾回收是自动的,或者称之为不可预知或不可控,尽管finalize方法所代表的也是在垃圾回收前所做的一些动作,但对于GC的时间你不能掌握,也就是说不能保证finalize方法会被及时执行,这是很危险的,一般情况下这个方法不会被用到。
终结方法的两点用途:
第一种用途就是充当一个“安全网”,终结方法“本该”是在GC前做一些清理动作,但GC的时间未知,也就是终结方法执行时间未知,对于FileInputStream类我们都知道应该在try-finally中对其调用close方法,但也许我们会忘记编写此方法,在FileInputStream源代码中就实现了终结方法目的就在于如果忘记了close方法,至少还有终结方法,虽然可能不能得到及时执行,但晚执行总比不执行好吧。
第二个用途可能使用的场景就可能比较少,JVM只回收普通对象,对于本地对象(也就是不是Java实现的对象),JVM并不会对它进行回收,此时我们就可以在终结方法中对本地对象进行一些清理操作,但一定记住一定要是“不拥有关键资源的前提”,且在子类中重写了终结方法一定要现实调用super.finalize(),否则父类的终结方法不会被调用。
覆盖equals时请遵守通用约定
对于equals方法,在编码中最常用的可能就是比较两个字符串是否是值相等的。需要自己重写equals方法的场景可能并不是人人都能有幸碰到,而如果碰到了该怎么办,本条目下书中说明了重写equals方法时需要遵守的一些通用约定,如果不遵守这些约定可能导致无法和其他类配合使用。
equals方法来自于Object类:
public boolean equals(Object obj){
return(this == obj)
}
顶级类只提供了引用是否相等,如果你需要自己实现一个逻辑是否相等,此时则需要重写equals方法,例如String类,但当在重写equals方法时,应该遵守以下约定:
1、自反性:对于任何非null的引用值x,x.equals(x)必须返回true。也就是说一个类的实例一定是与它本身相等的,不管你怎么实现它的逻辑判断,但它的“本”不能忘。
2、对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。这条比较好理解,x=1,y=1,你不能y=x而x!=y吧。
3、传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。显而易见。
4、一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中的所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。这条显然,你不能多调用判断几次它的结果就产生变化了吧。
对于任何非null的引用值x,x.equals(null)必须返回null。
一定要反复检查测试自己重写的equals方法是否遵守以上约定,否则程序可能会变得不正常,因为许多类,包括所有的集合类都依赖于是否遵守了equals约定。书中举了详细的例子来说明上述约定,这里不再叙述。
我们来分析下String类中重写的equals方法:
//String.equals
public boolean equals(Object anObject) {
if (this == anObject) { //是否等于自身
return true;
}
if (anObject instanceof String) { //类型是否相等
String anotherString = (String) anObject; //转换类型
int n = value.length;
if (n == anotherString.value.length) { //先判断长度是否相等
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) { //一个一个字符判断值是否相等
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String中equals方法的实现实际上就是书中给我们的重写equals的一些诀窍:
1、使用==操作检查“对象是否为这个对象的引用”,这不是必须的,只是作为一种性能优化,例如Integer类中并无此项判断。
2、使用instanceof操作符检查“参数是否为正确的类型”。
3、把参数转换成正确的类型。
4、对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域想匹配。
相关文章推荐
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- Effective Java - 对于所有对象都通用的方法 - 覆盖 equals 时请遵守通用约定
- Effective Java 第八条:覆盖equals请遵守通用约定
- Effective Java 第8条:覆盖equals时要遵守通用约定
- java对象通用方法之覆盖equals时请遵守通用约定、覆盖equals时总要覆盖hashCode、始终要覆盖toString、考虑实现Comparable接口
- effective java (8) 覆盖equals时请遵守通用约定
- Effective java3——覆盖equals方法的通用约定
- effective java(8) 之覆盖equals时遵守通用的约定
- 【Effective Java】4、覆盖equals时请遵守通用约定
- 对于所有对象都通用的方法 - 覆盖equals时请遵守通用约定