java基础面试题之老题新解:"==" 和 "equals()"方法的区别

来源:互联网 发布:ext json 编辑:程序博客网 时间:2024/05/17 08:44
这是一道经典的面试题,在java面试中出现频率极高,网络上流传的答案多半是不严谨的或者根本就是错误的。一个典型的错误答案是,"=="比较的是地址,"equals()"比较的是内容。
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}}public class EqualsTest {public static void main(String[] args) {Point p1 = new Point(1, 2);Point p2 = new Point(1, 2);System.out.println(p1.equals(p2));}}
按照上面的理论上面代码输出结果为 true,但是真实的结果却是false。这到底是为什么呢?  首先我们来分析一下这个 "equals" 方法,该方法是 java.lang.Object 类的方法。因为 java 中的 class 都隐含继承自 Object类,  所以别的类也都包含该方法,由于我们在上面的 Point 类中并没有对 “equals” 方法进行重写,于是其表现行为是由其父类即 Object 决定的。  下面我们来看看objcet类中的equals方法到底是个什么样子  
public boolean equals(Object obj) {    return (this == obj);}
看到没有,如果我们在子类中不对equals方法进行重写,它的默认行为是和“==”是一样的。比较的都是该引用是否真的指向物理内存中的同一个对象。  因为p1,p2是指向不同的对象,这就解释了为什么上面的代码会输出false。为什么那么多的程序员会认为“equals”比较的是内容呢?根据我的观察,绝大多数初学者是被“String”类给误导了。因为:
String s1 = new String("ustc");String s2 = new String("ustc");System.out.println(s1.equals(s2));
输出结果为true。为了解释上面的代码的输出结果,我们得看一下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 类重写了父类 Object 中的“equals”方法才让其具备了:String 类的equals方法比较的是内容这一行为。  也正式这样误导了很多的初学者。  现在让我们回到刚才那个 Point 的例子,我们只需要重写 equals方法,就可以得到我们期望的“相等性”。修改后的代码如下:
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object that) {if (this == that)return true;if (that instanceof Point) {Point p = (Point)that;return p.x == this.x && p.y == this.y;}return false;}}public class EqualsTest {public static void main(String[] args) {Point p1 = new Point(1, 2);Point p2 = new Point(1, 2);System.out.println(p1.equals(p2));}}
这样一来,输出结果即是true了。  到目前为止,我们对equals做了不少的描述,相信大家也有了个整体的认识。在java中 equals() 方法实现的是一种“逻辑上的相等性”,这个可以是由我们程序员来控制的。  比如在现实世界中经常說,这对双胞胎姐妹长得一模一样。身高、发型、脸蛋都长的一样,其实这里我们就是用equals来进行“相等性”的判断。但是其父母还是可以很容易的  分辨她们的,她们确实不是同一个人,她们的身份证是不一样的。现实生活中的唯一标识,这里隐含着她们父母用的是“==”来判断。  其实,我们还可以非常变态的重写“equals”方法。(这种写法是错误的,为了让大家印象深刻,笔者故意写了如下的版本
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object that) {return false;}}public class EqualsTest {public static void main(String[] args) {Point p1 = new Point(1, 2);System.out.println(p1 == p1);System.out.println(p1.equals(p1));}}
p1 == p1 // true   p1.equals(p1) // false    明明是同一个对象,用equals方法判断竟然是false。  现在大家应该很明白了吧,equals的控制权完全由你决定。  但是重写 equals 方法还是要遵循一定的准则的,不能像我们上面那种极端的做法。
下面是冲JDK帮助文档中摘下来的片段,也正是重写equals方法所要遵循的准则。equals 方法实现了等价关系: 1.自反性,对任何非空引用x, x.equals(x) 应该返回 true。 2.对称性,对任何非空引用x,y,x.equals(y) 应该返回 true,当且仅当 y.equals(x)返回true。 3.传递性,对任何非空引用x,y,z,如果x.equals(y)为true而且y.equals(z)为true,则x.equals(z)为true。 4.一致性,对任何非空引用x,y,对x.equals(y)多次调用的结果应该相同,如果返回true,每次调用结果都为true,为false,则都为false。 5.非空性,对任何非空引用x,x.equals(null)始终返回false。 上面的版本,违反了上述多种准则。

结论:

1. 在 java 中对于原生数据类型,比如 int, boolean 之类的,“==”就是和我们日常在算术中的等于号类似。2. 对于引用类型,“==”用来判断该引用是否真的指向内存中的同一个对象。3. equals 方法是 Object 类中的方法,程序员通过重写该方法从而实现期望的相等性,完全由程序员控制,但是要遵循上面的五个准则。
一言以蔽之:“==”用来判断物理意义上的相等性;“equals"用来判断逻辑上的相等性。进一步阅读:《Effective java》条目:重写equals()时,总要重写hashcode()方法。
最后给出一道题来考察下大家的学习效果:下面代码的输出结果是什么,true or false?
public class Person {private String  name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public boolean equals(Object o) {if (this == o)return true;if (o instanceof Person) {Person p = (Person)o;return this.name == p.name && this.age == p.age;}return false;}public static void main(String[] args) {String name1 = new String("a");String name2 = new String("a");Person p1 = new Person(name1, 1);Person p2 = new Person(name2, 1);System.out.println(p1.equals(p2));}}
原创粉丝点击