1. 程式人生 > 其它 >常用類(1):Object類

常用類(1):Object類

常用類:Object

一、API概述(Application Programming Interface)

應用程式程式設計介面
編寫一個機器人程式去控制機器人踢足球,程式就需要向機器人發出向前跑、向後跑、射門、
搶球等各種命令,沒有編過程式的人很難想象這樣的程式如何編寫。但是對於有經驗的開發人員來說,
知道機器人廠商一定會提供一些用於控制機器人的Java類,這些類中定義好了操作機器人各種
動作的方法。其實,這些Java類就是機器人廠商提供給應用程式程式設計的介面,
大家把這些類稱為Xxx Robot API。
本章涉及的Java API指的就是JDK中提供的各種功能的Java類。

二、常用類

Object類/Scanner類
String類/StringBuffer類/StringBuilder類
陣列高階和Arrays類
基本型別包裝類(Integer,Character)
正則表示式(Pattern,Matcher)
Math類/Random類/System類
BigInteger類/BigDecimal類
Date類/DateFormat類/Calendar類

三、Object類概述及其類中的方法

1、Object類概述

Class Object是類Object結構的根;
每個班都有Object作為超類;
所有物件(包括陣列)都實現了這個類的方法;
每個類都直接或者間接的繼承Object類

例:隨便寫個類,都預設繼承Object類
    public class Student extends Object{
    }

2、學習Object中的一個方法:雜湊演算法

public int hashCode()返回物件的雜湊碼值
注意:這裡的雜湊碼值是根據雜湊演算法計算出來的一個值,這個值和地址有關係,
	但是並不是實際的地址值。簡單理解為地址值的另一種表現形式

案例:

//建立一個class檔案,定義一個類
public class Student extends Object{
}

//同一個包下再建立一個class檔案,定義一個類
public class StudentTest {
    public static void main(String[] args) {
        //因為在一個包內,所以能建立Student物件
        Student s = new Student();
        //呼叫Object中的hashCode方法
        System.out.println(s.hashCode());
        //再建立一個Student物件
        Student s1 = new Student();
        System.out.println(s1.hashCode());
    }
}
        執行結果如下:
        356573597
        1735600054

        Process finished with exit code 0
        
  //結論:呼叫hashCode方法時,輸出的雜湊碼值不一定相同

3、學習Object中的一個方法:getClass

public final 類 getClass()
返回此Object的執行時類
返回的類物件是被表示類的static synchronized方法鎖定的物件

案例:

public class StudentTest {
    public static void main(String[] args) {
        //建立一個Student物件
        Student s = new Student();
        System.out.println(s.getClass());
    }
}
        執行結果如下:
            class myself.day18.Student

            Process finished with exit code 0
            //輸出的是當前專案下一個相對路徑的class型別

4、學習Object類中的一個方法:toSring

//建立一個Student2的類
public class Student2 {
    //定義成員變數
    private String name;
    private int age;
    //無參構造方法
    Student2(){
    }
    //帶參構造方法
    Student2(String name,int age){
        this.name = name;
        this.age  = age;
    }
    //定義公共的setXxx()和getXxx()方法
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}

public class StudentTest2 {
    public static void main(String[] args) {
        //建立Student2物件
        Student2 s2 = new Student2();
        System.out.println(s2.toString());//myself.day18.Student2@1540e19d
       //System.out.println(s2.getClass().getName());//myself.day18.Student2
    }
}
            執行結果如下:
                    myself.day18.Student2@1540e19d

                    Process finished with exit code 0
 /*
 s2.toString()在s2.getClass().getName()基礎上多了@1540e19d
 根據toString方法說明:
 			getClass().getName() + '@' + Integer.toHexString(hashCode())
 可知:
 Integer類中有一個toHexString方法將雜湊值轉換成地址值
 */

查詢Integer類中的toHexString方法

Integer類中有一個toHexString方法將雜湊值轉換成地址值
public static String toHexString(int i)
返回整數引數的字串表示形式,作為16位中的無符號整數

因此可知:
	System.out.println(s.toString());
等價於
    System.out.println(s.getClass().getName()+"@"+Integer.toHexString(s.hashCode()));
我們雖然掌握了toString()的方法使用,但是呢列印的結果是一個地址值,拿到地址值是沒有意義的

