欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你了解Java萬物之基之Object類

 更新時間:2022年03月09日 15:29:46   作者:不想起床的小張  
Java是一門天然的面向對象的語言。而所有我們手動創(chuàng)造出來的類,都繼承于同一個類,即Object類。本文將通過示例為大家詳細介紹一下Java中的Object類,需要的可以參考一下

Java是一門天然的面向對象的語言。而所有我們手動創(chuàng)造出來的類,都繼承于同一個類,即Object類。

可以看一下Object類的結構

native方法

首先,超類擁有一個native方法

private static native void registerNatives();
static {
    registerNatives();
}

Java中,用native關鍵字修飾的函數表明該方法的實現并不是在Java中去完成。而是被C/C++完成,并被編譯成了.ddl文件,由Java去調用。registerNatives()方法本身,主要作用是將C/C++中的方法映射到Java中的native方法,實現方法命名的解耦。同時,也定義了一個靜態(tài)代碼塊,由此,每當我們創(chuàng)建Java對象時,都系統(tǒng)總是先調用靜態(tài)代碼塊,即調用native方法。該方法被private修飾,表明了這個方法是私有的,不被外部調用

getClass方法

通過此方法,可獲得類的Class實例,具體可見Java反射機制

hashCode方法

百度百科的定義如下:

哈希碼(HashCode),并不是完全唯一的,它是一種算法,讓同一個類的對象按照自己不同的特征盡量的有不同的哈希碼,但不表示不同的對象哈希碼完全不同。也有相同的情況,看程序員如何寫哈希碼的算法。

散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構,把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度.

由此可見,通過Java內部的散列函數,可以給每個實例化的對象分配一個內存地址,并記錄在散列表中,便于在程序中查找、新建、對比對象時更加高效。

寫一個實例打印看看:

public class base {

    public static void main(String[] args) {

        Apple apple = new Apple();
        System.out.println(apple.hashCode());
        System.out.println(apple);
        System.out.println(Integer.valueOf("74a14482",16));

    }

}

class Apple {
}

打印結果:

1956725890
p2.Apple@74a14482
1956725890

進程已結束,退出代碼0

可見對象的哈希地址為10進制數,與打印的原生16進制地址相對應

equals方法

Object equals() 方法用于比較兩個對象是否相等。

equals() 方法比較兩個對象,是判斷兩個對象引用指向的是同一個對象,即比較 2 個對象的內存地址是否相等。

可見equals比較兩個對象是否相等時,比較的是兩個對象的hashcode是否相等。因此,若要重寫equals方法,通常也要重寫hashcode方法。

例如,String類型并不是一個原生的數據類型(例如int,char,double等),而是Java重新封裝的對象。String、Integer等都重寫了equals方法,改變?yōu)楸容^值是否相等,而不是引用類型(hashcode)

例如String對equals方法的重新封裝:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

其中,instanceof 是 Java 的保留關鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數據類型。源碼表示,會先匹配引用是否相同,相同則返回真,否則將String實例轉化為字符數組,并逐個匹配是否相等,即匹配值是否相等。

String同時也重寫了hashcode方法:

 public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

其中,hash默認為0,所以重寫hash計算公式為:hash=s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

==和equals的區(qū)別

  • == :當比較基本類型時,則比較兩者的值是否相等;當比較引用類型時,則比較引用引用(hashcode)是否相等
  • equals:由源碼,比較引用是否相等,部分類型如String、Integer等重寫了equals方法,比較值是否相等

舉幾個例子:

public class base {

    public static void main(String[] args) {

        // new兩個String對象,但內容相同
        String a = new String("xxx");
        String b = new String("xxx");
        System.out.println(a == b);    // 比較hash值,因為是兩個不同的實例化對象,所以不同,返回false
        System.out.println(a.equals(b)); // 比較內容,均為“xxx”,返回true

        // 生成兩個引用
        String c = "xxx";
        String d = "xxx";
        System.out.println(c == d); // 比較hash值,因為指向同一個引用,所以相同,返回true
        System.out.println(c.equals(d)); // 比較內容,均為“xxx”,返回true
        
    }

}

總結:equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較,所以一般情況下 可理解為equals 比較的是值是否相等。

clone方法

Object clone() 方法用于創(chuàng)建并返回一個對象的拷貝。

clone 方法是淺拷貝,對象內屬性引用的對象只會拷貝引用地址,而不會將引用的對象重新分配內存,相對應的深拷貝則會連引用的對象也重新創(chuàng)建。

