`
singleFcb
  • 浏览: 5848 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java中==和equals详解

阅读更多

引言:以下文字均来源于http://blog.sina.com.cn/s/blog_532637060100gkfc.html,感谢作者的辛勤付出!

 

中软国际电子政务部Jeff Chi总结,转载请说明出处。

概述:

       A.==可用于基本类型和引用类型:当用于基本类型时候,是比较值是否相同;当用于引用类型的时候,是比较对象是否相同。

       B.对于String a = “a”; Integer b = 1;这种类型的特有对象创建方式,==的时候值是相同的。

       C.基本类型没有equals方法,equals只比较值(对象中的内容)是否相同(相同返回true)。

       D.一个类如果没有定义equals方法,它将默认继承Object中的equals方法,返回值与==方法相同。

详述:

       ①==和equals的实质。

       在JAVA中利用"=="比较变量时,系统使用变量在"栈"中所存的值作为比较的依据。

       基本数据类型在"栈"中存的是其内容值,而对象类型在"栈"中存的是地址,这些地址指向"堆"中的对象。

       java.lang包中的Object类有public boolean equals(Object obj)方法,它比较两个对象是否相等。

       其它对象的equals方法仅当被比较的两个引用指向的对象内容相同时,对象的equals()方法返回true。

       总之,"=="和"!="比较的是地址.也可认为"=="和"!="比较的是对象句柄;而equals()比较的是对象内容.或者说,,"=="和"!="比较的是"栈"中的内容,而equals()比较的是"堆"中的内容.

       ②==操作符。专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相当,只能用==操作符。

      Java的基本数据类型为(char,byte,short,int,long,float,double,boolean)。

      如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(对内存),变量本身也占用一块内存,例如Object obj = new Object()变量obj是一个内存,new Object()是一个内存,此时,变量所对应的内存中存储的数据就是对象占用的那块内存的首地址。对于指向对象内存的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。

       ③构造器形成的差别。对于String和Integer来说,由于他们特有的创建对象的方式。使用构造器和不使用构造器得到一个对象,==方法比较所产生的结果是不同的。 String a = “abc”; String b = "abc"; 此时a==b得到结果为true。String a = new String("abc"); String b = new String("abc");此时a==b得到的结果为false。Integer a = 1; Integer b = 1;此时a==b的结果是true。Integer a = new Integer(1); Integer b = new Integer(1);此时a==b得到的结果为false。

       通过这一点其实我们也能够更加容易理解==对内存的实际操作,实际执行的是近似于基本类型比较。

       String对象和字符串连接池:

       引号内包含文本是String类特有创建对象的方式.但是"=="返回的结果是true,为什么呢?因为在JVM内,存在字符串池,其中保存着很多 String对象,并且可以被共享使用,因此它提高了效率.字符串池由String类维护,我们可以调用intern()方法来访问字符串池。当运用引号内包含文本创建对象时,所创建的对象是加入到字符串池里面的.如果要创建下一个字符串对象,JVM首先会到字符串池中寻找,是否存在对应的字符串对象,如果存在,则返回一个己存在对象的对象的引用给要创建的对象引用.如果不存在,才会创建一个新的对象,并返回一个新对象的对象引用给要创建的对象引用.以上这段话理解起来可能比较拗口.用代码理解就是str2和str1是两个对象引用,并指向了同一个对象.所以'=='返回的是true.

       只有引号内包含文本创建对象才会将创建的对象放入到字符串池。String str = new String("abc")这种方法创建的字符串对象是不放入到字符串池的。所以,引号内包含文本创建对象的性能要比后来那种方法创建字符串对象的性能要好。

String str1 = "abc";

String str2 = "abc";

String str3 = str1+str2;   //这种创建方式是不放入字符串池的.

String str4 = str1+"cd";   //这种创建方式是不放入字符串池的.

String str5 = "ab"+str2;   //这种创建方式是不放入字符串池的.

String str6 = "ab"+"cd"; //这种创建方式是放入字符串池的.这种情况实际上是创建了1个对象,abcd"1个对象

String str7 = "abcd";

System.out.println(str1==str2); //返回ture

System.out.println(str6==str7); //返回ture

       另一个问题:

我们首先来看一段 Java代码:

String str=new String("abc");  

紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。

我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:

Java代码

public String(String original) {    
//other code ...    
}  
大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。

使用new创建对象是调用Class类的newInstance方法,利用反射机制创建对象。

       ④equals方法。用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:

String a=new String("foo");

String b=new String("foo");

两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,他们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b即返回false,而这两个对象中内容是相同的,所以,表达式a.equals(b)将返回true。

       在实际开发中,我们经常要比较传递进行来的字符串内容是否相等,许多人稍不注意就使用==进行比较了,这是错误的,有大量这样的错误。记住,字符串的比较基本都是使用equals方法。

       ⑤如果一个类没有定义equals方法。它将继承Object类的equals方法,Object类的equals方法的实现代码如下:

boolean equals(Object o){

return this==o;

}

这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操作符,也是比较两个变量指向的对象是否是同一个对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可以认为两个对象的内容是相同的。

      

     示例代码:

  1. public class Test {  
  2.     public static void main(String[] args) {  
  3.         Integer p = 1;   
  4.          Integer q = 1;
  5.         Integer i = new Integer(1);   
  6.          Integer j = new Integer(1);
  7.         if(p == q){  
  8.              System.out.println("integer:p == q"); //实际结果
  9.          }else{  
  10.              System.out.println("integer:p != q");  
  11.          }
  12.         if(p.equals(q)){
  13.              System.out.println("integer:p.equals(q)"); //实际结果 
  14.          }else{  
  15.              System.out.println("integer:p.equals(q)");
  16.          }
  17.         if(i == j){  
  18.              System.out.println("int:i == j");  
  19.          }else{  
  20.              System.out.println("int:i != j"); //实际结果
  21.          }  
  22.         if(i.equals(j)){  
  23.              System.out.println("integer:i.equals(j)");//实际结果
  24.          }else{
  25.              System.out.println("integer:!i.equals(j)");
  26.          }  
  27.          String a = "abc";  
  28.          String b = "abc";  
  29.          String c = new String("abc");  
  30.          String d = new String("abc");  
  31.         if(a == b){  
  32.              System.out.println("abc对象相等"); //实际结果
  33.          }else{  
  34.              System.out.println("abc对象不相等");  
  35.          }  
  36.         if(a.equals(b)){  
  37.              System.out.println("ab相等"); //实际结果
  38.          }else{  
  39.              System.out.println("ab不相等");  
  40.          }  
  41.         if(c.equals(d)){  
  42.              System.out.println("cd相等"); //实际结果
  43.          }else{  
  44.              System.out.println("cd不相等");  
  45.          }  
  46.         if(c == d){  
  47.              System.out.println("cd对象相等");  
  48.          }else{  
  49.              System.out.println("cd对象不相等"); //实际结果
  50.          }  
  51.      }  
  52.   
  53. }

中软国际电子政务部Jeff Chi总结,转载请说明出处。

----------------------------------------------------------------------------------

深入探讨equals:

===================
转自硅谷动力

equals方法的重要性毋须多言,只要你想比较两个对象是不是同一对象,你就应该实现equals方法,让对象用你认为相等的条件来进行比较.

下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为这些规范在事实中并不是真正能保证得到实现.

1.对于任何引用类型, o.equals(o) == true成立.

2.如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立.

3.如果 o.equals(o1) == true 成立且 o.equals(o2) == true 成立,那么

o1.equals(o2) == true 也成立.

4.如果第一次调用o.equals(o1) == true成立,在o和o1没有改变的情况下以后的任何次调用都成立.

5.o.equals(null) == true 任何时间都不成立.

以上几条规则并不是最完整的表述,详细的请参见API文档.对于Object类,它提供了一个最最严密的实现,那就是只有是同一对象时,equals方法才返回true,也就是人们常说的引用比较而不是值比较.这个实现严密得已经没有什么实际的意义, 所以在具体子类(相对于Object来说)中,如果我们要进行对象的值比较,就必须实现自己的equals方法.先来看一下以下这段程序:

public boolean equals(Object obj)

{

if (obj == null) return false;

if (!(obj instanceof FieldPosition))

return false;

FieldPosition other = (FieldPosition) obj;

if (attribute == null) {

if (other.attribute != null) {

return false;

}

}

else if (!attribute.equals(other.attribute)) {

return false;

}

return (beginIndex == other.beginIndex

& endIndex == other.endIndex

&& field == other.field);

}

这是JDK中java.text.FieldPosition的标准实现,似乎没有什么可说的. 我信相大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDK的API实现啊. 还是让我们以事实来说话吧:

package debug

;import java.text.*;

public class Test {

public static void main(String[] args) {

FieldPosition fp = new FieldPosition(10);

FieldPosition fp1 = new MyTest(10);

System.out.println(fp.equals(fp1));

System.out.println(fp1.equals(fp));

}

}

class MyTest extends FieldPosition{

int x = 10;

public MyTest(int x){

super(x);

this.x = x;

}

public boolean equals(Object o){

if(o==null) return false;

if(!(o instanceof MyTest )) return false;

return ((MyTest)o).x == this.x;

}

}

运行一下看看会打印出什么:

System.out.println(fp.equals(fp1));打印true

System.out.println(fp1.equals(fp));打印flase

两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的BUG)?我相信有太多的程序员(除了那些根本不知道实现 equals方法的程序员外)在实现equals方法时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。

太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用instanceof运算符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false。

但事实上,"子类是父类的一个实例",所以如果子类 o instanceof 父类,始终返回true,这时肯定不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成父类而抛出异常,另一种是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能会出现太多的情况。

那么,是不是就不能用instanceof运算符来进行优化?答案是否定的,JDK中仍然有很多实现是正确的,如果一个class是final的,明知它不可能有子类,为什么不用 instanceof来优化呢?为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加加上了加上了这样的注释:

if (this == obj) // quick check

return true;

if (!(obj instanceof XXXXClass)) // (1) same object?

return false;

可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我......)那么对于非final类,如何进行类型的quick check呢?

if(obj.getClass() != XXXClass.class) return false;

用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类没有重新实现equals方法,那么子类在比较的时候,obj.getClass() 肯定不等于XXXCalss.class, 也就是子类的equals将无效,所以

if(obj.getClass() != this.getClass()) return false;

才是正确的比较。另外一个quick check是if(this==obj) return true;

是否equals方法比较的两个对象一定是要同一类型?上面我用了"通常",这也是绝大多数程序员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不是同一数学值。(事实上JDK的API中并没有这样做,所以我才说是不恰当的例子)在完成quick check以后,我们就要真正实现你认为的“相等”。对于如果实现对象相等,没有太高的要求,比如你自己实现的“人”类,你可以认为只要name相同即认为它们是相等的,其它的sex, ago都可以 不考虑。这是不完全实现,但是如果是完全实现,即要求所有的属性都是相同的,那么如何实现equals方法?

class Human{

private String name;

private int ago;

private String sex;

....................

public boolean equals(Object obj){

quick check.......

Human other = (Human)ojb;

return this.name.equals(other.name) && this.ago == ohter.ago && this.sex.equals(other.sex);

}

}

这是一个完全实现,但是,有时equals实现是在父类中实现,而要求被子类继承后equals能正确的工

作,这时你并不事实知道子类到底扩展了哪些属性,所以用上面的方法无法使equals得到完全实现。

一个好的方法是利用反射来对equals进行完全实现:

public boolean equals(Object obj){

quick check.......

Class c = this.getClass();

Filed[] fds = c.getDeclaredFields();

for(Filed f:fds){

if(!f.get(this).equals(f.get(obj)))

return false;

}

return true;

}

为了说明的方便,上明的实现省略了异常,这样的实现放在父类中,可以保证你的子类的equals可以按你的愿望正确地工作。关于equals方法的最后一点是:如果你要是自己重写(正确说应该是履盖)了equals方法,那同时就一定要重写hashCode().这是规范,否则.............

我们还是看一下这个例子:

public final class PhoneNumber {

private final int areaCode;

private final int exchange;

private final int extension;

public PhoneNumber(int areaCode, int exchange, int extension) {

rangeCheck(areaCode, 999, "area code");

rangeCheck(exchange, 99999999, "exchange");

rangeCheck(extension, 9999, "extension");

this.areaCode = areaCode;

this.exchange = exchange;

this.extension = extension;

}

private static void rangeCheck(int arg, int max, String name) {

if(arg < 0  ||  arg > max)

throw new IllegalArgumentException(name + ": " + arg);

}

public boolean equals(Object o) {

if(o == this)

return true;

if(!(o instanceof PhoneNumber))

return false;

PhoneNumber pn = (PhoneNumber)o;

return pn.extension == extension && pn.exchange == exchange && pn.areaCode == areaCode;

}

}

注意这个类是final的,所以这个equals实现没有什么问题。我们来测试一下:

public static void main(String[] args) {

Map hm = new HashMap();

PhoneNumber pn = new PhoneNumber(123, 38942, 230);

hm.put(pn, "I love you");

PhoneNumber pn1 = new PhoneNumber(123, 38942, 230);

System.out.println(pn);

System.out.println("pn.equals(pn1) is " + pn.equals(pn1));

System.out.println(hm.get(pn1));

System.out.println(hm.get(pn));

}

既然pn.equals(pn1),那么我put(pn,"I love you")后,get(pn1)为什么是null呢?

答案是因为它们的hashCode不一样,而hashMap就是以hashCode为主键的。所以规范要求,如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。

分享到:
评论

相关推荐

    Java中的==和equals区别

    ### Java中的`==`与`equals`方法的区别详解 在Java编程中,比较对象的相等性是一个常见的需求,但很多初学者对于`==`运算符与`equals`方法的区别容易混淆。本文将深入探讨两者之间的差异,以及它们在不同场景下的...

    java基础之 “==”与“equals”区别详解

    在java基础开发中,“==”操作符和equals()方法是非常重要的比较操作符。正确地使用“==”操作符和equals()方法可以避免许多错误,提高程序的稳定性和可靠性。 9. java基础之“==”与equals()的应用场景: java基础...

    Java中==运算符与equals方法的区别及intern方法详解

    "Java中==运算符与equals方法的区别及intern方法详解" Java中==运算符与equals方法的区别是Java程序设计语言中的一個非常重要的概念,它們兩者都是用來比较字符串是否相等,但是它们的比较方式完全不同。 ==运算符...

    Java中==符号与equals()的使用详解(测试两个变量是否相等)

    Java中==符号与equals()的使用详解是Java程序中的一个重要概念,它们都是用来测试两个变量是否相等的,但是它们的使用方法和应用场景是不同的。下面我们将详细介绍Java中==符号与equals()的使用详解。 一、使用==...

    Java常见笔试、面试题目深度剖析 相等性(==及equals方法)详解

    本篇文章将深入剖析“==”运算符和equals()方法的区别与联系,帮助你在Java的笔试和面试中更好地应对相关问题。 首先,“==”运算符在Java中用于比较基本类型变量的值是否相等,例如int、char或boolean。对于引用...

    详解java==运算符和equals()方法的区别

    Java编程语言中有两种主要的方法来比较对象的平等性:`==`运算符和`equals()`方法。理解这两者的区别对于编写正确和可靠的代码至关重要。 1. `==`运算符: `==`运算符主要用于比较基本类型(如int、char等)的值,...

    java中hashcode()和equals()的详解

    在Java编程语言中,`hashCode()`...以上就是关于Java中`hashCode()`和`equals()`的详解。这两个方法在Java编程中起着至关重要的作用,尤其是在处理集合类和数据结构时。了解并正确使用它们能够确保程序的正确性和效率。

    java中hashcode()和equals()和==的详解

    有许多人学了很长时间的Java,但一直不明白hashCode方法的作用以及equals()和==的区别,我来解释一下吧。首先,想要明白hashCode的作用,你必须要先知道Java中的集合。总的来说,Java中的集合(Collection)有两类,...

    Java中的== 和equals()方法详解与实例

    在Java编程语言中,`==` 和 `equals()` 方法是用来比较对象或基本数据类型的值的。两者虽然在外观上相似,但它们的行为却大不相同,尤其在处理引用数据类型时。 1. `==` 操作符: - **基本数据类型**:`==` 用于...

    java中hashcode()和equals()方法详解

    ### Java中`hashCode()`与`equals()`方法详解 #### 前言 在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将...

    详解Java中“==”与equals()的区别

    在Java编程语言中,"=="运算符和equals()方法是用来比较对象之间相等性的两种不同方式,它们在处理基本类型和引用类型时有着显著的区别。 首先,"=="运算符主要用于比较基本类型的值,如整型(int)、浮点型(float)、...

    详解 Java 中 equals 和 == 的区别

    在 Java 编程语言中,`equals()` 和 `==` 是两种常见的用于比较对象的运算符,但在处理字符串时,它们的行为有所不同。本文将详细解释它们的区别,并提供示例进行说明。 1. `equals()` 方法 `equals()` 方法是 ...

    java中hashcode和equals的详解.pdf

    Java 中的 hashCode 和 equals 方法详解 本文详细介绍了 Java 中的 hashCode 和 equals 方法,探讨了这两个方法的作用、实现机制和使用场景。通过对 hashCode 和 equals 方法的深入分析,我们可以更好地理解 Java ...

    java中hashcode()和equals()的详解.docx

    ### Java中`hashCode()`与`equals()`方法详解 #### 前言 在Java编程语言中,`hashCode()`与`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将...

    java equals和==的区别详解

    在Java编程语言中,`equals()` 和 `==` 运算符在比较对象时有着显著的不同。理解这两者的区别对于编写高效、可靠的代码至关重要。以下是它们的详细解释: 1. **`==` 运算符**: `==` 在Java中主要用于比较基本类型...

    JAVA中equals和的区别.doc

    ### JAVA中equals与==的区别详解 #### 一、概述 在Java编程语言中,`equals`方法和`==`操作符都是用来比较对象之间是否相等的重要工具。然而,它们在实现这一目标的方式上有着本质的区别。理解这些差异对于正确地...

    Java中的equals和hashCode方法详解1

    在Java编程语言中,`equals()`和`hashCode()`方法是对象的基本组成部分,它们主要用于对象的比较和存储。这两个方法在`java.lang.Object`类中定义,因此所有的Java类都默认继承了这两个方法。然而,根据具体的应用...

    set接口经常用的hashCode和equals方法详解

    ### set接口中hashCode和equals方法详解 #### 一、引言 在Java编程语言中,`Set`接口作为集合框架的重要组成部分,在实现无重复元素的数据结构方面扮演着关键角色。为了确保元素的唯一性,`Set`接口依赖于对象的`...

Global site tag (gtag.js) - Google Analytics