`

在Java中实现对象比较

    博客分类:
  • java
阅读更多

在Java中实现对象比较

 

一、跟对象比较的几个相关的概念

  为了更加审核的理解对象比较的含义以及多个比较方法之间的差异,笔者认为读者首先需要了解几个相关的概念,或者说几对关系。

  一、是类与对象的关系

类是具体的抽象,而对象是类的具体实现。这可能听起来还是有点模糊。做一个形象的比喻,类就好像是用来制作塑料盒子的模具,只要将PVC料注入到这个模具中就可以生产对应形状的盒子。而对象就好像是生产出来的盒子。虽然模具同一个,但是生产出来的盒子彼此之间仍然是不同的。一方面先天性就是不同的。因为根据相对论可以说明世界上没有两个相同的东西。其次后天性的影响,也会导致其不同。如生产出来后,在两个盒子上分别贴上不同的条码,他们两个就代表不同的盒子了。了解这个类与对象之间的关系,对于了解对象之间进行比较,会有很大的帮助。

 

二、是需要知道类与对象在内存中的实际存储情况。

当程序员定义一个类时(不含有静态成员或者变量),一般不会在内存中给其分配一个存储结构。而只有定义对象时,才会在内存中分配存储结构。当利用同一个类定义不同的对象时,系统会在内存中为不同的对象创建不同的存储结构。也就是说,会对应不同的内存地址。虽然同一个类中定义出来的对象,其内容可能相同(成员变量、成员方法等等都相同),但是其内存中的地址仍然是不同的。

 

三、是需要注意对象的复制问题:

如果要创建几个内容相同的对象,即复制相同内容的对象,现在主要有两种方法。

一是通过成员变量赋值来完成。如在根据同一个类创建对象时,分别给与他们相同的初始化值。那么这两个对象的内容就是相同的。(地址不同,内容相同)

二是通过地址赋值来完成。即将第一个对象在内存中的地址赋值给第二个对象。此时两个对象名字虽然不同,但是他们却指向内存中的同一块区域。此时就好像一个人有两个名字,其实是同一个人。所以这两个对象内容也就相同了。(地址内容都相同,别名)

 

  二、利用==运算符与equals方法来比较对象

  在Java语言中,主要可以利用==运算符(两个等号)和equal函数来对对象进行比较。不过这两个符号其实现的机制不同。或者说,对于同样的两个对象,如果利用他们来进行比较的话,往往会有不同的结果。如String是Java自定义的对象,其主要用来存储字符串数据。现在笔者利用如下语句定义了三个String对象。

  String str1=new String(“welcome”); //创建一个对象,给利用单词welcome初始化

  String str2=new String(“welcome”); //创建一个对象,给利用单词welcome初始化

  String str3=str1; //创建一个对象,并利用对象str1的地址赋值

  以上三个对象,显而易见,其内容都是相同的。但是利用这个两种方式来对他们进行比较的时候,往往会有不同的结果。

如利用==(两个等号)比较符号来进行比较,str1==str2,最后返回的结构是false,也就是他们是不相同的对象。

可是如果比较str1==str3对象,则最后返回的结果却是true。但是利用equal函数来比较,则返回的结果是相同的,ture。为什么对象的内容相同,它们返回的结果却是不同的呢?

 

  要回答这个问题,就需要大家先回顾一下笔者上面谈到的几对关系。首先,对象str1与对象str2的关系,就好像是同一个模具出来的两个盒子,他们从外观看起来虽然相同,但是通过放大镜或者其他精密仪器仍然可以看到,两个盒子是不同的东西。

这两个对象虽然内容相同,但是其在内存中分配的地址不同。也就是说,是同一个模具出来的外观看起来相同的不同的盒子

而对象str1与对象str3就好像是一个人有两个名字。虽然名字不同,但是实际上是同一个人。这主要是因为他们的身份证号码相同。其实这个身份证号码就好像是内存中发配的地址,而对象名字就好象是人的名字。一个人可以有好几个名字(一个对象有好几个名字),但是其身份证号码只有一个(内存分配地址只有一个)。在上面的语句中,通过str3=str1,其实现的功能,并不是将对象str1的值赋值给对象str3。而是将对象str1在内存中的地址赋值给了对象str1(就好像是将一个人的身份证号码复制给了另外一个人)。所以从本质上说,str3并不是一个新建立的对象。因为系统并没有在内存中为其分配一个新的存储区域(即并没有创造一个新的人),而只是好像给对象另外取了一个别名。

 

  所以说,在对象比较的时候,需要搞清楚一个问题。即现在要比较的是他们的内容还是在内存中指向的地址。

   一般来说,内容相同不一定他们在内存中指向的地址也是相同的。而不同的对象在内存中若指向同一个地址,则他们的内容肯定是相同的(因为他们实际上就是同一个对象)。

   而==(两个等号)运算符与equal函数就是运来比较这两块内容的。其中==运算符是用来比较内存中的地址是否相同,即比较它们的身份证号码是否相同。而equal函数则只比较他们的内容。如果他们的内容相同,即使身份证号码不相同(内存中的地址不同),这个函数也人们他们是相同的,会返回TRUE值。这就是这个两个对象比较方式的最大不同。或者说,他们在对对象进行比较时,出发点不同。一个比较对象名字所指向的内存地址是否相同,另外一个比较的时 对象名字所指向的存储模块中的内容是否相同。所以他们就会返回不同的结果。

 

  三、慎用内存地址赋值

  在实际工作中,笔者提醒程序开发人员,要慎用这种str3=str1内存地址的赋值形式。其实,利用这种形式来创建对象,其实根本没有创建一个新的对象。而只是将两个对象同时指向内存中的同一个存储区域。由于他们实际上是同一个对象,为此通过其中一个对象修改了对象的内容,那么另外一个对象名字调用的对象其也会受到影响。也就是说,它们相互之间缺乏独立性。为此在创建对象的时候,如果没有特殊的必要,最好为不同的对象名创建不同的实体对象。而不要将多个对象名指向同一个对象,这在开发应用程序的时候容易导致对象内容被无意中修改,从而导致应用程序结果出错。

  最后笔者需要提醒的是,在选择对象比较方式的时候,要了解==运算符与euqal函数之间的差异。如果只是想比较对象的内容是否相同,则只需要使用equal函数即可。但是如果要比较他们是否是同一个对象,即在内存中是否指向同一个存储区域,则需要使用==运算符。在实际应用的过程中,千万不能够张冠李戴。否则的话,很容易导致相反的结果。特别是将他们返回的值当作条件判断语句时,更加需要谨慎。因为此时如果选择的方法错误,则最后产生的结果往往是相反的。所以在对象进行比较时,跟变量的比较有很大的差异。在对象的比较上,程序员要谨慎行事。最根本的一点就是要搞清楚,到底比较的是什么东西,是对象内部的存储内容还是在对象名字与内存之间的关联关系(对象内存地址)。搞清楚这一点后,那么到底选择采用哪种方式来进行比较也就引刃而解了。

 

 

equals()对象的比较;hashcode()方法

 
一、 
String s1 = "abc"; 
String s2 = "abc"; 
s1 == s2        true;  
比较的是引用、物理地址。这样定义通过缓存机制,实际是只申请了一个内存地址,所以相等。 

String s3 = new String("abc"); 
String s4 = new String("abc"); 
s3 == s4        false; 
这样定义是申请了两个内存地址,所以不相等。 

s1.equals(s2)   true; 
s3.equals(s4)   true; 

Object 存在equals()方法执行的是比较两个Object的内存地址。因为所有的类都继承Object,所以都隐藏包含着equals()方法比较内存地址。但String 类复写了equals() 方法,它的策略是比较value,s1,s2,s3,s4内存都是abc所以是相等。 

对于一般的类都会复写equals()有自己的比较策略。当我们自己创建类时候,也同样需要复写equals()满足我们的业务要求。否则默认是比较对象的内存地址。
例如: 
class vo { 
 private Integer id; 
 private Integer name; 

 public Integer getId(){ 
     return id; 
 } 
 public void setId(Integer id){ 
     this.id = id; 
 } 
 ..........getName 
 ..........setName 

  public boolean equals(Object other){ 
     if(this == other){ 
        return true; 
     } 
     if(!(other instanceof vo)){ 
        return false; 
     } 
     vo o = (vo)other; 
     return this.getId == o.getId; 
  }

 public int hashcode(){ 
     return this.getId()+100; 
 } 

}

为什么要overwrite equals方法?业务需求

复写的 equals() 策略是如果两个object的 id 相等就认为两个对象是相等的,即使name不等。所以复写的方法完全按照自己的业务需求来制定。 

按照国际惯例也要加上下面这段 
在重写equals方法时,要注意满足离散数学上的特性 

 

自反性,自己跟自己比一定为true

非空性,跟空值null比,一定为false

 

对称性,a.equals(b) b.equals(a)结果是相同的

一致性如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变

传递性

 

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 



二、 
hashcode() 是创建对象自动生成的标识,为了索引等便利于快捷。 
Object 类带有生成 hashcode 策略,每次new 一个新的对象都会生成一个不同的hashcode。应用在hashMap,hastTable 等地方为索引用。 

当然也可以复写hashcode(),自己制定生成hashcode策略。 
例如: 
class vo { 
 private Integer id; 
 private Integer name; 

 public Integer getId(){ 
     return id; 
 } 
 public void setId(Integer id){ 
     this.id = id; 
 } 
 ..........getName 
 ..........setName 

 public boolean equals(Object other){ 
     if(this == other){ 
        return true; 
     } 
     if(!(other instanceof vo)){ 
        return false; 
     } 
     vo o = (vo)other; 
     return this.getId == o.getId; 
  } 

 public int hashcode(){ 
     return this.getId()+100; 
 }

} 
制定hashcode生成是 id + 100 . 

一般有这么一个规定: 

两个object 如果 equals() 相等,他们的hashcode也相等

如果equals()不相等,他们的hashcode不一定相等或者不等;他们的hashcode相等或者不等也决定不了equals()相等或者不等。

 

为什么要重写hashCode()?

重写hashCode是为了集合类存储这些对象的时候有个比较规则 
比如Map不允许重复元素,就是通过hashCode来检测的 
这就是为什么复写equals()时候也要复写hashcode()。如果不一同复写,equals()比较两个对象相等,但hashcode是产生的不等,在使用hashmap等集合类索引就会出问题。
分享到:
评论

相关推荐

    ACCP5.0S2教程\使用Java实现面向对象编程(Java OOP)\使用Java实现面向对象编程(Java OOP)上机贯穿案例源码

    在Java语言中,面向对象编程是其核心特性之一。 本资源是针对"ACCP5.0 S2"阶段的Java OOP上机贯穿案例,旨在帮助学习者深入理解和应用面向对象编程。"ACCP5.0 S2"可能是一个计算机编程课程或培训计划的一部分,其中...

    ACCP 5.0 S2_使用JAVA实现面向对象编程

    本课程"ACCP 5.0 S2_使用JAVA实现面向对象编程"主要涵盖了以下几个关键知识点: 1. 类与对象:类是创建对象的蓝图,它定义了一组属性(数据成员)和方法(成员函数)。对象则是类的实例,具有类所定义的属性和行为...

    使用Java实现面向对象编程.zip

    面向对象编程(Object-Oriented Programming,简称OOP)是一种编程...这些知识点是“使用Java实现面向对象编程”资料中可能涵盖的内容,通过学习和实践,你可以深入理解Java的面向对象特性,并运用到实际项目开发中。

    accp6.0使用Java实现面向对象编程

    使用Java实现面向对象编程使用Java实现面向对象编程

    使用Java实现面向对象编程

    面向对象编程(Object-Oriented Programming,简称OOP)是现代软件开发中广泛采用的一种编程范式,尤其在Java语言中得到了深入的应用。本课程旨在深入讲解如何使用Java语言实现面向对象编程,通过学习,你可以掌握...

    基于java面向对象实现扫雷程序源码

    基于java面向对象实现扫雷程序源码 基于java面向对象实现扫雷程序源码 基于java面向对象实现扫雷程序源码 基于java面向对象实现扫雷程序源码 基于java面向对象实现扫雷程序源码 基于java面向对象实现扫雷程序源码 ...

    JAVA类与对象及数组习题

    在JAVA中,编译错误可能发生在任何一行代码中,例如变量未声明、方法未定义等。 7. 继承的限制: JAVA中的单一继承使代码更可靠,一个类只能继承一个父类,但是可以实现多个接口。 8. 访问修饰符: 在JAVA中,访问...

    ACCP6.0使用Java实现面向对象编程完整课件

    在Java语言中,面向对象编程是核心特性之一,北大青鸟的ACCP6.0课程正是以此为主题,旨在帮助学习者掌握这一重要的编程技术。 在ACCP6.0的课程中,你会学习到以下关键的Java面向对象编程知识点: 1. 类与对象:类...

    java中把对象转化为byte数组的方法.doc

    Externalizable 接口提供了 writeExternal 和 readExternal 两个方法,用于手动实现对象的序列化和反序列化。 在实际应用中,序列化机制可以应用于各种领域,如网络通信、数据存储、分布式计算等。例如,在网络通信...

    JAVA中实现“相等”比较的深入分析.pdf

    本文将对Java中实现对象“相等”比较的方式进行深入分析,包括对JVM内存管理、Java函数重写、装箱与拆箱机制以及equals()方法的详细探讨。 首先,Java的数据类型分为基本数据类型和引用数据类型。基本数据类型包括...

    java注解实现通用Excel中文表格转实体对象列表,支持中文表头,Excel批量导入,excel转实体对象,excel读取

    java注解实现通用Excel中文表格转实体对象列表,支持中文表头,在Excel数据导入使用的通用工具类,代码简洁优雅 。基于注解实现的支持中文表头的读取Excel数据并转换为实体对象列表的工具类,使用该工具类可将Excel...

    ACCP6.0使用java实现面向对象编程ppt

    ACCP6.0使用java实现面向对象编程ppt

    北大青鸟6.0S2使用Java实现面向对象编程

    在Java语言中,面向对象编程是其核心特性之一,北大青鸟6.0S2课程针对这一主题进行了深入的教学。 1. **类与对象**: - **类**:类是创建对象的模板,定义了对象的数据属性和行为方法。在Java中,我们使用`class`...

    深入探索Java中的CORBA对象调用实现

    在Java中实现CORBA对象的调用涉及到ORB的初始化、对象的查找、调用以及异常处理等步骤。本文将详细介绍如何在Java中实现CORBA对象的调用,包括基础概念、ORB的使用、对象的存根(Stub)和骨架(Skeleton)的生成、...

    使用java实现面向对象编程

    这是一份关于使用java实现面向对象编程的pdf文档,适合新手学习java基础的

    Java面向对象程序设计课后答案全解

    在Java中,方法的覆盖和接口的实现是实现多态性的主要方式。此外,动态绑定(运行时多态)也是Java的一大特点。 5. **构造器**:构造器用于初始化新创建的对象,通常与类同名且无返回类型。学习何时、如何以及为...

    java中List对象列表实现去重或取出及排序的方法

    在Java中,对List对象列表实现去重或取出及排序是一种常见的操作。下面将详细介绍相关的方法和实现。 1. 实体类重写equals和hashCode方法 在Java中,想要去重List对象列表,首先需要重写实体类的equals和hashCode...

    12-Java面对对象高级(中)

    在Java编程语言中,"面向对象高级"是一个深入理解对象编程概念的重要阶段。这个主题涵盖了对象的多态性、抽象类、接口、适配器设计模式以及工厂设计模式等核心概念。下面将对这些关键知识点进行详细的阐述。 首先,...

Global site tag (gtag.js) - Google Analytics