由源碼文檔,clone方法只能實現淺拷貝,且類需要重寫clone方法,調用super.clone來獲取返回的對象,因為不同包下,基類保護的實例方法子類無權訪問。另外,object類本身沒有實現Cloneable接口,但我們自己寫的類需要繼承Cloneable接口,否則會總會拋出CloneNotSupportedException異常。

寫個例子:

public class base{

    public static void main(String[] args) throws CloneNotSupportedException {

        // 實例化一個Student對象
        Student student = new Student(18,"Tony");
        // 打印內容
        System.out.println(student);

        // 克隆student實例
        Student anotherStudent = (Student) student.clone();
        // 打印克隆內容
        System.out.println(anotherStudent);

    }

}

class Student implements Cloneable {
    int age;
    String name;

    Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

打印結果:

Student{age=18, name='Tony'}
Student{age=18, name='Tony'}

進程已結束,退出代碼0

淺拷貝和深拷貝

淺拷貝例子

當拷貝的對象的成員有引用對象時,例如在Student類中包含了另一個Teacher對象時,被克隆的對象和克隆的對象指向同一個Teacher引用,所以當改變Teacher的數據時,克隆的對象也會隨之改變

寫個例子:

public class base {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 實例化一個Teacher對象
        Teacher teacher = new Teacher(25,"JayChou");
        // 實例化一個Student對象
        Student student = new Student(18, "Tony",teacher);
        // 打印內容
        System.out.println(student);
        // 克隆student實例
        Student anotherStudent = (Student) student.clone();
        System.out.println(anotherStudent);
        System.out.println("---------------------------------------");
        // 修改teacher數據,并更新student
        teacher.setAge(30);
        student.setTeacher(teacher);
        // 打印修改后的student實例和克隆對象實例
        System.out.println(student);
        System.out.println(anotherStudent);

    }

}

class Student implements Cloneable {
    int age;
    String name;
    Teacher teacher;

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    Student(int age, String name, Teacher teacher) {
        this.age = age;
        this.name = name;
        this.teacher = teacher;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}

class Teacher implements Cloneable {
    int age;
    String name;

    Teacher(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

}

打印結果:

Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
---------------------------------------
Student{age=18, name='fuck', teacher=Teacher{age=30, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}}

進程已結束,退出代碼0

這就是淺拷貝的結果,因指向同一個引用,當其中一個實例發(fā)生更新時,會發(fā)生連鎖變化

所以相反,實現深拷貝,使得不會發(fā)生連鎖反應,讓克隆與被克隆對象徹底分離!

實現深拷貝

大致有一下思路:

不采用clone方法,重新new一個對象,將需要復制的對象所有屬性成員放進去

 // 實例化一個Teacher對象
        Teacher teacher = new Teacher(25,"JayChou");
        // 實例化一個Student對象
        Student student = new Student(18, "Tony",teacher);
        // 打印內容
        System.out.println(student);
        // new一個一模一樣的!
        Student anotherStudent = new Student(18,"Tony",new Teacher(25,"JayChou"));
        System.out.println(anotherStudent);
        System.out.println("---------------------------------------");
        // 修改teacher數據,并更新student
        teacher.setAge(30);
        student.setTeacher(teacher);
        // 打印修改后的student實例和克隆對象實例
        System.out.println(student);
        System.out.println(anotherStudent);

打印結果:

Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
---------------------------------------
Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}

進程已結束,退出代碼0

重寫clone方法,將每個引用對象也實現克隆

@Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.setTeacher((Teacher) this.teacher.clone());
        return student;
    }

打印結果:

Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
---------------------------------------
Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}

進程已結束,退出代碼0

序列化

序列化的方式有很多,主要是工具比較多...這里我使用Apache Commons Lang序列化

首先,相關類都需要繼承序列化接口(接口并沒有實質的實現內容,僅僅作為一個標志)

public class base {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 實例化一個Teacher對象
        Teacher teacher = new Teacher(25,"JayChou");
        // 實例化一個Student對象
        Student student = new Student(18, "Tony",teacher);
        // 打印內容
        System.out.println(student);
        // 序列化深拷貝
        Student anotherStudent = (Student) SerializationUtils.clone(student);
        System.out.println(anotherStudent);
        System.out.println("---------------------------------------");
        // 打印序列化后內容 為字節(jié)流
        byte[] res = SerializationUtils.serialize(student);
        System.out.println(SerializationUtils.serialize(student));
        // 打印反序列化結果
        System.out.println(SerializationUtils.deserialize(res));
        System.out.println("---------------------------------------");
        // 修改teacher數據,并更新student
        teacher.setAge(30);
        student.setTeacher(teacher);
        // 打印修改后的student實例和克隆對象實例
        System.out.println(student);
        System.out.println(anotherStudent);

    }

}

