java equals中 euqals和 ==一样吗?

Java问答:终极父类(2)—下篇 - ImportNew
| 分类: ,
| 标签: ,
问: hashCode()方法是用来做什么的?
答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来)。这个方法通常用在基于hash的集合类中,像java.util.HashMap,java.until.HashSet和java.util.Hashtable.
问: 在类中覆盖equals()的时候,为什么要同时覆盖hashCode()?
答: 在覆盖equals()的时候同时覆盖hashCode()可以保证对象的功能兼容于hash集合。这是一个好习惯,即使这些对象不会被存储在hash集合中。
问: hashCode()有什么一般规则?
答: hashCode()的一般规则如下:
在同一个Java程序中,对一个相同的对象,无论调用多少次hashCode(),hashCode()返回的整数必须相同,因此必须保证equals()方法比较的内容不会更改。但不必在另一个相同的Java程序中也保证返回值相同。
如果两个对象用equals()方法比较的结果是相同的,那么这两个对象调用hashCode()应该返回相同的整数值。
当两个对象使用equals()方法比较的结果是不同的,hashCode()返回的整数值可以不同。然而,hashCode()的返回值不同可以提高哈希表的性能。
问: 如果覆盖了equals()却不覆盖hashCode()会有什么后果?
答: 当覆盖equals()却不覆盖hashCode()的时候,在hash集合中存储对象时就会出现问题。例如,参考代码清单2.
代码清单2:当hash集合只覆盖equals()时的问题
import java.util.HashM
import java.util.M
final class Employee
Employee(String name, int age)
this.name =
this.age =
public boolean equals(Object o)
if (!(o instanceof Employee))
Employee e = (Employee)
return e.getName().equals(name) && e.getAge() ==
String getName()
int getAge()
public class HashDemo
public static void main(String[] args)
Map&Employee, String& map = new HashMap&&();
Employee emp = new Employee(&John Doe&, 29);
map.put(emp, &first employee&);
System.out.println(map.get(emp));
System.out.println(map.get(new Employee(&John Doe&, 29)));
代码清单2声明了一个Employee类,覆盖了equals()方法但是没有覆盖hashCode()。同时声明了一个一个HashDemo类,来演示将Employee作为键存储时时产生的问题。
main()函数首先在实例化Employee之后创建了一个hashmap,将Employee对象作为键,将一个字符串作为值来存储。然后它将这个对象作为键来检索这个集合并输出结果。同样地,再通过新建一个具有相同内容的Employee对象作为键来检索集合,输出信息。
编译(javac HashDemo.java)并运行(java HashDemo)代码清单2,你将看到如下输出结果:
first employee
如果hashCode()方法被正确的覆盖,你将在第二行看到first employee而不是null,因为这两个对象根据equals()方法比较的结果是相同的,根据上文中提到的规则2:如果两个对象用equals()方法比较的结果是相同的,那么这两个对象调用hashCode()应该返回相同的整数值。
问: 如何正确的覆盖hashCode()?
答: Joshua Bloch的《》第八版中给出了一个四步法来正确的覆盖hashCode()。下面的步骤和Bloch的方法类似。
声明一个int型的变量,命名为result(或者其他你喜欢的名字),然后初始化为一个不为零的常量(比如31)。使用一个不为零的常量会影响到所有的初始的哈希值(步骤2.1的结果)为零的值。【A nonzero value is used so that it will be affected by any initial fields whose hash value (computed in Step 2.1) is zero. 】如果初始的result为0的话,最后的哈希值不会被它影响到,所以冲突的几率会增加。这个非零result值是任意的。
对每一个对象中有意义的具体值(在equals()中所涉及的值),f,进行以下步骤的处理:
按照以下步骤计算f的基于int型的哈希值hc:
对于一个boolean型变量,hc = f? 0 : 1;。
对于一个byte,char,short,或者int型变量,hc = (int)f;.
对于一个long型变量,hc = (int) (f ^ (f &&& 32));.这个表达式是将long型变量作为32位(long型最多有32位)来计算的;
对于一个float型变量,hc = Float.floatToIntBits(f);.
对于一个double型变量,long l = Double.doubleToLongBits(f); hc = (int) (l ^ (l &&& 32));.
对于引用类型的变量,如果类中的equals()方法递归的调用equals()类比较成员变量,那么就递归调用hashCode();如果需要更复杂的比较,就计算这个值的“标准表示”来脚酸标准的哈希值;如果引用类型的值为null,f = 0.
对于一个数组类型的引用,将每一个元素视为单独的变量,对于每一个有意义的值,调用对应的方法计算其哈希值,最后如步骤2.2的描述那样将所有的哈希值合并。
计算result = 37*result+hc,将所有的hc合并到哈希值中。乘法使哈希值取决于它的值的规则,当一个类中存在多种相似的值时,就增加了哈希表的离散性。
返回result。
完成hashCode()之后,要确保相同的对象调用hashCode()得到相同的哈希值。
举例说明上面这个方法,代码清单3是代码清单2的第二个版本,它的Employee类重写了hashCode()。
代码清单3:正确地覆盖hashCode()
import java.util.HashM
import java.util.M
final class Employee
Employee(String name, int age)
this.name =
this.age =
public boolean equals(Object o)
if (!(o instanceof Employee))
Employee e = (Employee)
return e.getName().equals(name) && e.getAge() ==
String getName()
int getAge()
public int hashCode()
int result = 31;
result = 37*result+name.hashCode();
result = 37*result+
public class HashDemo
public static void main(String[] args)
Map&Employee, String& map = new HashMap&&();
Employee emp = new Employee(&John Doe&, 29);
map.put(emp, &first employee&);
System.out.println(map.get(emp));
System.out.println(map.get(new Employee(&John Doe&, 29)));
代码清单3的Employee类中声明了两个在hashCode()都涉及到的值。覆盖的hashCode()方法首先初始化result为31,然后将String类型的name变量和int型的age变量的哈希值合并到result中,随后返回result。
编译(javac HashDemo.java)并运行(java HashDemo)代码清单3,你将看到如下输出结果:
first employee
first employee
在第三部分中,我会讨论toString()方法和wait/notification方法。并探究Object中的方法在接口和Java8中的内容。
本文的代码可以在下载,代码运行环境为 Windows7,JDK7u6.
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(了解我更多,在:)
微信关注: ImportNew
分享Java相关的技术文章、工具资源和热点资讯。扫描加关注,碎片时间提高Java开发技能!
不过这不能算作保留的内存空间,from 和 to这两块空间都应回归为young space。所以说y...
Finneen Chiong
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
新浪微博:
微信号:importnew
反馈建议:@
广告与商务合作QQ:
& 2015 ImportNewJava 问答:终极父类(三)——finalize()和 getClass() - 推酷
Java 问答:终极父类(三)——finalize()和 getClass()
我之前发布了关于
java.lang.Object
类及其方法的一系列文章。在介绍了
之后,我们又探究了
方法。在这篇文章中,我们将继续讨论
finalize()
getClass()
hashCode()
finalize()
方法是用来做什么的?
finalize()
方法可以被子类对象所覆盖,然后作为一个终结者,当GC被调用的时候完成最后的清理工作(例如释放系统资源之类)。这就是终止。默认的
finalize()
方法什么也不做,当被调用时直接返回。
对于任何一个对象,它的
finalize()
方法都不会被JVM执行两次。如果你想让一个对象能够被再次调用的话(例如,分配它的引用给一个静态变量),注意当这个对象已经被GC回收的时候,
finalize()
方法不会被调用第二次。
&有人说要避免使用
finalize()
方法,这是真的吗?
&通常来讲,你应该尽量避免使用
finalize()
。相对于其他JVM实现,终结器被调用的情况较少——可能是因为终结器线程的优先级别较低的原因。如果你依靠终结器来关闭文件或者其他系统资源,可能会将资源耗尽,当程序试图打开一个新的文件或者新的系统资源的时候可能会崩溃,就因为这个缓慢的终结器。
&应该使用什么来替代终结器?
&提供一个明确的用来销毁这个对象的方法(例如,
java.io.FileInputStream
void close()
方法),并且在代码中使用
try - finally
结构来调用这个方法,以确保无论有没有异常从
中抛出,都会销毁这个对象。参考下面释放锁的代码:
Lock l = ...; // ... is a placeholder for the actual lock-acquisition code
// access the resource protected by this lock
l.unlock();
// ... is a placeholder for the actual lock-acquisition code
// access the resource protected by this lock
这段代码保证了无论
是正常结束还是抛出异常都会释放锁。
&什么情况下适合使用终结器?
&终结器可以作为一个安全保障,以防止声明的终结方法(像是
java.io.FileOutputStream
java.util.concurrent.Lock
方法)没有被调用。万一这种情况出现,终结器可以在最后被调用,释放临街资源。
finalize()
&可以遵循下面这个模式写
finalize()
finalize()示例
protected void finalize() throws Throwable
// Finalize the subclass state.
super.finalize();
// Finalize the subclass state.
子类终结器一般会通过调用父类的终结器来实现。当被调用时,先执行
模块,然后再在对应的
super.finalize()
;这就保证了无论
会不会抛出异常父类都会被销毁。
finalize()
抛出异常会怎样?
finalize()
抛出异常的时候会被忽略。而且,对象的终结将在此停止,导致对象处在一种不确定的状态。如果另一个进程试图使用这个对象的话,将产生不确定的结果。通常抛出异常将会导致线程终止并产生一个提示信息,但是从
finalize()
中抛出异常就不会。
&我想实践一下
finalize()
方法,能提供一个范例吗?
&参考代码清单1.
class LargeObject
byte[] memory = new byte[];
protected void finalize() throws Exception
System.out.println(&finalized&);
public class FinalizeDemo
public static void main(String[] args)
while (true)
new LargeObject();
LargeObject
&finalized&
FinalizeDemo
LargeObject
&代码清单1:实践
finalize()
代码清单1中的代码写了一个
FinalizeDemo
程序,重复地对
largeObject
类实例化。每一个
Largeobject
对象将产生4M的数组。在这种情况下,由于没有指向该对象的引用,所以
LargeObject
对象将被GC回收。
GC会调用对象的
finalize()
方法来回收对象。
LargeObject
finalize()
方法被调用的时候会想标准输出流打印一条信息。它没有调用父类的
finalize()
方法,因为它的父类是
,即父类的
finalize()
方法什么也不做。
javac FinalizeDemo.java
java FinalizeDemo
)代码清单1.当我在我的环境下(64位win7平台)使用JDK7u6来编译运行的时候,我看到一列
的信息。但是在JDK8的环境下时,在几行
之后抛出了
java.lang.OutOfMemoryError
finalize()
方法对于虚拟机来说不是轻量级的程序,所以不能保证你一定会在你的环境下观察到输出信息。
得到对象的类
getClass()
方法是用来做什么的?
getClass()
方法可以得到一个和这个类有关的
java.lang.Class
对象。返回的
对象是一个被
static synchronized
方法封装的代表这个类的对象;例如,
static sychronized void foo(){}
。这也是指向反射API。因为调用
getClass()
的对象的类是在内存中的,保证了类型安全。
&还有其他方法得到
对象的方法有两种。可以使用
类字面常量
,它的名字和类型相同,后缀位.class;例如,
Account.class
。另外一种就是调用
方法。类字面常量更加简洁,并且编译器强制类型安全;如果找不到指定的类编译就不会通过。通过
可以动态地通过指定包名载入任意类型地引用。但是,不能保证类型安全,可能会导致
方法的时候,
getClass()
instanceof
哪一个更好?
getClass()
instanceof
的话题一直都是Java社区争论的热点,Angelika Langer的
这片文章可以帮助你做出选择。关于正确覆盖
方法(例如保证对称性)的讨论,Lang的这篇文章可以作为一个很好的参考手册。
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见C#中的== Equals 与 Java 的== Euqals - Neo0820 - 博客园
随笔 - 112, 文章 - 0, 评论 - 59, 引用 - 6
一.首先看看C#
1、ReferenceEquals()比较的是对象reference,声明如下:
public&static&bool&ReferenceEquals(object&objA,object&objB);
特殊情况:objA、objB均为NULL时,为true
注意:该方法如作用与System.ValueType型(值类型),一律返回false:值类型需进行boxing(装箱)操作,则reference不一样了
2、==默认也是reference的,但很多对象给操作符重写了,进行的是value比较,System.ValueType均如此,部分class也重写了,典型的如System.String
3、Equals()包含两种定义:
public&virtual&bool&Equals(object);
public&static&bool&Equals(object,&object);
virtual方法的Equals默认的是reference的比较,但.NET&Framework中的大部分类都重写了该方法,所以需要具体类型对待。static方法的Equals则有点区别,其判别方式如下:i)&先A、B两对象是否同一instance,是-true.&ii)A、B是否为NULL,是-true.&iii)&前两者均不是true,返回调用virtual的Equals()方法的结果.这里需要注意的即virtual的Equals()方法大都被重写了,所以返回的结果需要依赖于各自类型的实现.
String&str0&="hello";&
String&str1&&="hello";&
System.Text.StringBuilder&sb=&new&System.Text.StringBuilder();
sb.Append("&str0.equals(str1)==&"&+&str0.Equals(str1));&
sb.Append("&br&");
sb.Append("&(str0==str1)==&"&+&(str0==str1));
Response.Write(sb.ToString());
*输出的结果
*str0.equals(str1)==&True
*(str0==str1)==&True
String&str0&=new&String(new&char[]{'h','e','l','l','o'});&
&&&&&&&&&&&&String&str1&&="hello";&
&&&&&&&&&&&&System.Text.StringBuilder&sb=&new&System.Text.StringBuilder();
&&&&&&&&&&&&sb.Append("&str0.equals(str1)==&"&+&str0.Equals(str1));&
&&&&&&&&&&&&sb.Append("&br&");
&&&&&&&&&&&&sb.Append("&(str0==str1)==&"&+&(str0==str1));
&&&&&&&&&&&&Response.Write(sb.ToString());
*输出的结果
*str0.equals(str1)==&True
*(str0==str1)==&True
然后再看看Java
==是判断两个变量或对象的实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相同
str0&与 str1 使用“字符串”池保存对象
String&str0&=&"<span style="color: #3";&
String&str1&=&"<span style="color: #3";&
System.out.println("&str0.equals(str1)==&"&+&str0.equals(str1));&
System.out.println("&(str0==str1)==&"&+&(str0==str1));
*str0.equals(str1)==&true&
*(str0==str1)==&true
str0&与 str1 使用内存保存字符串对象
String&str0&=&new&String("<span style="color: #3");&
String&str1&=&new&String("<span style="color: #3");&
System.out.println("&str0.equals(str1)==&"&+&str0.equals(str1));&
System.out.println("&(str0==str1)==&"&+&(str0==str1));
*str0.equals(str1)==&true&
*(str0==str1)==&falsejava中==和 equal区别 - zhuxiangdong的专栏
- 博客频道 - CSDN.NET
马上就面临找工作了,最近从网上下载了大量的java笔记题,看到笔试题中有很多关于基本数据类型和引用类型相关的题目,以下为==和equals()的区别
明确概念:
对象的引用:当用类创建一个对象时,类中的成员变量被分配内存空间,这些内存空间称为该对象的实体,而对象中存放着引用(地址),以确保该实体由该对象操作使用。
一、&#65279;&#65279;&#65279;&#65279;比较对象为基本数据类型(byte,short,char,int,long,float,double,boolean)
比较两个基本数据类型是否相等用==,因为只有类才会有equals方法。
备注:String不是基本数据类型
二、比较对象为引用数据类型
euqals和==本质上都是比较比较的是两个对象的引用(内存地址)是否相同。equals()是Object类的方法 ,object类是所有类的基类,所以每个类都会继承equals()方法。
但在String,Integer,Date在这些类当中重写了equals方法,而不再是比较对象在堆内存中的存放地址了,而是比较它们指向的实体(内容)是否相同。
&&&&&&&&&&&&& Person p1=new Person();//使用new 关键字创建一个对象会为此对象分配内存空间。
&&&&&&&&&&&&& Person p2= //此对象还没有内存空间
&&&&&&&&&& && p2=p1;//此时p1和p2指向的是同一个内存空间 或说是指向同一个实体(内容)
&&&&&&&&&& & String s1 = &nihao&;
&&&&& && & & String s2 = &nihao&;
&&&&&&&&&&& 内存示意图
&&&& & & && & s1==s2 //返回true,指向同一个内存空间,s1和s2为同一个对象
&&&&&&&&& && s1.equals(s2)//返回true
&&&&&&&&& && String s3=new String(&nihao&);
&&&&&&& &&&& String s4=new String(&nihao&);
&&&&&&&&&& 内存示意图:
&& & &&& &&& s3==s4//返回false,s3和s4分别指向不同的内存空间
&&& & & &&&& s3.equals(s4)//返回true,它比较的是两个字符串的内容
所以 对于引用类型之间进行equals比较,在没有覆写equals方法的情况下,还是比较它们在内存中的存放地址是否相同,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
以上为个人总结,若有错误之处,请大家批评指正。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:25226次
排名:千里之外
原创:23篇
(1)(1)(1)(4)(6)(3)(1)(3)(2)(1)(5)(2)

我要回帖

更多关于 java中equals 的文章

 

随机推荐