Java中的==和equals()
理解"=="的含义
==
在Java中有两个作用:
- 基本数据类型的比较。比较他们的值是否相等,比如两个int型变量,比较变量值是否相等。
- 引用数据类型的比较。这里注意,引用数据类型包括两部分,一是其引用地址(栈内存中),而是其对象内容(堆内存中)。
==
在这里比较的是引用地址是否相同。
public class Main {
public static void main(String[] args) {
int a = 1, b = 1;
System.out.println(a == b); //true
//此时使用的是引用类型Object
//o1和o2是引用变量,指向的其栈内存地址
//new Object()是引用的内容,即一个新的Object,存储在堆内存
Object o1 = new Object();
Object o2 = new Object();
System.out.println(o1 == o2); //false
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2); //false
}
}
理解equals()的含义
Object
是所用类的父类,其定义的方法有equals()
。我们先来看一看它的源码:
//注意这里只是Object的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
在这里obj
是一个引用数据类型,==
比较的是引用的地址,equals
也是比较的是引用的地址,所以他们的效果在这里是一样的。
重写equals()方法
我们先来看看这个代码:
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2); //false
//这里为什么是true?
System.out.println(str1.equals(str2)); //true
}
}
这里str1.equals(str2)
为true
是因为String
类(Object
的子类)对equals()
方法进行了重写。我们来看看String
的equals()
的源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (!COMPACT_STRINGS || this.coder == aString.coder) {
return StringLatin1.equals(value, aString.value);
}
}
return false;
}
从上面的源码,我们能够获取到的信息是:String中的equals方法其实比较的是字符串的内容是否一样。也就是说如果像String
、Date
这些重写equals
的类,你可要小心了。使用的时候会和Object
的不一样。
测试String
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
String str3 = str2; //引用传递
System.out.println(str1 == str2); //false
System.out.println(str1 == str3); //false
System.out.println(str2 == str3); //true
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println(str2.equals(str3)); //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)
嘛?意思是地址指向的内容一样嘛?一样。
(4)str1.equals(str3)
嘛?意思是地址指向的内容一样嘛?一样。
(4)str2.equals(str3)
嘛?意思是地址指向的内容一样嘛?一样。
总结
(1)、基础类型比较
使用==
比较值是否相等。
(2)、引用类型比较
①重写了equals
方法,比如String
。
第一种情况:使用==
比较的是String
的引用是否指向了同一块内存,即引用类型的地址是否相同。
第二种情况:使用equals
比较的是String
的引用的对象内容是否相等。
②没有重写equals
方法,比如User等自定义类
==
和equals
比较的都是引用是否指向了同一块内存。
一个小问题:
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
str2 = str2.intern();
System.out.println(str1 == str2); //true
System.out.println(str1.equals(str2)); //true
}
}
在这里多了一个intern
方法。intern
方法是检查字符串池里是否存在,如果存在了那就直接返回为true
。因此在这里首先s1会在字符串池里面有一个,然后 s2.intern()
一看池子里有了,就不再新建了,直接把s2
指向它。
参考链接:link