class Student implements Serializable {
    int age;
    String name;
    Teacher teacher;

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    Student(int age, String name, Teacher teacher) {
        this.age = age;
        this.name = name;
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}

class Teacher implements Serializable {
    int age;
    String name;

    Teacher(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

}

打印結果:

Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
---------------------------------------
[B@50040f0c
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
---------------------------------------
Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}}
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}

進程已結束,退出代碼0

總結:第一種方式笨笨的哈哈,第二種方式需要手動重寫clone方法,當對象復雜時,就不是一個明智的選擇了。相比較之下,第三種當時顯的十分方便帥氣,可由于底層實現的復雜,存在一定的系統(tǒng)開銷。

toString方法

當沒有重寫該方法時,當打印實例化對象時,則返回類名與hash地址的16進制拼接字符串。為便于人們閱讀,建議所有子類重寫該方法

例如我的Student類重寫了該方法:

class Student implements Serializable {
    int age;
    String name;
    Teacher teacher;

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    Student(int age, String name, Teacher teacher) {
        this.age = age;
        this.name = name;
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}

則打印該對象時會返回人們便于閱讀的內容:

Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}

線程方法

wait(),wait(long),wait(long,int),notify(),notifyAll()分別用于線程的休眠于喚醒,在多線程內容中再做詳解

finalize方法

到此這篇關于一文帶你了解Java萬物之基之Object類的文章就介紹到這了,更多相關Java Object類內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 關于MyBatis各種SQL操作詳解

    關于MyBatis各種SQL操作詳解

    這篇文章主要介紹了關于MyBatis各種SQL操作詳解,MyBatis 是一款優(yōu)秀的半自動的ORM持久層框架,它支持自定義 SQL、存儲過程以及高級映射,需要的朋友可以參考下
    2023-05-05
  • Java中兩種拋出異常的方式示例詳解

    Java中兩種拋出異常的方式示例詳解

    在Java中有兩種拋出異常的方式,一種是throw,直接拋出異常,另一種是throws,間接拋出異常,本文給大家詳細說明java中兩種拋出異常的方式,感興趣的朋友一起看看吧
    2023-08-08
  • 詳解springboot接口如何優(yōu)雅的接收時間類型參數

    詳解springboot接口如何優(yōu)雅的接收時間類型參數

    這篇文章主要為大家詳細介紹了springboot的接口如何優(yōu)雅的接收時間類型參數,文中為大家整理了三種常見的方法,希望對大家有一定的幫助
    2023-09-09
  • java控制Pdf自動打印的小例子

    java控制Pdf自動打印的小例子

    java控制Pdf自動打印的小例子,需要的朋友可以參考一下
    2013-04-04
  • 使用String類型小數值轉換為Long類型

    使用String類型小數值轉換為Long類型

    這篇文章主要介紹了使用String類型小數值轉換為Long類型操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Spring session整合到Redis過程解析

    Spring session整合到Redis過程解析

    這篇文章主要介紹了Spring session整合到Redis過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • IntelliJ IDEA 的 Spring 項目如何查看 @Value 的配置和值(方法詳解)

    IntelliJ IDEA 的 Spring 項目如何查看 @Value 的配置和值(方法詳解)

    這篇文章主要介紹了IntelliJ IDEA 的 Spring 項目如何查看 @Value 的配置和值,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 淺析Spring?Cloud?Gateway中的令牌桶限流算法

    淺析Spring?Cloud?Gateway中的令牌桶限流算法

    這篇文章主要為大家淺析了Spring?Cloud?Gateway中的令牌桶限流算法原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • netty中的ByteBuf源碼詳解

    netty中的ByteBuf源碼詳解

    這篇文章主要介紹了netty中的ByteBuf源碼詳解,ByteBuf,顧名思義,就是字節(jié)緩沖區(qū),是Netty中非常重要的一個組件,某些場景下性能不是太好,netty開發(fā)團隊重新設計了ByteBuf用以替代原生ByteBuffer,需要的朋友可以參考下
    2023-11-11
  • Java?9中的"菱形"語法詳解

    Java?9中的"菱形"語法詳解

    Java?9?再次增強了“菱形”語法,它甚至允許在創(chuàng)建匿名內部類時使用菱形語法,Java?可根據上下文來推斷匿名內部類中泛型的類型,下面程序示范了在匿名內部類中使用菱形語法,感興趣的朋友跟隨小編一起看看吧
    2023-06-06

最新評論