对于基本数据类型来说,== 比较的是值
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 10;
System.out.println(a == b); // output: false
System.out.println(a == c); // output: true
}
}
对于引用数据类型来说,== 比较的是对象的内存地址。
public class Book {
private String bookName;
private String bookAuthor;
public Book() {
}
public String getBookName() {
return this.bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return this.bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
}
public class Main {
public static void main(String[] args) {
Book book1 = new Book();
book1.setBookName("西游记");
book1.setBookAuthor("吴承恩");
Book book2 = new Book();
book1.setBookName("西游记");
book1.setBookAuthor("吴承恩");
System.out.println(book1); // output: Book@16b98e56
System.out.println(book2); // output: Book@7ef20235
System.out.println(book1 == book2); // output: false
}
}
即使 book1 与 book2 对象的属性值相同,他们 == 的结果还是 false,就是因为内存地址不同。
new 方式
String a = new String("yunhu");
String b = new String("yunhu");
System.out.println(a == b); // output: false
因为 a 和 b 都是 String 的对象引用,即使内容相同,但是指向的是不同的内存地址。
直接赋值方式
String aa = "yunhu";
String bb = "yunhu";
System.out.println(aa == bb); // output: true
直接赋值,对象存储在常量池中。
虚拟机会在常量池中寻找是否已经有与将要创建的值相同的对象,如果有直接赋值给当前的引用,如果没有,新建一个 String 对象。
equals()
只判断对象是否相等,没有重写 equals()
函数的话,判断的依据是对象的地址是否相同。
Book book1 = new Book();
book1.setBookName("西游记");
book1.setBookAuthor("吴承恩");
Book book2 = new Book();
book2.setBookName("西游记");
book2.setBookAuthor("吴承恩");
System.out.println(book1.equals(book2)); // output: false
判断对象地址,当然不同。
这边为了方便,没有重写 hashCode()
方法, 不过不影响理解。
public class Book {
private String bookName;
private String bookAuthor;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
// 判断 obj 对象是不是 Book 类的
if (!(obj instanceof Book)) {
return false;
}
Book book = (Book)obj;
if(book == null) {
return false;
}
if (this.bookName.equals(book.getBookName()) && this.bookAuthor.equals(book.getBookAuthor())) {
// 属性值相同,认为是同一个对象
return true;
}else {
return false;
}
}
}
Book book1 = new Book();
book1.setBookName("西游记");
book1.setBookAuthor("吴承恩");
Book book2 = new Book();
book2.setBookName("西游记");
book2.setBookAuthor("吴承恩");
System.out.println(book1.equals(book2)); // output: true
String a = new String("yunhu");
String b = new String("yunhu");
System.out.println(a.equals(b)); // output: true
想了想,这不对啊,a、b 是对象引用,equals 比较的是地址,这两个地址不同啊,怎么会返回 true。
String 确实是对象,但是它特殊处理了,重写了 equal 函数,变成比较值,而不是内存地址。
String equals 方法的源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
再看下 StringLatin1
类的 equals 源码:
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
确实是在比较值。