我們正常列印一個物件,其實是想看該物件中的成員變數的值。
又因為toString()方法的返回值是String型別,並且屬於Object類中的
而Object類是所有類的父類,那麼我們自己定義類的時候,就可以重寫該方法,重寫裡面的實現。
將來呼叫toString()方法的時候,嚴格遵循編譯看左,執行看右,所以呼叫的是重寫後的方法。

在Student2中重寫toString方法

   .......(方便觀看,上方省略)
   public int getAge() {
        return age;
    }
//重寫toString方法
    @Override
    public String toString() {
//        return super.toString();
        return "姓名:" + name + "年齡:" + age;
    }
}

重寫以後再返回StudentTest2類中,再次執行一下

public class StudentTest2 {
    public static void main(String[] args) {
        //建立Student2物件
        Student2 s2 = new Student2();
        System.out.println(s2.toString());
    }
}
        執行結果為:
                姓名:null年齡:0

                Process finished with exit code 0

賦值一下

public class StudentTest2 {
    public static void main(String[] args) {
        //建立Student2物件
        Student2 s2 = new Student2();
        System.out.println(s2.toString());
		//賦值
        s2.setName("偉爺");
        s2.setAge(18);
        System.out.println(s2.toString());
    }
}
        執行結果為:
                姓名:null年齡:0
                姓名:偉爺年齡:18

                Process finished with exit code 0

5、類最終寫法的誕生

通過上述案例,誕生了類的最終寫法

類最終寫法:4.0版本:

             成員變數:private
             構造方法:無參,有參
             成員方法:getXxx()和setXxx(),有特有方法就加上特有方法
		      toString():自動生成即可 快捷鍵	Alt+Insert回車

最終版本案例:

class Student2 {
    //定義成員變數
    private String name;
    private int age;
    //無參構造方法
    Student2(){

    }
    //帶參構造方法
    Student2(String name,int age){
        this.name = name;
        this.age  = age;
    }
    //定義公共的setXxx()和getXxx()方法
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    
  //重寫方法
//Alt+Insert回車,選擇toString()方法,回車再回車
    @Override
    public String toString() {
        return "Student2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class StudentTest2 {
    public static void main(String[] args) {
        //建立Student2物件
        Student2 s2 = new Student2();
        //賦值
        s2.setName("偉爺");
        s2.setAge(18);
        System.out.println(s2.toString());
    }
}
        執行結果為:
        Student2{name='偉爺', age=18}

        Process finished with exit code 0

toString方法總結

如果子類中(學生類)不重寫toString,在測試類中,通過物件呼叫該方法,打印出的是地址值;
重寫之後再呼叫toString,打印出的就是規範的成員變數值

6、學習Object類中的一個方法:equals

public boolean equals(Object obj)指示一些其他物件是否等於此
==:
   比較基本型別:比較的是值是否相同
   比較引用型別:比較的地址值是否相同
equals:
       比較的是引用資料型別

案例:

public class Student3 {
    //定義成員變數
    private String name;
    private int age;
    //無參構造方法
    Student3(){

    }
    //帶參構造方法
    Student3(String name,int age){
        this.name = name;
        this.age  = age;
    }
    //定義公共的setXxx()和getXxx()方法
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }

