斗胆给Thinking in JAVA挑错

Posted on 2005-10-08 12:05 BlueO2 阅读(497) 评论(0)  编辑  收藏 所属分类: JAVA foundation
今天晚上被讨厌的vba郁闷,以后谁再跟我说vb简单易用我跟谁急。由于计划上每天都要继续讨论java基础语言特性,所以最近也返璞归真,重读thinking in java。发现了一个问题如下:
在第9章 持有你的对象arrays的比较章节,有如此代码片断:
String[] s1 = new String[5];
Arrays.fill(s1,"hi");
String[] s2 = {"hi","hi","hi","hi","hi"};
System.out.println(Arrays.equals(s1,s2));
输出结果true
Bruce给出的解释是s1的所有元素指向同一个对象,s2却拥有5个不同对象,但是Arrays.equals比较的是内容,还特意强调通过Object.equals(),所以结果为true.
首先,我们看看Arryas.fill(s1,"hi")如何工作:
SRC:
public static void fill(Object[] a, Object val) {
        fill(a, 0, a.length, val);
    }
public static void fill(Object[] a, int fromIndex, int toIndex,Object val){
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i=fromIndex; i<toIndex; i++)
            a[i] = val;
    }
显然所有的s1中原素指向了相同的一个对象"hi"这里Bruce没错,那么后面的s2是否拥有5个不同的对象呢?我们做以下代码测试(其中捎带着String类型的测试)
import java.util.Arrays;
public class profit{
        public static void main(String args[]){
                String s1 = "Hi";
                String s2 = "Hi";
                String s3 = new String("Hi");
                String s4 = new String("Hi");
                if(s1==s2) System.out.println("s1 s2 equal");
                if(s3==s4) System.out.println("s3 s4 equal");
                //System.out.println(s3.hashCode());
                //System.out.println(s4.hashCode());
                String[] strArray = new String[2];
                Arrays.fill(strArray,"hello");
                if(strArray[0]==strArray[1]) System.out.println("strArray equal");
                String[] strArray2 = {"hello","hello"};
                if(strArray2[0]==strArray2[1]) System.out.println("strArray2 equal");
                String[] strArray3 = {new String("hello"),new String("hello")};
                if(strArray3[0]==strArray[1]) System.out.println("strArray3 equal");

输出结果:
---------- Run ----------
s1 s2 equal
strArray equal
strArray2 equal

Output completed (1 sec consumed) - Normal Termination
首先,各位应该明白最基础的东西 ==实现的比较是浅比较,就是非reference指向的具体对象内容,而是是否指向了同一个对象。第二,String类型被设计为非可变类,就是说 String s1 = "hi";与String s2 = "hi";其实指向了同一个对象,只有用new构造函数的时候才会产生新的对象,而不管内容是否已经在内存中存在。如下文字阐述了一些:
s = new String("Initial Value");
每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同
ok,回到我们的话题上来String[] s2 = {"hi","hi","hi","hi","hi"};是否产生了5个不同的对象呢?我写的简单的测试代码

String[] strArray2 = {"hello","hello"};
 if(strArray2[0]==strArray2[1]) System.out.println("strArray2 equal");
的结果大家应该得出了结论了吧。
那么,bruce给出的代码的结果之所以为true就是因为,这么多个数组中的成员"hi"其实都是同一个对象。那么我们继续看Arrays.equals是否比较的内容呢?
SRC:
public static boolean equals(Object[] a, Object[] a2) {
        if (a==a2)
            return true;   //如果指向同一对象,当然是相同的,返回true
        if (a==null || a2==null)
            return false;  //如果为null就没有相等的一说,false

        int length = a.length;
        if (a2.length != length)  //长度不同
            return false;

        for (int i=0; i<length; i++) {
            Object o1 = a[i];
            Object o2 = a2[i];
            if (!(o1==null ? o2==null : o1.equals(o2)))  //此处为关键,object的equals比较,Arrays.equals并没有专门为String类型重载此方法,全部都依赖Object的方法比较
                return false;
        }

        return true;
    }

那么我们再看看Object这个树根的equals方法吧:
public boolean equals(Object obj) {
 return (this == obj);
    }

短短几行代码,但是,是浅比较毫无疑问,所以,bruce说的,依赖的是Object.equals,比较的是内容,更是错误的。
但是必须强调的是,当String数组传递进来的时候,由于java的RTTI执行期型别辨识,Arryas.equals调用的其实是String.equals方法,而String.equals方法是比较内容的,所以,此处我想是bruce没有说明白,而又偏偏列举了String的例子。如果是自己写的类有没有复写equals方法,此时就调用Object.equals了,那么根本不可能比较内容,所以Arrays.equals大家一定要知道怎么回事再用。


只有注册用户登录后才能发表评论。


网站导航:
 

posts - 29, comments - 3, trackbacks - 0, articles - 0

Copyright © BlueO2