String str1 = "hello";
String str2 = new String("hello");
String str3 = str2;
System.out.println(str1 == str2);
System.out.println(str3 == str2);
System.out.println(str1 == str3);
打印结果:
false true false
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println(str3.equals(str2));
打印结果:
true true true
原因:
在java中我们一般把对象存放在堆区,把对象的引用放在栈区。因此上面三个字符串的内存状态应该是下面这样的。
现在明白了:
(1)String str1 = "Hello"会在堆区存放一个字符串对象
(2)String str2 = new String(“Hello”)会在堆区再次存放一个字符串对象
(3)String str3 = str2这时候Str3和Str2是两个不同的引用,但是指向同一个对象。
根据这张图再来看上面的比较:
(1)str1 == str2吗?意思是地址指向的是同一块地方吗?很明显不一样。
(2)str1 == str3吗?意思是地址指向的是同一块地方吗?很明显不一样。
(3)str2 == str3吗?意思是地址指向的是同一块地方吗?很明显内容一样,所以为true。
(4)str1.equals(str2)吗?意思是地址指向的内容一样吗?一样。
(5)str1.equals(str3)吗?意思是地址指向的内容一样吗?一样。
(6)str2.equals(str3)吗?意思是地址指向的内容一样吗?一样。
总结:
(1)基本类型比较:
①使用双等号 == 比较的是值是否相等。
②基本数据类型无equals方法(没有意义)。
(2)引用类型比较:
①重写了equals方法,比如String。
第一种情况:使用==比较的是String的引用是否指向了同一块内存。
第二种情况:使用equals比较的是String的引用的对象内容是否相等。
②没有重写equals方法,比如User等自定义类。
==和equals比较的都是引用是否指向了同一块内存。
注意:
字符串提供了intern 方法,意思是检查字符串池里是否存在,如果存在了那就直接返回为true。因此在这里首先s1会在字符串池里面有一个,然后 s2.intern()一看池子里有了,就不再新建了,直接把s2指向它。
// 字面值每次都会先检查内存中是否有此字符串
//如果有则不创建字符串对象直接返回。
String str1 = "hello";
// new关键字创建【每次都会创建一个新的字符串对象】
String str2 = new String("hello");
str2 = str2.intern();
System.out.println(str1 == str2);//true
System.out.println(str1.equals(str2));//true
我们进到Integer的equals看一看:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return this.value == (Integer)obj;
} else {
return false;
}
}
会发现内部使用的是用进行比较,这里号两边一个是基本数据类型,一个是Integer包装类型,因此会进行一个自动拆箱的过程,编译器会调用Integer的intValue()方法,然后比较两个的值,所以这里的equals比较的就是值
其余和Integer同类型的包装类内部有各自对equals方法的重写方式,例如Long:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return this.value == (Long)obj;
} else {
return false;
}
}



