说明
Java程序中测试两个变量是否相等有两种方式:一种是利用==运算符;一种是利用equals函数。
当使用==来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型,则只要两个变量的值相等,就将返回true.
但对于两个引用变量,它们必须指向同一个对象时,==判断才会返回true。==不可用于比较类型上没有父子关系的两个对象。下面程序示范了使用==来判断两种类型变量是否相等的结果。
int i = 65;
float f = 65.0f;
//将输出true
System.out.println("65和65.0f是否相等?"+ (i==f));
char c = 'A';
//将输出true
System.out.println("65和A是否相等?"+ (i==c));
String str1 = new String("hello!");
String str2 = new String("hello!");
; //将输出false
System.out.println("str1和str2是否相等?"+ (str1==str2));
//String类与Math类没有父子关系,将编译出错
//System.out.println("hello"== new Math());
输出结果:
65和65.0f是否相等?true
65和A是否相等?true
str1和str2是否相等?false
从结果可以看到,65和65.0f和’A’相等。但对于str1 和 str2,它们都是引用型变量,分别指向两个不同的String对象,因此str1和str2两个变量不相等。
那如果我们改成如下这样:
String str1 = "hello!";
String str2 = "hello!";
System.out.println("str1和str2是否相等?"+ (str1==str2));
输出结果:
str1和str2是否相等?true
这里的输出显示,两个字符串文字保存为一个对象。就是说,上面的代码只创建了一个String对象。
那么”hello”和new String(“hello”)具体区别在哪呢?
提到这里,就正好来解答一个面试问题:
String s = new String(“xyz”)创建了几个对象?
当Java程序直接使用形如”hello”的字符串直接量时,JVM将会使用常量池来管理这些字符串。
String str1 = “hello”;这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为”hello”的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。由于str1 和 str2的文本字符串相同,因此只创建了一个字符串对象”hello”。
Java中所有的字符串文字[字符串常量]都是一个String的对象。
String str1 = new String(“hello”);这行代码被执行的时候,JVM会先在常量池中创建一个String对象“hello”,然后再通过String类的构造器来创建一个新的String对象,新创建的String对象将会保存在堆内存中。因此这行代码实际产生了两个对象。
那么 String str = “a”+”b”+”c”创建了几个对象?
答案是一个;
编译器会对”a”+”b”+”c”这样的代码在编译期间优化成一句,即字符串合并成一个字符串相当于(String str= “abc”);也就是说,在代码编译后的运行期间只会生成一个临时对象。
JVM常量池保证相同的字符串常量只能有一个。如果对于两个指向不同对象的引用变量,只需要判断它们的”值”是否相等来决定两个变量是否相等,此时就可以利用String对象的equals方法来进行判断。
比如,上面代码中可改写比较str1 和 str2的方法:
String str1 = new String("hello!");
String str2 = new String("hello!");
System.out.println(str1.equals(str2));
输出结果:
true
equals方法是Object类提供的一个实例方法,而Object类又是所有类的父类,因此所有的类里面都有equals函数,所有引用变量都可调用该方法来判断是否与其他变量相等。
String类的equals方法判断两个字符串相等的标准是:只要两个字符串所包含的字符串序列相同,通过equals方法将返回true,否则返回false。
总的来说,equals的作用就是比较两个对象的内容是否相等,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。
通常情况下,两个对象的内容相等需要符合两个条件:
对象的类型相同(可以使用instanceof运算符进行比较)
两个对象的成员变量的值完全相同
先看下面使用equals比较两个对象的代码:
class Person{
String name;
int age;
}
public class Test {
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.equals(p2));
}
输出结果:
false
使用equals判断两个对象的标准实际上和==运算符没有区别,同样要求两个引用变量指向同一个对象才会返回true(String已经重写了Object的equals方法,因此上面代码才会返回true),因此这个Object提供的equals方法没有太大的作用,如果希望采用自定义的相等标准,则可重写equals方法来实现。
根据上面列出的两点要求,下面程序示范了重写equals方法:
class Person{
String name;
int age;
public boolean equals(Object obj){
//如果两个变量指向同一对象,this指调用equals方法的那个变量
if(this == obj){
return true;
}
//判断obj类型是否和this类型相同,this是Person类型
boolean b = obj instanceof Person;
//如果相同
if(b){
//向下转型
Person p = (Person)obj;
//判断两个变量的成员变量是否相同
if(this.age == p.age&&this.name.equals(p.name)){
return true;
}
else{
return false;
}
}
else{
return false;
}
}
public class Test {
public static void main(String[] args){
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 10;
Person p3 = new Person();
p3.name = "zhangsan";
p3.age = 10;
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
}
}
输出结果:
false
true
代码中之所以用this.name.equals(p.name)
而不用==运算符来比较两者是否相等,是因为name是String类型引用变量,String已经重写了equals方法,可以拿来直接比较两者的内容是否相同。
==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同。
如果一个变量指向的数据是对象类型的,那么这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量占用一块内存(栈内存),例如Object obj = new Object();变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址,==操作符就是用来比较两个变量是否指向堆内存中的同一块地址,即比较这两个变量所对应的内存(栈内存)中的数值是否相等。
equals方法主要用于比较两个独立对象的内容是否相同,首先是否类型相同,其次是否成员变量相同。