==:对于基本类型,比较的是值是否相等;对于引用类型,比较的是地址是否相等
equals:比较的是对象是否相等(不能比较基本类型,因为equals是Object超类中的方法,而Object是所有类的父类)
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
如果你不了解Java只有值传递,请看这篇文章:带你深入理解值传递
==来了
看下面的例子
String str = new String("zsh");
String str2 = new String("zsh");
String str3 = "zsh";
String str4 = "zsh";
System.out.println(str3 == str4);
System.out.println(str == str2);
System.out.println(str == str3);
结果:
因为这是引用类型,所以比较的是地址是否相等,另外这也涉及到字符串常量池的概念,字符串常量池是什么呢,我怎么想不起来了呢?
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
字符串常量池简单了说就是一块内存空间,减少重复创建字符串所需的时间,更多细节请看我的JVM专栏
分析str3==str4的结果
1、这两个对象没有直接使用new,所以当执行String str3=“zsh”时,字符串常量池中就会被创建一个“zsh”的字符串;
2、当执行到String str4=“zsh”,会先去常量池中查找是否有“zsh”这个字符串,如果有,则将str4也指向这个字符串;否者在常量池中创建该字符串。
那么结果为true,自然就能够明白了。
分析str==str2的结果
这个为false应该也很简单,new两个对象,那么地址肯定不同;
分析str==str3的结果
一个是在字符串常量池中,一个在堆中new的对象,那么地址肯定不同了
equals来了
String str = new String("zsh");
String str2 = new String("zsh");
String str3 = "zsh";
String str4 = "zsh";
System.out.println(str3.equals(str4));
System.out.println(str.equals(str2));
System.out.println(str.equals(str3));
是不是感觉很奇怪,明明比较的是引用,为什么都是true呢?这就要分析String的底层代码了
public boolean equals(Object anObject) {
if (this == anObject) { //比较地址是否相等
return true;
}//判断是否是String类型,如果不是,还比较个锤子,肯定false呀
if (anObject instanceof String) {
//转换为本地变量
String anotherString = (String)anObject;
//计算字符长度,注意Java字符串末尾没有\0,因为对象都内置的有length方法,所以不需要计算长度。
int n = value.length;
//如果长度不等,也不用比较了,肯定不相等
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//核心代码,逐个字符地比较
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
核心代码
这你应该就明白了,String中重写了Object中的equals方法,并重定义了它的功能,把它改成了比较字符串是否相同。
不仅String是这样,包装类也是同样如此
Integer
public boolean equals(Object obj) {
if (obj instanceof Integer) { //判断是否是Integer类型
return value == ((Integer)obj).intValue();//比较值是否相等
}
return false;
}
Float
public boolean equals(Object obj) {
return (obj instanceof Float)//是否满足是Float类型和值相等
&& (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}
Double
public boolean equals(Object obj) {
return (obj instanceof Double)
&& (doubleToLongBits(((Double)obj).value) ==
doubleToLongBits(value));
}
Boolean
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
Character
public final boolean equals(Object obj) {
return (this == obj);
}
Long
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
Boolean
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
包装类全部都重写了equals方法,将其的地址比较改为了值比较。
User user = new User();
user.setUsername("zsh");
User user2 = new User();
user2.setUsername("zsh");
System.out.println(user.equals(user2));
结果:false
这个结果应该都在大家的意料之中,User类没重写equals方法,自然就按照地址比较了