全面解读 == 和 equals

前言

    在Java中,==和equals都是判断是否相等的,返回值也都是Boolean类型,但是 == 和 equals 判断的内容是不一样的。

== 比较的是值

    1. 基本数据类型的比较:比较的是数值。

    2. 引用类型的比较:比较的是引用指向的值(地址)

    注:Java中没有指针的概念,只有引用。

equals 比较的是地址或者内容

    equals默认比较的也是地址,这个equals方法是Object类的方法;我们可以看一下Java源码:

See Also:
equals(Object), System.identityHashCode   
 public boolean equals(Object obj) {
        return (this == obj);
    }

    this代表当前对象的引用,也就是说谁调用equals方法谁就是this,obj是参数,可以看到Object类还是用 == 来比较两个对象的,所以Object类的equals方法还是比较的地址的值(在引用比较的过程中。

    所以在自定义的类中,如果需要比较的是内容,此时就需要学String类,重写equals方法,写定比较规则之后,自定义类才能比较引用对象的内容。

测试你是否真正了解 == 和 equals 方法的区别:

public static void main(String[] args) {
        String s1 = new String("ja");
        String s2 = new String("ja");
        System.out.println(s1 == s2);//false
        String s3 = "ja";
        String s4 = "ja";
        System.out.println(s3 == s4);//true
        System.out.println(s3 == s1);//false
        String s5 = "jaja";
        String s6 = s3 + s4;
        System.out.println(s5 == s6);//false
        final String s7 = "ja";
        final String s8 = "ja";
        String s9 = s7 + s8;
        System.out.println(s5 == s9);//true

        String s10 = "ja" + "ja";
        System.out.println(s5 == s10);//true
    }

   解析:

第一个输出:

    因为s1和s2都是new了两个对象,new对象的时候会在内存中开辟两块不同的空间,== 如果比较的是引用,此时比较的是对象的地址是否想同,输出false。

 第二个输出: 

    s3和s4两个引用都没有new对象,都是赋值了一个字符串常量,我们都知道字符串的不可变性,是因为字符串字面常量是放在字符串常量池中的,所以在赋值操作的过程,jvm先去看常量池中是否有这个对象,如果没有,就在常量池中创建 "ja" 这个字符串常量。   再等s4赋值时,此时就先去常量池中检查是否有这个字符串,如果有就不会再创建这个字符串了,直接让s4指向s3这个引用所指向的地址,所以此时s3和s4两个引用所指向的是同一个地址,输出true。

  第三个输出:

    s3引用的字符串在常量池中,s1是new了一个对象,开辟了新的空间,所以此时两个地址是不同的,输出false。

 第四个输出:

    s6引用指向的是s3 和 s4 两个字符串的拼接,s3 和 s4 是变量,虽然指向的字符串常量是放在常量池中的,但是s3 和 s4 两个引用的指向是随时可以改变的,而且字符串引用的拼接:我们可以看一下反编译的源码,String对于字符串的拼接底层是调用了StringBuilder 类中的方法的,会new一个新的StringBuilder对象,然后调用StringBuilder类中的append方法对字符串进行拼接,产生了新的对象,而s5指向的是字符串常量,在常量池中存放,两个引用的地址一定是不一样的,输出false。

 第五个输出:

    s7和s8两个引用是被final修饰的,被final修饰的变量是常量,也就是不可改变的量,此时编译器就会做出优化:在编译时期就把s7 和 s8 两个常量进行拼接,在运行之后 s9行代码和s5行代码是一样的,s5所指向的对象是放在常量池中的,s9同样也会指向这个对象,所以s5 和s9地址的值是一样的, 输出true。

第六个输出:

    s10同样的是字符串字面常量进行拼接,所以在编译时期就会将两个字符串进行拼接,在运行时和s5行代码是一样的,所以输出 true。

总结

1. 如果是常量引用的字符串字面常量进行拼接,编译器会做出优化:在编译时期就将字符串拼接的工作完成。
2. 如果是变量引用的字符串字面常量,虽然字符串是常量,但是引用的指向是可以发生改变的,所以编译器不确定引用的指向是否会发生改变,此时编译器不会做出优化。
3. 上述代码如果用equals方法进行比较,输出的结果都是true,因为String类重写了equals方法,所以比较的是地址中的内容。

你可能感兴趣的:(java,开发语言)