    //重寫toString()
    @Override
    public String toString() {
        return "Student3{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class StudentTest3{
    public static void main(String[] args) {
        Student3 s3 = new Student3("偉爺",18);
        Student3 s4 = new Student3("偉爺",18);
        System.out.println(s3==s4);//比較的是地址值是否相同:false

        Student3 s5 =s3;//將物件s3賦值給s5物件
        System.out.println(s5==s3);//true

        //用epuals比較引用資料型別
        System.out.println(s3.equals(s4));//比較的是地址值是否相同:false
        System.out.println(s3.equals(s5));//比較的是地址值是否相同:true
        System.out.println(s3.equals(s3));//比較的是地址值是否相同:true
    }
}

我們只知道equals是用來比較引用資料型別的,但是我們卻不知道是內部是如何實現的;

如果想要弄明白一個類中的方法是如何實現的時候,API也沒告訴我們怎麼辦呢?

看原始碼。將滑鼠游標放在方法名上  ctrl+滑鼠左鍵

以 System.out.println(s3.equals(s4));為例
ctrl+滑鼠左鍵
會出現下面程式碼:
   public boolean equals(Object obj) {
        return (this == obj);
    }
分析可知:this指的s3,obj指的是s4

通過觀察原始碼發現:Object類中的equals方法,底層的實現依舊是 ==

而 == 比較引用資料型別比較的是地址值,當地址值不一樣的時候返回的是false

由上可知,比較的都是地址值

如果我們想要比較兩個物件的成員變數值,該怎麼做呢?

由public boolean equals(Object obj)可知,equals是被public修飾的

想要在測試類中比較物件的成員變數值,在學生類中需要重寫equals方法

(不需要我們自己重寫,快捷鍵 Alt+Insert回車 自動生成即可)

重寫equals方法後,再回到測試類。再次執行,呼叫equals比較的就是成員變數值了

public class StudentTest3{
    public static void main(String[] args) {
        Student3 s3 = new Student3("偉爺",18);
        Student3 s4 = new Student3("偉爺",18);
        System.out.println(s3==s4);//比較的是地址值是否相同:false

        Student3 s5 =s3;//將物件s3賦值給s5物件
        System.out.println(s5==s3);//true

        //用epuals比較引用資料型別
        System.out.println(s3.equals(s4));//比較的是地址值是否相同:true
        System.out.println(s3.equals(s5));//比較的是地址值是否相同:true
        System.out.println(s3.equals(s3));//比較的是地址值是否相同:true
    }
}

equals方法總結:

如果子類中(學生類)不重寫equals方法,預設呼叫的是Object類中的equals方法,預設比較的是地址值;
重寫equals方法之後,比較的是物件成員變數值

7、學習Object類中的一個方法:clone

protected Object clone()
            建立並返回此物件的副本

未重寫clone方法之前,呼叫該方法的時候會直接報錯,所以要先在子類(學生類)中重寫克隆方法

重寫該方法與重寫其他方法有點區別,該方法需要通過子類super去呼叫,不能使用Alt+Inset快捷鍵

//重寫clone方法。否則無法呼叫
    //通過子類super去呼叫(重寫該方法不能使用Alt+Inset快捷鍵)
    //輸入c,然後根據提示選擇protected Object clone() {...}
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

重寫clone方法後,再次回到測試類來呼叫該方法

由上可知,重寫clone方法後,在測試類呼叫該方法後,列印輸出的時候,還是會報錯;

這說明並不是什麼樣的類都可以被克隆,類需要滿足一個標記才能被克隆

類該怎麼去標記?

在工具說明書中:
clone的方法Object執行特定的克隆操作。
首先,如果此物件的類不實現介面Cloneable ,則丟擲CloneNotSupportedException
將想要呼叫克隆方法的類實現Cloneable介面
通過觀察API發現,Cloneable介面中沒有成員變數也沒有成員方法
今後看到類似於Cloneable接口裡面什麼都沒有的介面,我們稱之為標記介面

標記介面,只需在類名後面加implements Cloneable即可,表示實現介面

Public class Student4 implements Cloneable{
    //定義成員變數
    private String name;
    private int age;
    //無參構造方法
    Student4(){

    }
    //帶參構造方法
    Student4(String name,int age){
        this.name = name;
        this.age  = age;
    }
    //定義公共的setXxx()和getXxx()方法
    ...(為了方便觀看,暫且省略)

新增指標後,回到測試類再次執行

由上可知,通過呼叫克隆方法,輸出的結果會打印出一模一樣的。

我們可以發現,通過再次建立物件,再次賦值,也可以打印出一模一樣的結果,那麼克隆的意義在哪?

答:當二次賦值的時候,再輸出,結果與第一次是不一樣的;

但是用克隆輸出的時候,無論前面再怎麼賦值,打印出的結果都是和第一次一樣的

拷貝在IT行業中常見兩種:
    淺拷貝
        拷貝的時候,重新建立一個物件,成員變數值和被拷貝的一樣,但是後續修改與原先的沒有關係
    深拷貝
        拷貝的時候,沒有建立新的物件,只是改個名字,地址值都一樣,修改拷貝後的連帶影響到原先的

8、學習Object類中的一個方法:finalize

protected void finalize()
throws Throwable當垃圾收集確定不再有對該物件的引用時,
垃圾收集器在物件上呼叫該物件。
一個子類覆蓋了處理系統資源或執行其他清理的finalize方法。
這個方法簡單理解為就是用來垃圾回收的,什麼時候回收呢?不確定。

具體情況下什麼時候回收呢?自行上網瞭解GC機制(面試會問道)