Java中equals和==的区别,是十分经典的问题。本人有次面试时,就被问了这个问题。在此深入研究下。
第一原则:
equals和==都是比较变量(varivable)才可以比较。类、方法、接口等非变量是不能比较的。
以下代码,编译器是不能通过的。
eg1:
class A{
void fun(){
}
}
class B{
void fun(){
}
}
interface C{
}
interface D{
}
public class test2 {
public static void main(String[] args){
A a = new A();
B b = new B();
System. out.println(A.equals( B));
System. out.println(C.equals( D));
}
}
第二原则:
基础数据类型是可以直接用==比较的。
在第一原则的情况下,Java的变量分为基本数据类型和引用型,两者比较是不同的。
首先熟悉下Java的变量存储机制。
1、基本数据类型(
包括数值型,字符型和布尔型
)在创建时,
在栈上给其划分一块内存,将数值直接存储在栈(stack)上。
2、引用数据类型在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。
在eg1中,A a = new A(),简单的一句话,其实执行了如下的步骤:
step1:在stack区分配了一块区域,存放a;
step2:在heap区新建A的一个对象;
step3:将A对象在堆内存中的地址,赋值给栈中的a,通过句柄a可以找到堆中对象的具体信息。
当我们使用==比较时,比较的是在stack内容中存储的内容,所以基础类型是可以直接用==比较的,那么引用型的呢?
第三原则:
引用型变量的比较,
要具体情况具体分析。
结合具体情况是指结合什么东西的具体情况呢,当然是指JDK文档中的不同对象的equals方法了。
情况一:
eg2:
class A{
void fun(){
}
}
public class test2 {
public static void main(String[] args){
A a1 = new A();
A a2 = new A();
System. out.println(a1.equals(a2));
}
}
打印结果是false,这是怎么回事?当我们新建A对象时,继承的是java.lang.Object,并且没有重写equals方法。而Object类中的equals方法是用来比较“地址”的,所以等于false。
情况二:
eg3:
public class test2 {
public static void main(String[] args){
String str = new String( "helloworld");
String str2 = new String( "helloworld");
System. out.println(str.equals(str2));
}
}
打印结果是true,这是怎么回事?在JDK中String的equals方法已经被重写,比较就是内容,所以等于true。
情况三:
eg4:
public class test2 {
public static void main(String[] args){
StringBuffer str = new StringBuffer( "helloworld");
StringBuffer str2 = new StringBuffer( "helloworld");
System. out.println(str.equals(str2));
}
}
打印结果是false,这是因为StringBuffer并没有重写equals方法,还是比较的地址。
读到这里,大家也许明白了,equals千万不能死记硬背,要根据JDK的文档来查看相应对象的比较方法才行。而我们自己定义的类,如果想用equals的比较的话,就需要重写了。
eg5:
class A{
private String age;
public String getAge() {
return age;
}
public void setAge(String age) {
this. age = age;
}
public boolean equals(A a) {
return this. age.equals(a. age);
}
}
public class test2 {
public static void main(String[] args){
A a1 = new A();
a1.setAge( "12");
A a2 = new A();
a2.setAge( "12");
System. out.println(a1.equals(a2));
}
}
打印结果是true!
PS:
还有一种很特殊的情况,既可以使用equals又可以使用==,这就涉及到Java的装箱拆箱了(详见笔者的另一篇文章:点击打开链接)。
分析:
在C语言中,数组的定义方法有两种:
char a[] = "leirenbaobao"
char *p = "leirenbaobao"
第一种是用数组定义,第二种是用指针指向字符串常量区的一个字符串,而字符串常量区有个特性:当我定义了char *p = "leirenbaobao"时,再定义个char *q = "leirenbaobao",这时候字符常量区不再新建另一个"leirenbaobao",而是将两个指针同时指向一个第一个"leirenbaobao",想必这样也是为了程序执行的效率吧。
而在java中,也是这尿性,java的字符串常量区,有时也被称为字符串常量池。
eg6:
public class test2 {
public static void main(String[] args){
String str = "leirenbaobao";
String str2 = "leirenbaobao";
System. out.println(str.equals(str2));
System. out.println(str==str2);
}
}
结果返回两个true。
Ctrl+Z presents!