前言
在Java中,==和equals都是判断是否相等的,返回值也都是Boolean类型,但是 == 和 equals 判断的内容是不一样的。
1. 基本数据类型的比较:比较的是数值。
2. 引用类型的比较:比较的是引用指向的值(地址)
注:Java中没有指针的概念,只有引用。
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方法,写定比较规则之后,自定义类才能比较引用对象的内容。
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方法,所以比较的是地址中的内容。 |