前言
相信大家一定在很多地方多看到过==
与equals()
的区别,也把区别讲的很详细,每次记住了,很快又会变得模糊不清。
这时就需要从原理深入剖析,一招制敌!
认识==的用法
在Java中,==的作用有两个
(1).基础数据类型:比较的是两者的值是否相等,比如int,float,double变量。
public static void main(String[] args) {
int a = 1;
int b = 1;
float x = 3.2f;
float y = 3.2f;
System.out.println(a == b);
System.out.println(x == y);
}
此时结果,两个都为true。
(2).引用数据类型:比较的是两者的地址是否相同,比如Integer等。String是一个特殊的,后面再讨论。
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
Integer c = 1;
System.out.println(a == b);
System.out.println(a == c);
}
此时结果,两个都为false。因为使用Integer类,每次都创建了新的对象,所以分配的地址是不同的。但Integer类与int作比较结果如何呢?
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
int c = 1;
System.out.println(a == b);//false
System.out.println(c == a);//true
System.out.println(c == b);//true
}
输出结果:false,true,true。此时我们不难发现,当包装类与其基本类型进行比较时,会转变为对两者的值的比较。
认识equals()的用法
equals()方法源自Object类,先看一下源码是什么样:
public boolean equals(Object obj) {
return (this == obj);
}
看到这里,很快就发现equals()方法和==是相同的,所以在使用基本数据类型和引用对象(包装类除外)比较时,两者不存在区别。
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
int c = 1;
System.out.println(a.equals(c));
System.out.println(b.equals(c));
}
此时结果为:true,true。当包装类与其基本类型用equals()方法进行比较时,同样比较两者的值,也不存在区别。
不同点
有一点是比较特殊的,在使用equals()方法对包装类和包装类进行比较时,则会不同。
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a.equals(b));//true
System.out.println(a == b); //false
}
输出结果:true,false。包装类和包装类采用equals()方法,也会转变成对值得比较。为什么呢?因为包装类重写了Object类的equals()方法。打开重写后equals()方法的源码:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
从这里可以看出,包装类和包装类的比较,就是两者值得比较。
String类中的==和equals()方法
在String中,重写了Object类的equals(),自然逻辑和功能不同了。
String类中的equals()方法
先来看看String类中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;
}
在源码第一个判断中,若两个对象指向地址相同,则返回true。第二个判断中,若字符内容相同,则返回true。其实就是对两者值得比较。
public static void main(String[] args) {
String a = "Hello";
String b = "Hello";
System.out.println(a.equals(b));//true
}
输出结果:true。
String类中的测试
public static void main(String[] args) {
String a = "Hello";
String b = "Hello";
String c = new String("Hello");
String d = c;
System.out.println(a == b);//true
System.out.println(a == c);//false
System.out.println(c == d);//true
System.out.println(a.equals(b));//true
System.out.println(a.equals(c));//true
System.out.println(c.equals(d));//true
}
为什么出现这样的结果。可以从内存的角度来解释:
3.通过内存来解释
在Java中,一般是把对象放在堆区,对象的引用则放在了栈区。
从上图可以看出:
当String a = “Hello”;创建一个引用类型a,指向堆区一个位置,用于保存字符串对象;
当String b = “Hello”;java会到它的常量池中找"hello"是不是在常量池中已存在。如果已经存在则返回这个常量池中的"hello"的地址(在java中叫引用)给变量b;
String c= new String(“hello world”); 会在堆区再次存放一个字符串对象;
String d = c;当中d和c是相同的引用,指向同一个地址。
所以根据此可以判断以上输出结果:
//true 指向同一个地址
System.out.println(a == b);
//false 指向不同地址
System.out.println\(a == c\);
//true 指向同一个地址
System.out.println\(c == d\);
//true 地址指向的内容相同
System.out.println\(a.equals\(b\)\);
//true 地址指向的内容相同
System.out.println\(a.equals©\);
//true 地址指向的内容相同
System.out.println\(c.equals\(d\)\);