`
denyx123
  • 浏览: 42441 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java中equals和==的区别

阅读更多
在讲 == 和 equals 的区别前我们需要确认一下 Java 中的数据类型。在Java中有基本数据类型和引用数据类型两种。
1、8种基本数据类型:
四种整数类型(byte、short、int、long)
两种浮点数类型(float、double)
一种字符类型(char)
一种布尔类型(boolean)
2、引用数据类型:
除了上面我们说的 8 种基本数据类型外,其他在Java中出现的类型都是引用数据类型。如我们自己写了一个Dog类,而在上面说的8种基本数据类型中不包括Dog类型,所以Dog类型是隶属于引用数据类型的。
==的理解:
要理解 == 我们学要从两个方面来理解:
(1) == 两边为 8 种基本数据类型时:
    当 == 两边为8种基本数据类型时, == 判断的是两边变量中存放的内容是否相等。例:
public class Test {
public static void main(String[] args) {
  int a = 10;
  int b = 10;
  if (a == b) {
   System.out.println("变量a和变量b中的值相等");
  }
}
}
上面的例子的运行结果是在控制台中输出"变量a和变量b中的值相等"  我们可以看出当 == 两边为8种基本数据类型时比较的时内容换句话说就是比较变量的值。
(2) == 两边为引用数据类型时:
     当 == 两边为引用数据类型时,== 判断的是引用数据类型的对象的内存地址是否一样,如果 == 比较的内存地址一样表示 == 两边是同一个对象,否则 == 两边不是同一个对象。例:
public class Test {
public static void main(String[] args) {
            //在8中数据类型中没有String类型所以String是属于引用数据类型的
  String str1 = new String("abc");
            //在这里在创建一个str2对象让他的值和str1一样
  String str2 = new String("abc");
            //我们在用 == 来进行判断会发现 str1 == str2条件不成立我们可以看出这里比较的不是str1和str2的值
  if (str1 == str2) {
   System.out.println("对象str1和对象str2是同一对象,他们的内存地址相同");
  } else {
   System.out.println("对象str1和对象str2不是同一对象,他们的内存地址不相同");
  }
}
}
equals的理解:
equals的理解就容易多了,equals他是一个方法,而方法只有对象才能拥有,所以equals的左边一定是一个对象他的语法格式因该是:对象名.equals(比较的内容)。
例:
public class Test {
public static void main(String[] args) {
  String str1 = new String("abc");
        //equals的括号中可以直接写入要比较的内容
  if (str1.equals("abc")) {
   System.out.println("对象str1的内容和 abc 相同");
  }
String str2 = new String("abc");
        //equals括号中也可以是一个要比较的对象,注意这里比较的是str1对象和str2对象的内容
  if (str1.equals(str2)) {
   System.out.println("对象str1的内容和 str2 的内容相同");
  }
}
}


Java中的equals是十分重要的,和= =要区别开来, = =和 equals
1、声明格式
public boolean equals(Object obj)

其比较规则为:当参数obj引用的对象与当前对象为同一个对象时,就返回true,否则返回false.


比如以下两个对象animal1和animal2,引用不同的对象,因此用==或equals()方法比较的结果为false;而animal1和animal3变量引用同一个DOg对象,因此用= =或者equals()方法比较的结果为true.

Animal animal1=new Dog();
Animal animal2=new Cat();
Animal animal3=animal1;

则animal1==animal2 (FALSE)
animal1.equals(animal2) (false)

animal1==animal3 (true)
animal1.equals(animal3) (true)


而JDK类中有一些类覆盖了oject类的equals()方法,比较规则为:如果两个对象的类型一致,并且内容一致,则返回true,这些类有:
java.io.file,java.util.Date,java.lang.string,包装类(Integer,Double等)

比如
Integer int1=new Integer(1);
Integer int2=new Integer(1);


String str1=new String("hello");
String str2=new String("hello");

int1==int2 输出:false,因为不同对象
int1.equals(int2) 输出:TRUE


str1==str2 (false)
str1.equals(str2) (true)
当然,可以自定义覆盖object类的equals()方法,重新定义比较规则。比如,下面Person类的equals()比较规则为:只要两个对象都是Person类,并且他们的属性name都相同,则比较结果为true,否则返回false

public class Person{
private String name;
public Person(String name)
{
this.name=name;
}
public boolean equals(Object o)
{
if (this==0) return true;
if (!o instanceof Person) return false;
final Person other=(Person)o;
if (this.name().equals(other.name()))
return true;
else
return false;
}

}


注意,在重写equals方法时,要注意满足离散数学上的特性

1、自反性 :对任意引用值X,x.equals(x)的返回值一定为true.
2 对称性: 对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
3 传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true
4 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
5 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false

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

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

对于任何引用类型, o.equals(o) == true成立。
如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立。
如果 o.equals(o1) == true 成立且 o.equals(o2) == true 成立,那么o1.equals(o2) == true 也成立。
如果第一次调用o.equals(o1) == true成立再o和o1没有改变的情况下以后的任何次调用都成立。
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运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。

  我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用nstanceof运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回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要求返回相等的值。

1.如果是基本变量,没有hashcode和equals方法,基本变量的比较方式就只有==;
2.如果是变量,由于在java中所有变量定义都是一个指向实际存储的一个句柄(你可以理解为c++中的指针),在这里==是比较句柄的地址(你可以理解为指针的存储地址),而不是句柄指向的实际内存中的内容,如果要比较实际内存中的内容,那就要用equals方法,但是!!!
如果是你自己定义的一个类,比较自定义类用equals和==是一样的,都是比较句柄地址,因为自定义的类是继承于object,而object中的equals就是用==来实现的,你可以看源码。
那为什么我们用的String等等类型equals是比较实际内容呢,是因为String等常用类已经重写了object中的equals方法,让equals来比较实际内容,你也可以看源码。
3. hashcode
在一般的应用中你不需要了解hashcode的用法,但当你用到hashmap,hashset等集合类时要注意下hashcode。
你想通过一个object的key来拿hashmap的value,hashmap的工作方法是,通过你传入的object的hashcode在内存中找地址,当找到这个地址后再通过equals方法来比较这个地址中的内容是否和你原来放进去的一样,一样就取出value。
所以这里要匹配2部分,hashcode和equals
但假如说你new一个object作为key去拿value是永远得不到结果的,因为每次new一个object,这个object的hashcode是永远不同的,所以我们要重写hashcode,你可以令你的hashcode是object中的一个恒量,这样永远可以通过你的object的hashcode来找到key的地址,然后你要重写你的equals方法,使内存中的内容也相等。。。
==是判断两个变量或实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相同

分享到:
评论

相关推荐

    java中equals和==的区别

    Java 中 equals 和 == 的区别 Java 中的 equals 和 == 是两个不同的概念,很多开发者容易混淆它们。理解这两个概念的区别是非常重要的,因为它们对编程的正确性和性能都有很大的影响。 首先,我们需要了解 Java ...

    java中equals和==的区别.doc

    Java 中 equals 和 == 的区别 Java 中的 equals 和 == 是两个不同的运算符,它们之间的区别是非常重要的。 首先,我们需要了解 Java 中的内存模型。在 Java 中,变量可以分为两种:基本类型(primitive type)和...

    Java 中equals和==的区别.doc

    Java 中equals和==的区别

    java中equals和==的区别.docx

    Java 中 equals 和 == 的区别 Java 中的 equals 和 == 是两个常用的操作符,经常用于比较对象或变量的值。然而,许多开发者不知道它们之间的区别,或者误用它们,导致程序出错。下面我们将详细解释 equals 和 == 的...

    java中equals和==的区别-5页.pdf

    java中equals和==的区别-5页.pdf

    Java基础复习(内附String中equals与==区别的分析)

    本篇复习将重点讨论String类中的`equals()`方法和`==`运算符的区别,这对于理解对象比较和字符串操作至关重要。 首先,`==`运算符在Java中用于比较基本类型(如int、char)的值,而在比较对象时,它实际上是检查两...

    java中equals和==的区别.pdf

    Java中equals和==的区别 Java是一门面向对象的编程语言,它提供了多种运算符和方法来比较对象和变量。在Java中,比较两个对象是否相等时,经常使用到的运算符有"=="和"equals"。虽然两者都可以用于比较,但它们有着...

    java 资料 equals 与== 的区别

    Java 中的equals和==的区别 Java 是一种面向对象的编程语言,其中有两种数据类型:基本数据类型和引用数据类型。基本数据类型包括整数类型、浮点数类型、字符类型、布尔类型等,共有八种;而引用数据类型则包括 ...

    java中equals和==的区别.

    在Java编程语言中,`equals()`方法和`==`运算符是用于比较对象的两种不同方式,它们在处理不同类型的数据时有不同的行为。了解这两者的区别对于编写正确的代码至关重要。 `==`运算符: 1. `==`用于比较基本类型的...

    java中equals和==的比较.pdf

    总结,理解`equals()`和`==`的区别是Java编程中的关键知识点。在使用时,根据需要比较的是对象的引用还是内容,选择适当的操作符或方法。对于String类,由于其特殊的常量池机制,`==` 和 `equals()` 的行为可能与...

    java中equals和==的区别文.pdf

    在Java编程语言中,`equals()` 和 `==` 是两种常用的比较操作,它们在处理不同数据类型时有着明显的区别。本文将深入探讨这两个操作符在Java中的行为,特别是在处理值类型和引用类型时的不同。 首先,值类型如整型...

    java中equals和==的区别的剖析.pdf

    在Java编程语言中,`equals()` 和 `==` 是两种常用的比较操作,它们在处理不同数据类型时有着明显的区别。`==` 操作符主要用于基本数据类型(如 int, double 等)以及引用类型的变量,它比较的是两个变量所指向的...

Global site tag (gtag.js) - Google Analytics