面试例题3:The code output(下列代码输出结果是什么?)
import java.util.*;
public class Test{
private String value = null;
public Test(String v){
value = v;
}
public boolean equals(Test o){
if(o == this){
return true;
}
if(o instanceof Test){
Test test = (Test)o;
return value.equals(test.value);
}
return false;
}
public static void main(String[] args){
List list = new ArrayList();
Test test1 = new Test("object");
Test test2 = new Test("object");
Test test3 = new Test("object");
Object test4 = new Test("object");
list.add(test1);
System.out.println(list.contains(test2));
System.out.println(test2.equals(test3));
System.out.println(test3.equals(test4));
}
}
答案是false true false,为什么觉得有问题,先看下解析:
第一个输出false是因为list.add(test1);在list放入的是test1,也就是“object”的内存地址,而不是“object”本身。所以这样肯定是输出false。
第二个输出true,很明显,按照题目的equals的执行顺序,结果是true。
第三个是false,是调用了Object的Equals方法。
这道题困惑了我很久。因为我一直没有明白的是第三个为什么调用了Object的Equals方法,因为个人感觉test3.equals调用的是String.equals()方法;后来我加上了测试语句,代码如下。
import java.util.*;
public class Test{
private String value = null;
public Test(String v){
value = v;
}
public boolean equals(Test o){
if(o == this){
System.out.println("o == this"); //test1
return true;
}
if(o instanceof Test){
Test test = (Test)o;
System.out.println("o instanceof Test"); //test2
return value.equals(test.value);
}
return false;
}
public static void main(String[] args){
List list = new ArrayList();
Test test1 = new Test("object");
Test test2 = new Test("object");
Test test3 = new Test("object");
Object test4 = new Test("object");
list.add(test1);
System.out.println(list.contains(test2));
System.out.println(test2.equals(test3));
System.out.println(test3.equals(test4));
}
}
运行之后,输出如下图:
发现只输出了一句"o instanceof Test",然后就开始思考,后来去百度了下资料,参考了http://blog.csdn.net/witsmakemen/article/details/7323604这篇博文,是关于ArrayList的contains这个方法的实现方式,ArrayList的源码如下:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
也就是说,ArrayList会调用传进来的对象的equals()方法与list里面的元素逐个作比较,所以说解析中的第一句话:“ 第一个输出false是因为list.add(test1);在list放入的是test1,也就是“object”的内存地址,而不是“object”本身。” 并不是很合理,因为list.contains(test2)会调用test2的equals而在ArrayList的源码中我们可以看出contains(Object o)这个方法传递的是Object对象,相当于Object o = test2;但是我们这个时候会考虑多态的存在,即父类引用指向之类对象,但是请擦亮眼睛,我回到一开始那个程序,看看Test的equals方法:
public boolean equals(Test o){
if(o == this){
return true;
}
if(o instanceof Test){
Test test = (Test)o;
return value.equals(test.value);
}
return false;
}
这个并没有重写他的父类Object的equals方法,Object中应该是这样public boolean equals(Object o);所以不存在多态中方法调用的问题,也不能算是方法的重载,因为它们不是发生在同一个类内部,最多是写了一个新方法。到这里已经思路已经很清晰了吧。一开始我就陷入了方法重写的误区,后来去看了Object的API才发现自己搞错了,原谅我的才疏学浅。
所以这个问题应该这样解释:
第一个输出false是因为list.contains(test2)中test2是作为Object传递进来的,而且Test这个类没有对Object中的equals方法进行重写,所以调用的是它的父类Object的equals方法,Object中的equals方法是==比较,比较的还是元素的引用,list中只有test1的引用,所以输出false。
第二个输出true,这个比较明显,调用了题目中的Test的equals。
第三个输出false,是因为test3.equals(test4)这个方法中,test4的类型是Object,在Test中找不到equals(Object o)的方法,所以去super中即父类找,父类是Object,于是调用父类的equals(Object o)方法,所以还是调用test3父类Object的equals方法,比较的还是引用,所以是false。
最后,我们证明一下,我们稍微改动一下equals方法,如程序所示:
import java.util.*;
public class Test{
private String value = null;
public Test(String v){
value = v;
}
public boolean equals(Object o){ //这里我们将参数Test o改成Object o
System.out.println("load equals()");
if(o == this){
System.out.println("o == this");
return true;
}
if(o instanceof Test){
Test test = (Test)o;
System.out.println("o instanceof Test");
return value.equals(test.value);
}
return false;
}
public static void main(String[] args){
List list = new ArrayList();
Test test1 = new Test("object");
Test test2 = new Test("object");
Test test3 = new Test("object");
Object test4 = new Test("object");
list.add(test1);
System.out.println(list.contains(test2));
System.out.println(test2.equals(test3));
System.out.println(test3.equals(test4));
}
}
运行结果如图:
这个程序中我们对Object的equals方法进行了重写,并且父类引用指向了子类对象,所以就存在了多态的问题,所以contains()方法调用了Test中的equals(),所以输出三个都是true。
因为基础不扎实,所以这个小问题困扰了我一下午,后面查了很多资料,看了一些博文,才发现问题所在,看了一半《java程序员面试宝典》这本书,个人感觉很多地方还是有一些错误的,很不靠谱,建议读者要持怀疑的态度去看。