在Java中“==”的含义需要看比较的是什么类型的数据。
如果比较的都是基本类型的数据,那么“==”的含义就是比较它们之间的值是否相等:
public static void main(String[] args) throws Exception {
int a = 10;
int b = 10;
double c = 10.0;
double d = 10.0;
// print true
System.out.println(a == b);
// print true
System.out.println(a == c);
// print true
System.out.println(c == d);
}
如果比较的都是引用数据类型,那么比较的是两者之间存放内容的地址是否是相等,而不是比较它们的内容,如何证明这一点呢?我们来看如下的例子:
public static void main(String[] args) throws Exception {
String s1 = "zhangsan";
String s2 = "zhangsan";
String s3 = new String("zhangsan");
String s4 = new String("zhangsan");
String s5 = s3;
// print true
System.out.println(s1 == s2);
// print false
System.out.println(s1 == s3);
// print false
System.out.println(s3 == s4);
// print true
System.out.println(s3 == s5);
}
s1和s2都是字符串常量值,它们被放置在栈内存中,相同内容只会保留一份,所以当比较它们的时候,都是指的同一份内容,即地址也是相同的,因此返回true;
s3是新建了一个对象,存放在堆内存中,它和s1虽然内容相等,但是存放的地方不一样,地址当然也是不同的,因此它们比较的结果是返回false;
s4是仿照s3重新建立了一个对象,在堆内存中,即使相同内容的对象也是两个不同的对象,所以它们地址是不同的,比较的结果是false;
s5是赋值了s3对象所在的地址,那么此时s3和s5都是指向堆内存中相同的地址,因此它们比较的结果是返回true;
知道了“==”的作用后,我们再来看看equals的作用。equals最早是在Object类中出现的,因此,可以理解为所有的类都是具有equals方法的,如下是Object中的相关源码:
public boolean equals(Object obj) {
return (this == obj);
}
也就是说,默认情况下,任何对象之间使用equals方法进行比较,比较的是两者之间的地址是否相同,和“==”的作用一模一样:
public class Fruit {
private String name;
private Integer weight;
public Fruit(){}
public Fruit(String name, Integer weight){
this.name = name;
this.weight = weight;
}
// setters and getters...
public static void main(String[] args) throws Exception {
Fruit apple1 = new Fruit("apple",13);
Fruit apple2 = new Fruit("apple",13);
// print false
System.out.println(apple1.equals(apple2));
// print false
System.out.println(apple1 == apple2);
}
那么如何才能使用equals方法来比较两个对象之间的内容是否相等呢?这个其实在我们日常使用的String和Integer中已经实现了的。
public static void main(String[] args) throws Exception {
String s1 = new String("zhangsan");
String s2 = new String("zhangsan");
String s3 = new String("lisi");
// print true
System.out.println(s1.equals(s2));
// print false
System.out.println(s1 == s2);
// print false
System.out.println(s1.equals(s3));
Integer t1 = new Integer(10);
Integer t2 = new Integer(10);
Integer t3 = new Integer(1);
// print true
System.out.println(t1.equals(t2));
// print false
System.out.println(t1 == t2);
// print false
System.out.println(t1.equals(t3));
}
为什么String和Integer就可以做到比较的是内容,而不是存放内容的地址呢?我们来看下它们的源码是怎么实现的:
// 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;
}
// Integer源码中equals方法实现
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
如此,我们得出结论,想要通过equals方法来比较两个对象的内容是否相等,是需要重写从Object中继承而来的equals方法的,如果不重写,那么默认就是比较的两个对象的地址。
我们在刚才的Fruit中增加equals的重写,这个使用IDE可以自动生成:
public class Fruit {
private String name;
private Integer weight;
public Fruit(){}
public Fruit(String name, Integer weight){
this.name = name;
this.weight = weight;
}
@Override
public boolean equals(Object o) {
// 被比较对象等同自身则返回true
if (this == o) return true;
// 被比较对象等于null或者两个根本是不同的类型则返回false
if (o == null || getClass() != o.getClass()) return false;
// 将被比较对象强制转成相同的类型
Fruit fruit = (Fruit) o;
// 将对象中的各个属性依次进行比较,都相同的情况下才返回true
return Objects.equals(name, fruit.name) &&
Objects.equals(weight, fruit.weight);
}
// setters and getters...
public static void main(String[] args) throws Exception {
Fruit apple1 = new Fruit("apple",13);
Fruit apple2 = new Fruit("apple",13);
// print true
System.out.println(apple1.equals(apple2));
// print false
System.out.println(apple1 == apple2);
}
重写后的对象使用equals方法就比较的是两个对象的内容了。
PS:
如上重写的equals中,使用了Objects工具类的equals方法,两个比较的属性是否相等需要取决于属性自身是否重写自身的equals方法。如下是相关的源码:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
网友评论