jdk源码分析(一)——Object类

一.概述

Object类是java中所有类的父类,所有类默认(而非显式)继承Object。这也就意味着,Object类中的所有公有方法也将被任何类所继承。如果,整个java类体系是一颗树,那么Object类毫无疑问就是整棵树的根,因此值得我们仔细研读(以下代码基于jdk1.6)。

Object类中的方法如下:


下面我们逐一介绍。

二.核心方法

1.equals方法

默认的实现是:

可以看出默认情况下equals进行对象比较时只判断了对象是否是其自身,当我们有特殊的“相等”逻辑时,则需要覆盖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)返回true,那么x.equals(z)也必须返回ture。

一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回ture,或者一致的返回false。

非空性:对于任何非null的引用值x,x.equals(null)必须返回false。

当我们在类继承尤其要注意实现的equals方法是否满足约定。一个不满足约定的例子:

我们有一个水果类:

还有一个苹果类:

如果我们执行一下比较:

则会得到如下结果:

很显然,这并不符合对称性。

事实上:我们无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equals的约定,除非愿意放弃面向对象的抽象所带来的优势。

那这个问题该如何解决呢:

方法一:

不要拿Friut和Apple去比,这依赖于我们的主观自觉,虽然可以既利用继承带来的优势,但总归还要取决于人,有一定风险。

方法二:

将继承转为组合。

这时,再进行比较:

此时,结果是满足equals约定的:

2.hashCode方法

当我们覆盖了equals方法时,一定不能忘记覆盖hashCode方法。

hashCode方法的约定:

(1)在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一的返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

(2)如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

(3)如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。(尽管如此,理想的hashCode方法对不相等的对象,应当提供不同的hashCode值,这样当我们将对象作为Map的key时,可以提高散列表的性能)

如果我们覆盖了equals方法,但是没有覆盖hashCode方法,就会违反上述第2条约定。我们可以用代码来验证一下:

执行结果如下:

这会带来什么后果呢?如果我们把fruit1放到HashMap中,随后试图用一个“相等”的水果来找回时,就会出问题。如下:

执行程序,得到如下结果:

如何覆盖hashCode方法

一个好的散列函数通常倾向于“为不相等的对象产生不相等的散列码”。理想情况下,散列函数应该把集合中不相等的实例均匀的分布到所有可能的散列值上。

《effective java》一书中提到了一种简单的解决办法:

(1)把某个非零的常数值,比如17,保存在一个名为result的int类型的变量中。

(2)对于对象中的每个关键域f(equals方法中涉及的每个域,如果equals方法中没有涉及,则一定要排除在外),完成如下步骤:

a.为该域计算int类型的散列码c:

i.如果该域是boolean类型,则计算(f?1:0)。

ii.如果该域是byte、char、short或者int类型,则计算(int)f。

iii.如果该域是long类型,则计算(int)(f^(f>>>32))。

iv.如果该域是float类型,则计算Float.floatToIntBits(f)。

v.如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤iii中所述,为得到的long类型值计算散列值。

vi.如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式来比较这个域,则同样为这个域递归的调用hashCode。如果这个域的值为null,则返回0。

vii.如果该域是一个数组,则要把每一个元素当作单独的域来处理,也就是说,递归地应用上述规则,对每个重要元素计算一个散列码,然后根据步骤2.b中的做法把这些散列值组合起来。也可以使用Arrays.hashCode方法。

b.按照下面的公式,把步骤(2)a中计算得到的散列码c合并到result中:

result = 31 * result + c。

(3)返回result。

如果对我们之前提到的Fruit类重写hashCode方法,代码如下:

3.clone方法

通用约定:

clone方法将创建和返回该对象的一个拷贝。这个“拷贝”的精确含义取决于该对象的类。一般的含义是,对于任何对象x,表达式x.clone() != x 将会是true,并且,表达式x.clone().getClass() == x.getClass()将会是true。并且通常情况下,表达式x.clone().equals(x)将会是true。

