Object類入門這一篇就夠了!

第三階段 JAVA常見對象的學習

第一章 常見對象——Object類

引言:

在講解Object類以前,咱們不得不簡單的提一下什麼是API,先貼一組百度百科的解釋:java

API(Application Programming Interface,應用程序編程接口)是一些預先定義的函數,目的是提供應用程序與開發人員基於某軟件或硬件得以訪問一組例程的能力,而又無需訪問源碼,或理解內部工做機制的細節。編程

簡單的說:就是 Java 中有好多現成的類庫,其中封裝了許多函數,只提供函數名和參數,但隱藏了函數的具體實現,這些可見的部分做爲與外界聯繫的橋樑,也就是咱們所稱的 API ,不過因爲Java是開源的,因此這些隱藏的實現咱們也是能夠看到的。框架

(一) Object 類的概述

(1) Object是類層次結構的根類,全部的類都隱式的(不用寫extends)繼承自Object類。ide

(2) Java 全部的對象都擁有Object默認方法函數

(3) Object類的構造方法有一個,而且是無參構造學習

這就對應了前面學習中的一句話,子類構造方法默認訪問父類的構造是無參構造測試

咱們須要瞭解的方法又有哪些呢?this

A: hashCode() B: getClass() C: finalize() D: cloneidea

E: notify() F: notifyAll()spa

咱們須要掌握的方法又有哪些呢?

A: toString() B: equals()

(1) hashCode

返回對象的哈希值(散列碼),不是實際地址值,不過能夠理解爲地址值。

它實際上返回一個int型整數,用於肯定該對象在哈希表中的索引位置

暫時瞭解便可,學習集合框架內容後將會專篇深刻學習

//Student類

public class Student extends Object {
}
複製代碼
//StudentDemo類

public class StudentDemo {
    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println(s1.hashCode());

        Student s2 = new Student();
        System.out.println(s2.hashCode());

        Student s3 = s1;
        System.out.println(s3.hashCode());
    }
}

//運行結果:
460141958
1163157884
460141958
複製代碼

(2) getClass

返回對象的字節碼文件對象,在反射篇章詳細解釋, 暫作簡單瞭解。

public class StudentDemo {
    public static void main(String[] args) {
        Student s = new Student();
        Class c = s.getClass();
        String str = c.getName();
        System.out.println(str);

        //鏈式編程
        String str2 = s.getClass().getName();
        System.out.println(str2);
    }
}

//運行結果
cn.bwh_02_getClass.Student
cn.bwh_02_getClass.Student
複製代碼

(3) finalize()

在對象將被垃圾回收器清除前調用,但不肯定時間,而且對象的finalize()方法只會被調用一次,調用後也不必定立刻清除該對象。

(4) clone()

以實現對象的克隆,包括成員變量的數據複製,可是它和兩個引用指向同一個對象是有區別的。

咱們先來解釋一下後半句話

若是咱們想要複製一個變量,能夠這樣作 Eg:

int a = 20;
int b = a;
複製代碼

那麼咱們想要複製一個對象,是否是也能夠這樣作呢?

//Student

public class Student {
    int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
複製代碼
//StudentDemo

public class StudentDemo {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setAge(20);
        Student s2 = s1;//將引用賦值
        System.out.println("學生1年齡:" + s1.getAge());
        System.out.println("學生2年齡:" + s2.getAge());
        System.out.println("------------------------------");

        s2.setAge(25);
        System.out.println("學生1年齡:" + s1.getAge());
        System.out.println("學生2年齡:" + s2.getAge());
    }
}

//運行結果
學生1年齡:20
學生2年齡:20
---------------------------
學生1年齡:25
學生2年齡:25
複製代碼

很明顯,即便將對象s1賦值給對象s2,可是經過set傳值的時候,二者仍然會同時變化,並無起到克隆(獨立)的做用,這是由於賦值時只是將存儲在棧中,對對象的引用賦值,所以它們兩個的引用指向同一個對象(堆中),因此不管如何賦值,只要堆中的對象屬性發生了變化,經過引用顯示屬性的時候,均是相同的。

用法:
  1. 實現Cloneable接口
  2. 重寫clone方法
分類:

淺拷貝: 僅拷貝對象,不拷貝成員變量,僅複製了變量的引用,拷貝先後變量使用同一塊內存,內存銷燬後,必須從新定義(二者同生共死)

深拷貝: 不只拷貝對象,也拷貝成員變量(真正意義上的複製, 二者獨立無關)

//淺拷貝

