java.lang.Object 类详解

简述

Object 方法包括 5 个非 final 类型的方法,分别是:clone、hashCode、equals、toString、finalize 方法;包括 4 个 final 方法,分别是 getClass、wait、notify、notifyAll 方法。其中 clone 方法是 protected 方法,finalize 方法自 Java 9 之后被废弃。

1. clone 方法

1.1 源码

@HotSpotIntrinsicCandidate protected native Object clone() throws CloneNotSupportedException;

说明:调用该方法实现一个对象的浅复制,创建并且返回此对象的副本(“副本”的准确含义可能依赖于对象的类)。 Object.clone() 方法是一个 protected 方法,类只有实现 java.lang.Cloneable 接口,并重写 Object.clone() 方法才能使用该 clone 方法,否则抛出 CloneNotSupportedException 。

1.2 clone 与 copy 的区别

假设我们有一个 Person 对象,并假设 Person 类实现了Cloneable 接口并重写了 clone 方法。

Person a = new Person();
//copy 的做法通常为:
Person b = a;
//clone 的做法通常为:
Person c = a.clone();

说明:
(1)copy 是将对象 a 的引用赋值给对象 b,赋值之后对象 a 和对象 b 都指向同一个引用 a。
(2)clone 是实现对象的浅拷贝,产生一个新的对象,对象 c 与 对象 a 不指向同一个引用。

clone 在内存中实际操作是:将对象 a 的内存,拷贝一个副本,并重新分配一块内存区域用于保存副本。

1.3 浅拷贝和深拷贝

前面我们提到,clone 在内存中的实际操作时将一个对象的内存拷贝出一个副本、并重新分配一个内存用于保存副本。由于原对象(被拷贝的对象)属性可能存在两种值传递类型,分别是值传递和引用传递,对副本的操作可能对原对象造成影响(改变副本的引用传递属性的值,由于引用传递导致原对象的响应值同样改变)。

如果一个对象包含引用传递类型的属性,直接拷贝对象,不做特殊处理,这种拷贝称为浅拷贝。若一个对象不存在引用传递类型的数据,那也就不区别什么浅拷贝和深拷贝,可以称为浅拷贝也可以称为深拷贝。

示例:

定义2个类:Person类 包含 Book属性

public class Person implements Cloneable {

    private String name;
    private Book book;
    
    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", book=" + book +
                '}';
    }
}
public class Book {

    private String bookName;
    private String author;
    
    //省略 constructor 、 getter 、 setter 方法

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}
public class Main {
    //主方法:
    public static void main(String[] args) throws Exception {
        Book effectiveJava = new Book("Effective Java", "Joshua Bloch");
        Person sungm = new Person("sungm", effectiveJava);
        Person sunhw = (Person) sungm.clone();

        System.out.println("对象 sungm 的信息:" + sungm);
        System.out.println("对象 sunhw 的信息:" + sunhw);
        System.out.println("对象 sungm 与对象 sunhw 是否相等:" + (sungm == sunhw));

        System.out.println("-----------------------------------------");

        //改变对象 sunhw 的 book 属性
        sunhw.getBook().setBookName("Vue.js");
        sunhw.getBook().setAuthor("尤雨溪");
        System.out.println("对象 sunhw 的信息:" + sunhw);
        System.out.println("对象 sungm 的信息:" + sungm);
    }
}
/* 输出结果 */
对象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
对象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
对象 sungm 与对象 sunhw 是否相等:false
-----------------------------------------
对象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}
对象 sungm 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}

从输出结果可以看出:对象 sunhw 改变了的属性 book 的内容, 对对象 sungm 造成了影响。因为两个对象的 book 属性保存的是同一个引用,造成这种差异是由于对象进行了浅拷贝。

进行深拷贝示例:

Person implements Cloneable {

    private String name;
    private Book book;

    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.book = (Book) book.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", book=" + book +
                '}';
    }
}
class Book implements Cloneable{

    private String bookName;
    private String author;

    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        Book effectiveJava = new Book("Effective Java", "Joshua Bloch");
        Person sungm = new Person("sungm", effectiveJava);
        Person sunhw = (Person) sungm.clone();