一个标准的clone实现需要做到以下两点:

(1)调用super.clone()方法

(2)对于对象中的所有引用类型,均需要实现Cloneable接口,并重写clone方法,然后对每个引用执行clone方法。

示例代码:

使用clone方法的优点:

(1)速度快。clone方法最终会调用Object.clone()方法,这是一个native方法,本质是内存块复制,所以在速度上比使用new创建对象要快。

(2)灵活。可以在运行时动态的获取对象的类型以及状态,从而创建一个对象。

当然,使用clone方法创建对象的缺点同样非常明显:

(1)实现深拷贝较为困难,需要整个类继承系列的所有类都很好的实现clone方法。

(2)需要处理CloneNotSupportedException异常。Object类中的clone方法被声明为可能会抛出CloneNotSupportedException,因此在子类中,需要对这一异常进行处理。

因此,我们如果想使用clone方法的话,需要非常谨慎。事实上,《Effective Java》的作者Joshua Bloch建议我们不应该实现Cloneable接口,而应该使用拷贝构造器或者拷贝工厂。

4.toString方法

Object类提供了默认的toString方法实现:

从代码中我们可以看出,默认的toString方法仅仅返回类名+当前实例hashCode值的十六进制串,这非常不便于阅读,且没有包含实例中属性的值。

通过重写toString方法,我们可以输出需要的实例信息,例如可以对实例属性值进行格式化显示等等。如下所示:

除了手工编写格式化方式,我们还可以借助于一些第三方类库来实现实例的格式化,例如使用Apache Commons Lang工具包中的ToStringBuilder类。

三.其他方法

1.registerNatives方法

该方法被声明为是一个private static native方法,该方法的调用执行是在接下来声明的static块中:

由于是native方法,因此源代码我们不能直接看到,而在相关的C++代码中。该方法主要是为了服务于JNI(Java Native Interface)的,它主要是提供了java类中的方法与对应C++代码中的方法的映射,方便jvm查找调用C++中的方法。

2.getClass方法

该方法被声明为public final native方法,这说明该方法无法被重写,且是一个本地方法,通过API文档,我们可以了解到:该方法将返回对象的运行时类。例如:

运行结果如下:

获得运行时类信息后,我们接下来就可以做更多的事情:

(1)生成类的实例

(2)获取类中的方法、属性

(3)获取类的注解

(4)获取类所在的包

……

有关Class类,我们后续还会专门学习。

3.wait、notify、notifyAll方法

这三个方法都是public final native的。不可以被子类重新,且都是本地方法。这三个方法提供了java线程间等待、挂起等协同机制,是java多线程的基础,也留待后续深入学习。

4.finalize方法

根据java api中的说法:

“当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法”。

“finalize方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize的主要目的是在不可撤消地丢弃对象之前执行清除操作”。

“Java 编程语言不保证哪个线程将调用某个给定对象的finalize方法”。

“对于任何给定对象,Java 虚拟机最多只调用一次finalize方法”。

尽管finalize在某些时候是有用的,但是在大部分情况下,还是不建议使用,基于以下几点:

(1)不保证会被jvm执行,且不知道何时才会执行。这就给程序执行带来了很大不确定性。

(2)不同的jvm垃圾回收算法不一致,在一个jvm上工作良好,可能在另一个jvm上未必有效。

(2)性能。根据Joshua Bloch在《Effective Java》中的描述,增加了finalize后,对象的创建和销毁时间慢了430倍。


本文已迁移至我的博客:http://ipenge.com/21344.html

参考资料:

1.《effective java》

2. http://techbook.blog.163.com/blog/static/304885102012235613945/

3. http://www.cnblogs.com/zuoxiaolong/p/pattern24.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,064评论 0 62
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 1,956评论 0 3
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • 加油!用心去画,不论美丑
    凉亭_3087阅读 198评论 0 0