String用==和equals两种方式比较的区别

文章目录

==

面试的时候经常会遇到让你说明==equals的区别,通常会拿String类做举例,比如下面代码:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";
        System.out.println(s1 == s2);
    }
}

输出结果为:true
但是如果是下面的代码:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");
        System.out.println(s3 == s4);
    }
}

输出结果为:false
为什么同样的字符串值,一个是直接赋值给String,一个是通过new实例,2者比较却截然不同。接下来我们先说下==的结论,然后我们再证明,结论如下:

结论一:比较两个值或对象指向的地址值是否相同。
结论二:如果是基本数据类型,则直接比较其值是否相等。

我们看看结论一:先利用JClassLib工具查看class文件字节码指令信息,从以下图看出,红圈部分就是在使用==比较判断用的字节码指令
在这里插入图片描述
if_acmpne是什么意思呢?点击 if_acmp 可以查看它的介绍,总的来说就是if_acmp就是比较2个的地址是否相等,它是Java虚拟机的一个字节码指令,因此在Java虚拟机里对==的这种比较是采用它们指向的地址是否相同,那怎么知道2个值或对象指向的地址是否相同呢?由于Java没有直接获取自己的地址的方法,这边可以用写好的 AddressUtil.java 这个工具类可以拿到对象地址,代码如下:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";

        AddressUtil.printAddresses("s1的地址",s1);
        AddressUtil.printAddresses("s2的地址",s2);
        System.out.println(s1 == s2);
    }
}

输出结果为:

s1的地址: 0x76b18c570
s2的地址: 0x76b18c570
true

可以看到s1地址和s2的地址是相同的,因此符合s1 == s2比较的结果是true。接下来看下面的代码:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");

        AddressUtil.printAddresses("s3的地址",s3);
        AddressUtil.printAddresses("s4的地址",s4);
        System.out.println(s3 == s4);

    }
}

输出的结果为:

s3的地址: 0x76b18c5d0
s4的地址: 0x76b18c628
false

可以看到s3 的地址和s4的地址是不一样的,因此符合s3 == s4比较的结果是false那么思考一下,为什么s3和s4的地址不相同呢?

我们再来看看结论二:如果是基本数据类型,则直接比较其存储的 “值”是否相等。我们看下面代码:

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=1;
        System.out.println(a==b);
    }
}

输出结果为true,这里a和b都是基本数据类型,我们来看看它是怎么直接比较存储值是否相等的 ,同样用JClassLib工具查看class文件字节码指令信息,如下图:
在这里插入图片描述
看到红圈部分if_icmpne在官网有解释,点击if_icmpne可以查看,总的俩说if_icmpne指令就是用来比较int值是否相等的,依此类推,你能证明float、double、long、char等等基本数据类型也是比较存储值是否相等吗?

equals

接下来看equals方法,代码如下:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";
        System.out.println(s1.equals(s2));

    }
}

输出结果为true
再看下面代码:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");
        System.out.println(s3.equals(s4));

    }
}

输出结果为true

从上面看出,不管是直接赋值给String的还是new实例出来的,equals比较都是true,那equals是比较地址的值还是比较的是内容呢?先说下equals的结论:

结论一:默认比较的是两个值或对象指向的地址值是否相同
结论二:String类重写了equals()方法,比较的是内容是否相同

来看看结论一,首先都知道Object是所有类的父类,任何类都默认继承Object,而String的equals方法重写的是Object类equals方法:java.lang.Object#equals,来看看Object类里equals方法的代码是怎么实现的:

    public boolean equals(Object obj) {
        return (this == obj);
    }

居然默认使用了==比较,前面说了==比较是比较的是地址是否相同,因此结论一是对的。
然后再来看结论二,String类重写了equals()方法,来看看java.lang.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的equals方法代码逻辑是比较字符串内容的,依此类推Integer、Double、Float对象是不是也是重写equals了方法。

总结

  1. ==和equals比较的是两个值或对象指向的地址值是否相同
  2. 如果比较内容是否相同需要重写equals方法,在重写的方法实现比较内容的逻辑,比如String、Integer类就是重写equals了方法

延伸:对象作为 map 的 key 时,为什么需要重写 equals 方法和 hashCode 方法?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章