《Effective Java》第2章 对所有对象都通用的方法
第10条:覆盖equals时,请遵守通用约定
1、使用==来比较两个对象的时候,比较的是两个对象在内存中的地址是否相同(两个引用指向的是否为同一个对象);Object中定义的equals方法也是这样比较的;
2、当我们自定义类的时候,如果不覆盖equals方法,那么就会使用默认的equals方法(Object中已经定义了),这样的话,只有当对象与对象自身比较才会返回true(指定同一个对象);
// Object中的equals,只有对象与对象自身比较才会返回true
public boolean equals(Object obj) {
return (this == obj);
}
3、非自定义类(Java内置类)中进行比较的时候,不一定会使用Object.equals方法,因为内置类可能覆盖了equals方法,比如Integer的equals方法:
// Integer 中的equals方法,比较的两个对象中的“值”,而非原始的比较两个地址
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
4、覆盖equals方法,是因为我们要进行判断“逻辑相等”,比如两个对象的哪些属性相等,而不是简单判断是不是同一个对象;
5、覆盖equals时的规范:1、自反性;2、对称性;3、传递性;4、一致性;5、与null判断相等时,结果必需是false;
6、当在扩展类的时候(比如创建子类,也许会增加新字段),那么这个时候很难保留上面的equals规范;
7、实现equals方法的诀窍:
a:使用“==”检查“参数是否为这个对象的引用”
b:使用instance检查是否为正确的类型;
c:instance检查通过后,将参数转换为正确的类型;
d:根据业务需求检查参数中的某些字段是否与该对象中的字段相匹配,并且在比较的时候,优先匹配最有可能不一致的域;
8、覆盖equals方法的时候,总是要覆盖hashCode方法;
9、不要太过依赖equals方法进行比较,完全可以使用if elseif else 来进行逐项匹配;
10、推荐写法是x.equals(Object o),但是不要将equals方法的参数替换为其他类型,比如x.equals(X o),这样的做法并不是覆盖Object的equals方法,而是重载了Object.equals方法;为了防止这样的错误,可以使用@Override注解。
11、推荐使用Google的AutoValue框架,或者IDEA来生成equals方法;
第11条、覆盖equals时总要覆盖hashCode
1、如果定义类的时候只是覆盖了equals方法,但是没有hashCode方法,那么类的对象在使用HashMap和HashSet的时候就会出现问题,至于为什么,可以看一下HashMap的底层原理;
2、规范中对于equals和hashCode的描述:
a:应用执行期间,只要调用equals进行比较的那些字段信息没有发生更改,那么对同一个对象调用多次equals方法,hashCode都必须返回同一个值;一个程序与另外一个程序的执行过程中,执行hashCode的返回值可以不一致;
b:两个对象调用equals方法相等,那么hashCode也必须要相等;如果没有覆盖hashCode方法,那么就无法满足这一条;
c:如果通过equals方法比较不相等,但是hashCode是可以相等的;
3、覆盖hashCode的时候,应该尽量做到“为不相等的对象产生不相等的散列码”,但是不要为了提高性能,试图从散列码计算中排除某个对象关键域;
4、推荐使用Google的AutoValue框架,或者IDEA来生成hashCode方法;
下面是一个hashCode的示例:
@Override
public int hashCode() {
// 类中有id,name,addr三个属性,equals中也是比较这3个属性值,下面是计算hashCode的方式
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (addr != null ? addr.hashCode() : 0); // 如果增加了一个gender的属性,那么就加这么一行
// result = 31 * result + (gender!= null ? gender.hashCode() : 0); return result;
}
5、如果懒得自己动手覆盖equals、hashCode方法,可以使用lombok的@EqualsAndHashCode注解。
第12条:始终要覆盖toString
1、不覆盖toString方法,在打印对象的时候,输出的是ClassName@Number格式,无法看出对象的具体信息,调试时麻烦;
2、静态工具类、枚举类不需要覆盖toString方法,前者是因为没有意义,后者是因为Java已经提供了支持;
第13条、谨慎使用clone
实现Clonable接口,然后覆盖clone方法的例子
// 方式1
@Override
protected Object clone() throws CloneNotSupportedException {
return (Person) super.clone();
} // 方式2
@Override
public Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
注意事项:
1、不可变的类不应该提供clone方法;
2、需要注意深拷贝与浅拷贝的情况:如果属性是基本数据类型就不存在这个问题,但如果是引用数据类型(包括String),调用clone方法只是单纯的浅拷贝,需要进行递归clone;深拷贝与浅拷贝的区别;
3、解决深拷贝与浅拷贝,还可以先调用super.clone,然后把结果对象中的所有域都设置为初始状态,然后调用高层方法进行设置对象属性;
4、如果编写线程安全的类需要实现Clonable接口,那么clone方法也需要改成同步的,只需要在clone方法前加synchronized关键字即可;
5、对象拷贝的最佳方式提供拷贝构造器和拷贝工厂,如下示例:
package cn.ganlixin.effective_java; import lombok.Getter;
import lombok.Setter; @Getter
@Setter
public class Person { int id;
String name; public Person(int id, String name) {
this.id = id;
this.name = name;
} // 拷贝构造器
public Person(Person person) {
this(person.id, person.name);
} // 拷贝工厂,方法名随意
public static Person newInstance(Person person) {
return new Person(person);
} public static void main(String[] args) {
Person p1 = new Person(1,"hello"); Person p2 = Person.newInstance(p1);
System.out.println(p1 == p2); // false p1.setName("world");
System.out.println(p1.getName()); // world
System.out.println(p2.getName()); // hello
}
}
第14条:考虑实现Comparable接口
1、实现Compareable接口后,可以覆盖compareTo方法,这个方法可以进行等同性比较(和equal功能相同),还可以指定比较的顺序(自己写equals时,也可以指定,只不过一般会优先比较最有可能不同的属性);
2、实现了Compareable接口,并且覆盖compareTo方法后,可以进行元素的排序操作;
3、实现Compareable接口时,推荐加上泛型的类型,可以免去compareTo中强制类型转换;另外compareTo方法中,数值类型(int、double..)不建议使用>或者<符号,建议使用装箱基本数据类型
public class Person implements Comparable<Person> { int id; @Override
public int compareTo(Person o) {
// 不推荐使用> 或者 <符号进行比较,比如 return (x < y) ? -1 : ((x == y) ? 0 : 1);
// 其他数值类型也是一样的
return Integer.compare(id, o.id);
}
}
4、比较时,value1 > value2 返回 正数;value1 < value2 返回负数;两者相等,返回0;不要使用两数相减的方式,因为可能会出现溢出:
@Override
public int compareTo(Person o) {
// 不推荐使用两数相减,容易造成整数溢出
return id - o.id;
}
《Effective Java》第2章 对所有对象都通用的方法的更多相关文章
- [Effective Java]第三章 对所有对象都通用的方法
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- [Effective Java 读书笔记] 第三章 对所有对象都通用的方法 第八 ---- 九条
这一章主要讲解Object类中的方法, Object类是所有类的父类,所以它的方法也称得上是所有对象都通用的方法 第八条 覆盖equals时需要遵守的约定 Object中的equals实现,就是直接对 ...
- [Java读书笔记] Effective Java(Third Edition) 第 3 章 对于所有对象都通用的方法
第 10 条:覆盖equals时请遵守通用约定 在不覆盖equals方法下,类的每个实例都只与它自身相等. 类的每个实例本质上都是唯一的. 类不需要提供一个”逻辑相等(logical equality ...
- 《Effective Java》第3章 对于所有对象都通用的方法
第8条:覆盖equals时请遵守通用约定 覆盖equals方法看起来似乎很简单,但是有许多覆盖方式会导致错误,并且后果非常严重.最容易避免这类问题的办法就是不覆盖equals方法,在这种情况下,类的每 ...
- Effective Java读书笔记——第三章 对于全部对象都通用的方法
第8条:覆盖equals时请遵守通用的约定 设计Object类的目的就是用来覆盖的,它全部的非final方法都是用来被覆盖的(equals.hashcode.clone.finalize)都有通用约定 ...
- [Effective Java 读书笔记] 第三章 对所有对象都通用的方法 第十---十一条
第十条 始终覆盖toString() toString的实现可以使类使用起来更加舒适,在执行println等方法时打印出定制信息. 一单实现了自己的toString,指定输出的固定格式,在方法的文档说 ...
- Effective Java:对于全部对象都通用的方法
前言: 读这本书第1条规则的时候就感觉到这是一本非常好的书.可以把我们的Java功底提升一个档次,我还是比較推荐的.这里我主要就关于覆盖equals.hashCode和toString方法来做一个笔记 ...
- Java高效编程之二【对所有对象都通用的方法】
对于所有对象都通用的方法,即Object类的所有非final方法(equals.hashCode.toString.clone和finalize)都有明确的通用约定,都是为了要被改写(override ...
- 《Effective Java》读书笔记 - 3.对于所有对象都通用的方法
Chapter 3 Methods Common to All Objects Item 8: Obey the general contract when overriding equals 以下几 ...
随机推荐
- 线性排序总结(c++实现)
前面介绍了一些常用的比较排序算法,它们都是通过比较两个元素的大小进行排序,归并排序和堆排序在最坏情况下的复杂度为O(nlgn),可以证明(使用决策树模型),通过比较进行排序,算法的下界为O(nlgn) ...
- python+ddt+unittest+excel+request实现接口自动化
接口自动化测试流程:需求分析-用例设计--脚本开发--测试执行--结果分析1.获取接口文档,根据文档获取请求方式,传输协议,请求参数,响应参数,判断测试是否通过设计用例2.脚本开发:使用request ...
- JPA 报错:Page 2 of 1 containing UNKNOWN instances
JPA 中,page是从0开始,不是从1开始: 因此,将用户输入的从1开始的page页码减1: PageRequest pageRequest = PageRequest.of(page - 1, p ...
- table的各种用法
使用 colgroup 和 col 实现响应式表格(table的各种用法):http://coderlt.coding.me/2017/11/20/table-colgroup/
- reduce要素与适用总结
要素: 1.高阶函数:reduce: 2.处理函数:reducer: 3.数据:可以是具体数据.签名相同的普通函数.签名相同的高阶函数: reduce(reducer, datas(data or f ...
- 强制结束虚拟机 centos home 卷丢失导致无法挂载进入 emergency mode 紧急模式
参考文章: https://blog.51cto.com/13059784/2054378 xfs文件修复参考1:https://codeday.me/bug/20181112/367781.html ...
- python--requests模块初识
requests,发送http请求(用python模拟浏览器浏览网页)requests.get("http://www.baidu.com") 示例: import request ...
- [AGC007E] Shik and Travel
题目 给定一棵n节点的 以1为根的 满二叉树 (每个非叶子节点恰好有两个儿子)n−1 条边. 第ii条边连接 i+1号点 和 ai, 经过代价为vi设这棵树有m个叶子节点定义一次合法的旅行为:(1) ...
- [转]Reids配置文件redis.conf中文详解
转自: Reids配置文件redis.conf中文详解 redis的各种配置都是在redis.conf文件中进行配置的. 有关其每项配置的中文详细解释如下: 对应的中文版解释redis.conf # ...
- 【CF1142B】Lynyrd Skynyrd
[CF1142B]Lynyrd Skynyrd 题面 洛谷 题解 假设区间\([l,r]\)内有一个循环位移,那么这个循环位移一定有一个最后的点,而这个点在循环位移中再往前移\(n-1\)个位置也一定 ...