public class Person implements Cloneable{
    private int age = 20;
	
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
複製代碼
//深拷貝

public class Person implements Cloneable {
    public int age = 20;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //拷貝對象
        Person person = (Person) super.clone();
        //拷貝成員變量
        person.age = (int) age.clone();
        //返回拷貝對象
        return person;
    }
}
複製代碼

咱們來利用淺拷貝解決剛開始那個問題

//Person類,寫出get、set方法、重寫clone方法

//PersonDemo類

public class PersonDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person();
        p1.setAge(20);
        Person p2 = (Person) p1.clone();
        System.out.println("第一我的的年齡:"+ p1.getAge() );
        System.out.println("第二我的的年齡:"+ p2.getAge() );
        System.out.println("--------------------------");

        p2.setAge(25);
        System.out.println("第一我的的年齡:"+ p1.getAge() );
        System.out.println("第二我的的年齡:"+ p2.getAge() );
    }
}

運行結果:
第一我的的年齡:20
第二我的的年齡:20
--------------------------
第一我的的年齡:20
第二我的的年齡:25
複製代碼

(5) wait、notify和notifyAll

三者屬於線程通訊間的Api,此部分放在往後講

(6) toString()——重要

public static toString(): 返回該對象的字符串表示

//Student類

public class Student {
    private String name;
    public int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
複製代碼
//StudentDemo類

package cn.bwh_04_toString;

public class StudentDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("admin");
        s.setAge(20);
        //直接輸出s也會默認的調用toString方法
        System.out.println(s.toString());
    }
}

//經過set方法賦值後,直接調用toString()

運行結果:
cn.bwh_04_toString.Student@1b6d3586
複製代碼

很明顯,給咱們返回這樣的信息意義是不大的,因此咱們建議對全部子類重寫該方法

//在Student類中重寫 toString()

	@Override                                                          
	public String toString() {                                         
		return "Student[" + "name=" + name + ", " + "age=" + age + "]";

//運行結果:
Student[name=admin, age=20]
複製代碼

經過重寫toString後,結果按照咱們所定的規則以字符串的形式輸出

(重寫後會優先使用類中的toString方法)

爲何要用它呢?

主要目的仍是爲了簡化輸出

  1. 在類中重寫toString()後,輸出類對象就變得有了意義(輸出s 和 s.toString()是同樣的 ,不寫也會默認調用),變成了咱們實實在在的信息,而不是上面的cn.bwh_04_toString.Student@1b6d3586。

  2. 若是咱們想要屢次輸出 類中的成員信息,就須要屢次書寫get方法(每用一次就得寫)

    System.out.println("Student[" + "name=" + s.getName() + ", " + "age=" + s.getAge() + "]");
    複製代碼

    而調用toString()就簡單多了

補充:
//二者等價
toString();
getClass().getName()+ '@' + Integer.toHexString(hashCode())
    
//輸出結果
cn.bwh_04_toString.Student@1b6d3586。
複製代碼

(7) equals()——重要

比較兩個對象是否相同

默認狀況下,比較的是地址值是否相同。

而比較地址值是沒有意義的,因此,通常子類也會重寫該方法。

在諸多子類,如String、Integer、Date 等均重寫了equals()方法

改進思路:

咱們能夠將比較地址值轉變爲比較成員變量

  1. 由於name爲String類型,而String類型爲引用類型,因此不可以用==比較,應該用equal()

  2. String中默認重寫過的equal()方法是用來比較字符串內容是否相同

  3. 咱們要使用的是學生類的成員變量,因此父類 Object不能調用子類Student的特有功能

因此使用向下轉型

//重寫v1.0
    public boolean equals(Object o) {
        Student s = (Student) o;
        if (this.name.equals(s.name) && this.age == s.age) {
            return true;
        } else {
            return false;
        }
    }
複製代碼
//重寫v2.0 (可做爲最終版)
    public boolean equals(Object o) {
        if (this.name == o) {
            return true;
        }
        //測試它左邊的對象是不是它右邊的類的實例,返回 boolean 的數據類型。
        if (!(o instanceof Student)) {
            return false;
        }
        Student s = (Student) o;
        return this.name.equals(s.name) && this.age == s.age;
    }
複製代碼
//idea自動生成版
	@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }
複製代碼
equals() 和 == 的區別

== 的做用:   

基本類型:比較值是否相同   

引用類型:比較的就是堆內存地址是否相同

equals 的做用:

引用類型:默認狀況下,比較的是地址值。

注:通常選擇重寫方法,比較對象的成員變量值是否相同 ,不過通常重寫都是自動生成。

結尾:

若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^

若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創Java技術的公衆號:理想二旬不止

img
相關文章
相關標籤/搜索