Java中的 == 和 equals 到底有什么区别?
- 提到Java的 == 和 equals,很多人就会说 == 比较的值(地址), equals比较内容,这句话到底对不对?
equals是object类的一个方法,定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
我们看到,当调用equals时, 实际还是调用了 == ,而 == 操作符 当且仅当 左右两端的值相等时返回true,那么我们常说的equals比较内容又有错吗?错在哪里?
实际上我们口中说的比较内容大都是说String类,String类重写了Object的equals方法,定义如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
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的equals方法同样先 == 判断,如果值相等返回true,否则将比较对象转成String(此处讲解String比较),而后比较它们的字符序列,这就是常说的“内容”, equals方法相对简单,不做过多讲解。
2. 为什么Java中字符串比较都用equals而不用 ==
上面已经讲到字符串的equals方法,几个字符串 == 比较的示例代码,读者可以不看答案自己心里先思考一下是否相等,看看自己是否理解了 字符串的 == 操作。
public static final String str0 = “abcde”;
public static void main(String[] args) {
String strObj = new String(“abc”);
String strObj1 = new String(“abc”);
String strObj2 = new String(“de”);
String str1 = “abc”;
String str2 = “de”;
final String str3 = “abc”;
final String str4 = “de”;
System.out.println(strObj == strObj1);//false
System.out.println(strObj.intern() == strObj1.intern()); //true
System.out.println((strObj1 + strObj2) == str0); //false
System.out.println((str1 + str2) == str0);//false
System.out.println((str1 + str2).intern() == str0);//true
System.out.println((str3 + str4) == str0);//true
System.out.println((str3 + str4).intern() == str0); //true
首先 new String 操作会在堆上创建一个String对象,若字符串常量池中没有此字符串则也会在常量池中放一份,这里也有一道面试题String a = “a”;和String a = new String(“a”)有什么区别,区别就是前者创建一个后者则会创建两个, 所以这里第一个就会返回false。
String.intern方法,如果常量池中已有此对象则返回该对象的引用, 如果没有则在常量池中放一份并返回该引用。因此,strObj.intern()时常量池没有,会放入“abc”,并返回该引用,而strObj1.intern()时常量池中已有该字符串,则直接返回该引用,因此地址值相等,返回true。
第三行,被 String类对+操作符的重载导致字符串相加实际上是创建了StringBuilder对象的append方法之后调用ToString()实现的,因此等同于:
new StringBuilder().append(strObj1).append(strObj2).toString(),显然是false。
第四行,和第三行一样,返回false。
第五行,有了前面的基础,第五行可以看作是"abcde".intern() == “abcde”,这样返回true就不难理解了,因为字符串”abcde“已经存在,直接返回引用。
第六行,这里注意,与前面的区别是final修饰符,被final修饰的不可变量会在编译期优化掉,因此第六行编译期间就会"知道" str3 + str4实际上是"abcde"。
第七行在第六行的基础上已经是显而易见了。
实际上,字符串字面量相加也会被编译优化,例如 String test = “abc” + “de”;也会在编译期被认为String test = “abcde”;因此,如果声明了这个test,则test == str0和 (“abc” + “de”)== str0都会返回true。