1、超类Object的equals()底层原理:
在Object超类中已经提供了equals()方法,源码如下:
public boolean equals(Object obj) { return (this == obj); }
所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较的是两个对象的内存地址,在Object的equals()底层调用的是==号,所以说Object的equals()是比较两个对象的内存地址是否相等,如果为true,则表示的引用的是同一个对象。
2、equals()与 == 的区别:
(1)== 号在比较基本数据类型时比较的是数据的值,而比较引用类型时比较的是两个对象的地址值;
(2)equals()不能用于基本的数据类型,对于基本的数据类型要用其包装类。
(3)默认情况下,从Object继承而来的 equals 方法与 “==” 是完全等价的,比较的都是对象的内存地址,因为底层调用的是 “==” 号,但我们可以重写equals方法,使其按照我们的需求方式进行比较,如String类重写equals()方法,使其比较的是字符的内容,而不再是内存地址。
String类重写的equals源码如下:
public boolean equals(Object anObject) {//判断调用equals方法的对象和 形参引用地址是否相等if (this == anObject) {return true;}//判断左边形参引用是否是右边String类的实例对象if (anObject instanceof String) {//将形参引用强制转换为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;}
3、equals()的重写规则:
- 自反性。对于任何非null的引用值x,x.equals(x)应返回true。
- 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。
- 传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。
- 一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。
- 对于任何非空引用值x,x.equal(null)应返回false。
4、有关equals()与 == 号的小例子:
public class Test {public static void main(String[] args) { String str1 = new String("abc");String str2 = new String("abc");System.out.println(str1 == str2);//falseSystem.out.println(str1.equals(str2));//trueString str3 = "123";String str4 = "123";System.out.println(str3 == str4);//trueSystem.out.println(str3.equals(str4));//true }
}
在上述代码中,str1和str2都是new String()的对象,所以尽管()中的字符串值一样,但仍然是两个不同的对象,所以str1==str2比较对象地址不同,为false。而string的equals比较的是字符串值,所以str1.equals(str2)为true。而str3和str4都是在堆中创建了同一个字符串对象“123”,指向的都是字符串常量池中的“123”,所以str3==str4为true。
5、重写equals()中 getClass 与 instaceof 的区别:
在重写equals() 方法时,一般都是推荐使用 getClass 来进行类型判断,不是使用 instanceof(除非所有的子类有统一的语义才使用instanceof)。我们都知道 instanceof 的作用是判断其左边对象是否为其右边类的实例,返回 boolean 类型的数据,可以用来判断继承中的子类的实例是否为父类的实现。
下来我们来看一个例子:父类Person
public class Person {protected String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(String name){this.name = name;}public boolean equals(Object object){if(object instanceof Person){Person p = (Person) object;if(p.getName() == null || name == null){return false;}else{return name.equalsIgnoreCase(p.getName ());}}return false;}}
子类 Employee:
public class Employee extends Person{private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}public Employee(String name,int id){super(name);this.id = id;}/*** 重写equals()方法*/public boolean equals(Object object){if(object instanceof Employee){Employee e = (Employee) object;return super.equals(object) && e.getId() == id;}return false;}}
上面父类 Person 和子类 Employee 都重写了 equals(),不过 Employee 比父类多了一个id属性,而且这里我们并没有统一语义。测试代码如下:
public class Test {public static void main(String[] args) {Employee e1 = new Employee("employee", 23);Employee e2 = new Employee("employee", 24);Person p1 = new Person("employee");System.out.println(p1.equals(e1));//trueSystem.out.println(p1.equals(e2));//trueSystem.out.println(e1.equals(e2));//false}}
上面代码我们定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理来说结果应该全部是 false,但是事与愿违,结果是:true、true、false。对于那 e1!=e2 我们非常容易理解,因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2,这是非常奇怪的,因为 e1、e2 明明是两个不同的类,但为什么会出现这个情况?首先 p1.equals(e1),是调用 p1 的 equals 方法,该方法使用 instanceof 关键字来检查 e1 是否为 Person 类,这里我们再看看 instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回 true 了,而两者 name 又相同,所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof,这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof(除非子类拥有统一的语义)。