11.Object方法
综述
Object是Java中所有类的父类,对它的学习十分的重要, Object的函数除了final方法,基本上都是被设计为要被覆盖的(Override),这节我们就一起来学习这些函数。
1.equals函数
/*equals的源代码*/
public boolean equals(Object obj) {
return (this == obj);
}
从源代码中我们可以看出来,不重写equals函数的话,一个对象只会与它本身相等,因此对于"值类"(String、Integer等)我们往往需要覆盖其equals函数。
覆盖equals时需要保证的几个条件
- 自反性,对于任何非null的引用值x,x.equals(x)必须反回true
- 对称性,对于任何非null的引用值x,y,当且仅当y.equals(x)返回true时,x.equals(y),才返回true.
- 传递性,对于任何非null的引用值x,y,z,如果x.equals(y)返回true,并且y.equals(z)返回ture,那 么x.equals(z)也必须返回true.
- 一致性,对于任何非null的引用值x,y,只要equals比较操作的两个对象中所用的信息没有被修改,多次 调用x.equals(y)就会一致的返回true,或者一致的返回false.
- 对于任何的非null的值x,x.equals(null),必须返回false
覆盖equals时必须要覆盖hashCode函数
Java中有如下的"约定"
equals()返回true------------------->hashCode()值相等//这个也是覆盖equals函数必须覆盖hashCode函数的原因之一
hashCode值相同------推不出--------equals()结果
a==b --------------------->equals()值为true
instanceOf返回false------------------>equals()值为false
重点:只覆盖equals函数不覆盖hashCode函数会有什么问题?
final class PhoneNumber{
private final int areaCode;
private final int prefix;
private final int lineNumber; public PhoneNumber(int areaCode,int prefix,int lineNumber){
this.areaCode=areaCode;
this.prefix=prefix;
this.lineNumber=lineNumber;
} @Override
public boolean equals(Object obj){
if(obj==this) return true;
if(!(obj instanceof PhoneNumber)) return false;
PhoneNumber o=(PhoneNumber) obj;
return o.lineNumber==this.lineNumber
&&o.prefix==this.prefix
&&o.areaCode==this.areaCode;
}
} main 函数
public static void main(String[] args) {
Map<PhoneNumber,String> map=new HashMap<>();
map.put(new PhoneNumber(1,2,10),"AAA");
map.put(new PhoneNumber(4,6,20),"BBB"); System.out.println(new PhoneNumber(1,2,10).equals(new PhoneNumber(1,2,10)));
System.out.println(new PhoneNumber(1,2,10).hashCode()==new PhoneNumber(1,2,10).hashCode());
System.out.println(map.get(new PhoneNumber(1,2,10)));
}
答案和解析
这段代码有三个输出流,而且PhoneNumber类并没有覆盖hashCode函数
1.验证equals函数,输出true,可以看出没有覆盖hashCode函数对equals没有什么影响
2.验证hashCode函数,由于没有覆盖HashCode函数,调用的是Object类的hashCode函数,两个类就算值是相同的,hashCode值也是不一样的,输出为false
3.Map中插入了一条数据key:new PhoneNumber(1,2,10) value:AAA,但是使用get函数的时候却找不到这个值了(输出null),这是因为没有遵循java中重写equals不重写hashCode函数的原因。
具体的原因:
HahMap底层是一个数组Entry[],每个数组元素是一个单链表,插入数据和获取数据的过程基本如下(过滤掉细节)
a.插入数据过程
- 根据Key值计算数据下标index,HashMap中index的计算是这样的,index=key.hashCode()^value.hashCode() //省去进行算法数运算
- 计算出下标之后在到对应的单链表中进行查找key,找到了就替换value,找不到就插入k-V
b.查找数据过程
- 计算index,index=key.hashCode()^value.hashCode(),但是这里的Key.hashCode值与插入的时候由于是不同的对象,所以hashCode值是不一样的
- index不一样,所以在对应的单链表中就找不到对应的数据
结论:
1.遵循Java规范,覆盖equals()时应该覆盖hashCode()的值
2.覆盖hashCode之后输出结果改变
@Override
public int hashCode(){
return this.areaCode^this.prefix^this.lineNumber;
}
//输出结果
true
true
AAA
2.HashCode函数
/*Object 中的hashCode函数*/
public native int hashCode();
//是一个native函数,也就是不是用Java开发的
问题:它是怎么编写的?----先留着
2.1HashCode函数的编写规范
- 可用性--"相同"的对象的hashCode值是相同的
- 说明:这个hashCode是可用的,但是所有对象的hashCode值都是一样的,散列表就回退化为单链表
@Override
public int hashCode(){
return 1;
}
- 高质量的HashCode要求相同的对象HashCode相同,不相同的对象的HashCode不相同-------怎么设计感觉是一个难题
- 对于散列表来说,希望散列表各个分支的长度相差不大
- 对于原始的HashCode函数
- 有些朋友误以为默认情况下,hashCode返回的就是对象的存储地址,事实上这种看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。下面是HotSpot JVM中生成hash散列值的实现:
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
} value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
3.toString函数
/*Object类的toStrign函数*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//toString函数默认输出的是类名+@+hashCode()
建议每个字类都重写toString函数,并且建立相关的文档
4.谨慎的覆盖clone
/*Object中的clone函数*/
protected native Object clone() throws CloneNotSupportedException;
4.1 数组是支持clone()操作的
利用这个函数我们可以复制一个"独立"的数组
int[] arrays=new int[]{1,2,3,4};
int[] copyArray=arrays.clone();//这里好像自动转换类型了?
System.out.println(Arrays.toString(copyArray));
这里独立的意思是这两个数组的地址是不一样的,如果这样写就不是独立的数组,int[] refArray=arrays;只是原来的数组上加了一个指针。
4.2 覆盖clone()函数需要注意的地方
1.必须要实现Clonable接口,不然就算覆盖了clone()函数,也会抛出ClonenotSupportException
我们奇怪的发现Cloneable竟然是空的,那么我们为什么要实现Cloneable接口呢?-----参考文献1
其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。
2.深层次的拷贝与潜层次拷贝
- 浅克隆
- 只能复制变量,对于对象,只复制他的引用
public class TaskTimeoutDemo extends Object implements Cloneable{
public static void main(String[] args) {
Inner inner=new Inner(22);
MyClass myclass=new MyClass(20,"yangyun",inner);
try {
MyClass copyOfClass=myclass.clone();
inner.setNum(90);
System.out.println(copyOfClass);
System.out.println(myclass);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
} class MyClass implements Cloneable{
public int age;
public String name;
public Inner inner; public MyClass(int age,String name,Inner inner){
this.age=age;
this.name=name;
this.inner=inner;
} @Override
public MyClass clone() throws CloneNotSupportedException{
MyClass copyOfMyClass=(MyClass) super.clone();
return copyOfMyClass;
} @Override
public String toString(){
return super.toString()+" ["+age+":"+name+inner+"]";
}
} class Inner implements Cloneable{
private int num; public Inner(int num){
this.num=num;
} public void setNum(int num) {
this.num = num;
} @Override
public String toString(){
return String.format("%d ",num);
}
}
输出结果:MyClass@4deb1dbd [20:yangyun90 ]
MyClass@77700f3d [20:yangyun90 ]- 深克隆
- 对于对象,也完整的复制一份
public class TaskTimeoutDemo extends Object implements Cloneable{
public static void main(String[] args) {
Inner inner=new Inner(22);
MyClass myclass=new MyClass(20,"yangyun",inner);
try {
MyClass copyOfClass=myclass.clone();
inner.setNum(90);
System.out.println(copyOfClass);
System.out.println(myclass);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
} class MyClass implements Cloneable{
public int age;
public String name;
public Inner inner; public MyClass(int age,String name,Inner inner){
this.age=age;
this.name=name;
this.inner=inner;
} @Override
public MyClass clone() throws CloneNotSupportedException{
MyClass copyOfMyClass=(MyClass) super.clone();
copyOfMyClass.inner=inner.clone();
return copyOfMyClass;
} @Override
public String toString(){
return super.toString()+" ["+age+":"+name+inner+"]";
}
} class Inner implements Cloneable{
private int num; public Inner(int num){
this.num=num;
} public void setNum(int num) {
this.num = num;
} @Override
public Inner clone(){
Inner copyOfInner=null;
try {
copyOfInner = (Inner) super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return copyOfInner;
} @Override
public String toString(){
return String.format("%d ",num);
}
}
//输出结果
MyClass@27abcd5e [20:yangyun22 ]
MyClass@28d2e37 [20:yangyun90 ]
参考文献
1.http://www.cnblogs.com/gw811/archive/2012/10/07/2712252.html
11.Object方法的更多相关文章
- python列表的11种方法
python列表的11种方法2017年11月24日 03:26:43 Milton-Long 阅读数:254版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.n ...
- [Guava学习笔记]Basic Utilities: Null, 前置条件, Object方法, 排序, 异常
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3842433.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...
- python 3.x 字典的11种方法
python 3.x 字典的11种方法2017年11月25日 01:02:11 Milton-Long 阅读数:535 标签: python python字典方法 更多个人分类: python-学习之 ...
- JavaScript非构造函数的继承( object()方法、浅拷贝与深拷贝 )
一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese = { nation:'中国' }; 还有一个对象,叫做&qu ...
- [Google Guava] 1.3-常见Object方法
原文链接 译者: 沈义扬 equals 当一个对象中的字段可以为null时,实现Object.equals方法会很痛苦,因为不得不分别对它们进行null检查.使用Objects.equal帮助你执行n ...
- LoadRunner 11破解方法:
LoadRunner 11破解方法: 请严格安装顺序操作! a.用LR8.0中的mlr5lprg.dll.lm70.dll覆盖LR11安装目录下“bin”文件夹中的对应文件: b.运行deleteli ...
- 执行ArrayList的remove(object)方法抛异常?
简介 或许有很多小伙伴都尝试过如下的代码: ArrayList<Object> list = ...; for (Object object : list) { if (条件成立) { l ...
- 【java】学习路径16-重写Object方法(equals()等)
在平时开发中,想要比较自定义类对象中的特定成员时,我们需要逐一手动比较,非常不方便. 举个栗子,我们有两个cafe对象,我们想比较两杯咖啡的价格是否一样,一般来说我们使用getter()来比较,但是这 ...
- Object方法equals、hashCode
java知识背景: 1)hashCode()方法返回的是Jvm的32位地址 2)==比较的是对象在jvm中的地址 3)Object的equals()比较的就是jvm物理地址 4)比较2个对象使用equ ...
随机推荐
- 【原创】PageAdminCMS 前台SQL注入漏洞(3)
之前根据公司的要求找了几个web程序的漏洞提交CNVVD,发现漏洞提交上去两个月了,CNVVD却没有任何回应,我提交的这几个漏洞却悄悄的修补掉了. 文章作者:rebeyond 受影响版本:V3.0 漏 ...
- IRequiresSessionState接口控制
刚刚接触.net web端的朋友都会被Session坑过,莫名其妙的不能读取Session数据,后来知道原来有IRequiresSessionState这个接口,不继承的就不能读取Session里面的 ...
- 12月8日phpcms添加子栏目后的读取
一个栏目下面如果没有子栏目,那么它调用的模板就是列表页模板(及list_为前缀的模板):如果一个栏目下面有子栏目,那么它调用的就是栏目首页模板(category_为前缀的模板). 所以,当你这个栏目添 ...
- ADO.NET--收藏整理别人的教程
本文所有内容均从前辈的博客中收集整理而来,仅供自己学习参考的时候快速访问用. ADO.NET入门教程(一) 初识ADO.NET ADO.NET入门教程(二)了解.NET数据提供程序 ADO.NET入门 ...
- 关于entityframework 自动生成实体类中加验证的属性重新生成后属性被覆盖解决办法
1.手动创建一个部分类 (你可以手动创建 partial class, 内容为空) [MetadataType(typeof(AppleMetadata))] public partial class ...
- SpringMVC工作原理
SpringMVC的工作原理图: SpringMVC流程 1. 用户发送请求至前端控制器DispatcherServlet. 2. DispatcherServlet收到请求调用HandlerMa ...
- 用JMeter测试monggodb的请求
JMeter测试MongoDB性能有两种方式,一种是利用JMeter直接测试MongoDB[即通过MongoDB协议测试],另一种是写Java代码方式测试MongoDB[即通过java请求测试] 注: ...
- Redis指南
一.简介 redis 和 memcached 都是高性能的键值缓存数据库服务,其中 memcached 支持多线程,而 redis 支持丰富的数据结构且能内置持久化机制. redis 数据都是以键值形 ...
- 如何基于Azure平台实现MySQL HA(方法论篇)
我们都知道,相较于传统的数据中心,Pulic cloud也有劣势,比如说数据库的HA,很多熟悉公有云平台的读者都知道,因为出于安全性性考虑以及一些技术条件的限制,很多本地数据中心的mysql HA方法 ...
- Python 局部变量与全局变量
本来以为 局部变量就是在函数/def/class/lambda内部的变量,全局变量就是在之前这些之外的变量.但是,再一次学习Python atm 中应用时发现了一次特例(意外) 字典中 在函数内部改变 ...