        System.out.println("对象 sungm 的信息:" + sungm);
        System.out.println("对象 sunhw 的信息:" + sunhw);
        System.out.println("对象 sungm 与对象 sunhw 是否相等:" + (sungm == sunhw));

        System.out.println("-----------------------------------------");

        //改变对象 sunhw 的 book 属性
        sunhw.getBook().setBookName("Vue.js");
        sunhw.getBook().setAuthor("尤雨溪");
        System.out.println("对象 sunhw 的信息:" + sunhw);
        System.out.println("对象 sungm 的信息:" + sungm);
    }
}
/*运行结果*/
对象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
对象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
对象 sungm 与对象 sunhw 是否相等:false 
-----------------------------------------
对象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}
对象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}

说明:进行深拷贝一般有2中方式
(1)将属性对象实现 java.lang.Cloneable 接口并重写 clone 方法,然后在原始类中修改 clone方法。
(2)实现 java.io.Serializable 接口,通过序列化和反序列号拷贝对象。

2. hashCode 方法

返回对象的 Hash 值 (也称散列码)。对象的散列码是为了更好的支持基于哈希机制的 Java 集合类,例如:HashMap、HashSet、HashTable。

2.1 通用约定

(1)在 Java 程序执行期间,多次调用该方法应该返回相同的值,前提是未修改在 equals 方法中使用的信息。
(2)如果 2 个对象通过 equals 方法判定为 2 个对象相等、那么他们返回的 Hash 值也应该相等。
(3)对于 2 个对象来说,如果使用 equals 方法返回 false,那么这两个对象的 hashCode 值不要求一定不同(可以相同,可以不同),但是如果不同则可以提高应用的性能。
(4)对于 Object 类来说,不同 Object 对象的 hash 值是不同的、其 hash 值返回的是内存地址。

说明:鉴于第 (2) 条约定,如果重写了 equals 方法,那就要求重写 hashCode 方法。

3. equals 方法

判断两个对象是否相等。仅当两个对象引用的是同一个内存地址,即同一个对象,该方法返回 true。若不满足指向同一个内存地址、即使两个对象的内容相同,也会返回 false。

源码:

public boolean equals(Object obj) { return (this == obj); }

规则:
(1)自反性:对于任意非空对象, x.equals(x) 应该返回 true
(2)对称性:对于任意非空对象,若 x.equals(y) 返回 true,则 y.equals(x) 也应该返回 true
(3)传递性:对于任意非空对象,若 x.equals(y) 返回 true、y.equals(z) 也返回 true ,则 x.equals(z) 也应该返回 true
(4)一致性:若 x.equals(y) 返回 true,那第二次、第三次调用也应该返回 true,前提是未修改两个对象。

4. toString 方法

返回对象的字符串表现形式(类全名及无符号十六进制的 Hash 值)。API 建议所有的子类都重写该方法。

源码:

public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

5. finalize 方法

该方法自 Java 9 之后被废弃。

源码:

@Deprecated(since="9") protected void finalize() throws Throwable { }

说明:该方法并非一两句话能解释清楚,这里引入一篇博客、可供参考学习。https://www.jianshu.com/p/9d2788fffd5f

6. getClass 方法

返回运行时该对象的 class 对象,返回的 class 对象是被表示对象的类的 static synchronized 方法锁定的对象。

该方法一般常见于反射技术。

7. wait 方法

导致当前线程等待,可设置等待的毫秒数,知道其他线程调用 notify 方法或者调用该对象的 notifyAll 方法唤醒该线程。

源码:

public final void wait() throws InterruptedException {
    wait(0L);
}

public final native void wait(long timeoutMillis) throws InterruptedException;

8. notify 方法

唤醒正在此对象的监听器上等待的单个线程。如果该对象的监听器等待的线程存在多个、则唤醒其中一个线程,该线程的唤醒是随机的。

源码:

@HotSpotIntrinsicCandidate public final native void notify();

9. notifyAll 方法

唤醒正在此对象的监听器上等待的所有线程。

源码:

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

推荐阅读更多精彩内容