在Java中,我们使用==和equals的目的是为了比较两个数据是否相等,然而,对于不同的数据类型,“相等”有不同的含义。对于基本数据类型,我们所期待的相等是指两个基本类型的值相等;对于引用类型,我们所期待的相等是指这两个引用是否引用同一个对象,或者引用对象的值知否相等。下面我们针对这个问题展开。
Java的数据类型
1.基本数据类型
Java中一共有8种基本数据类型:
整型:byte(1byte)、short(2byte)、int(3byte)、long(byte)
浮点型:float(4byte),double(8byte)
字符:char(2 byte)
布尔型:boolean(没有给出精确的定义)
我们在声明一个基本数据类型变量时,这个变量直接指向基本数据类型的原始值。
每一种基本数据类型都有各自的封装类,如Integer等,封装类为各自基本数据类型提供了一些操作方法,可以直接使用。
2.引用数据类型
类(class)
接口(interface)
数组(array)
我们声明一个引用数据类型的变量时,这个变量存储指向对象的地址单元,并不存储对象的原始值。
==的说明
==是Java的一个关系运算符,它的作用是判断两者是否相等,具体的作用方式是直接判断值是否相等。==既可以判断基本数据类型,又可以判断引用数据类型。对于基本数据类型来说,由于基本数据类型变量存储的都是原始值,==在判断变量是否相等时也是直接比较变量的值,所以如果基本变量的原始值相同,那么这两个变量使用==判断就输出true,例如:
public class Test{
public static void main(String[] args){
int a = 2;
int b = 2;
int c = 3;
System.out.println("a==b? " + (a==b));
System.out.println("a==c? " + (a==c));
}
}
运行结果为:
a == b? true
a == c? false
然而,如果将基本数据类型封装成各自的封装类,情况又不相同,此处不再细说。
对于引用数据类型,也可以使用==来判断,由于引用数据类型变量存储的是引用对象的地址,所以使用==来比较两个引用数据类型变量,实际上是在比较这两个引用变量是否引用了同一个对象,例如:
public class Test{
public static void main(String[] args){
Object a = new Object();
Object b = new Object();
System.out.println("a==b? " + (a==b));
}
}
运行结果为:
a == b? false
因为a和b没有指向相同的引用,所以a!=b,如果改变b的引用,结果就会有所不同:
public class Test{
public static void main(String[] args){
Object a = new Object();
Object b = a;
System.out.println("a==b? " + (a==b));
}
}
此时输出结果为:
a == b? true
由此可以得知,当两个引用变量指向相同的对象时,==的判断才会为true。
equals()的说明
equals()只能判断引用数据类型是否相等,因为所有的引用类型都继承自object类,而object类中定义了equals()方法,所以每一个类都继承了equals()方法。根据不同类对equals方法重载的不同,不同类的equals()方法的使用原理有所不同。在object类中,equals()方法定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看出,equals()方法实际上还是使用了==来判断,判断的还是两个引用类型变量是否引用同一对象。但是有些object子类例如String、Date等,对equals方法进行了重载,这时候就不仅仅是简单地比较是否引用同一个对象,例如String类中的equals方法如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (!COMPACT_STRINGS || this.coder == aString.coder) {
return StringLatin1.equals(value, aString.value);
}
}
return false;
}
String类对equals()方法重载后,equals()方法就不仅仅是简单地判断两个字符串是否引用同一个字符串对象,而是比较引用的两个字符串的值是否相等。
String的特别说明
我们先来看一段程序
public static void main(String args[]){
String a1 = "a";
String a2 = "a";
System.out.println(a1.equals(a2));
System.out.println(a1==a2);
}
运行结果为:
true
true
String是引用数据类型,它重载了equals()方法,所以比较的是a1和a2指向的字符串的值,equals()方法返回true没有问题。令人费解的是,为什么使用==也返回true呢?原因在于,String常量在Java中存储在字符串常量池中,当我们使用String str = "hello";创建字符串时,虚拟机首先会在字符串常量池中寻找是否已经存在“hello”常量,若存在则将此常量的引用直接返回给变量,否则创建“hello”常量,并返回该常量的引用。如果我们使用String str = new String("hello");创建字符串,虚拟机则会在非字符串常量池中创建字符串对象,并不会把“hello”引入到字符串常量池中,并将该字符串的引用返回给字符串变量。下面的程序说明了第二种创建字符串的方法:
public static void main(String args[]){
String a1 = "a";
String a2 = new String("a");
System.out.println(a1.equals(a2));
System.out.println(a1==a2);
}
运行结果为:
true
false
总结
==:针对基本数据类型,直接比较原始值,原始值相等,返回true;针对引用数据类型,比较变量是否引用同一个对象,引用相同的对象返回true
equals():只能用于引用数据类型的比较,若未重载该方法,比较原理和==一样;若重载该方法,比较原理会有所不同
String要特别注意,建议使用equals()方法比较字符串