Java入門:創建多個對象


當使用一個類實例化多個對象時,多個對象之間是什么關系?他們各自的數據會不會發生混淆?這次課跟大家講解一下這個問題。學完本次課,大家應該對對象在內存中的表示方式有一個初步的了解,為理解更深入的面向對象概念打一個基礎。

 

編程任務:小明家的狗狗

我們還是通過一個程序任務來理解相關概念。先看看任務描述:小明養了兩只小狗,一只叫旺財,白色,3歲;另一只叫小強,黃色,2歲。我們通過鍵盤向小狗發出指令,當我們輸入旺財的時候,旺財需要做自我介紹。當我們輸入小強的時候,小強要做自我介紹。當我們輸入的既不是旺財,又不是小強的時候,輸出“木有這只小狗”。我們用面向對象的方法來完成這個任務。

 

 創建類和對象

使用面向對象的方法編寫程序,通常包含如下三個核心步驟:

1.創建與待解決問題相關的類

2.定義屬於某類的對象

3.使用new關鍵字實例化該對象

從編程任務的描述我們可以看到。兩只小狗的共同狀態特征是姓名和年齡,行為特征是"自我介紹",因此,可以聲明一個Dog類如下:

第一步:新建一個項目:DemoDogs

第二步:在項目中新增一個類:AppMain,項目的入口主函數main放在該類中。

新建AppMain類

AppMain類代碼如下:

第三步:在項目中新增一個類:Dog,類代碼如下:

public class Dog {
    int age;
    String name;
    
    public void Introduce(){
        System.out.println("我是"+name+",我今年"+age+"歲了");
    }
}

第四步:修改AppMain類的main函數:

public class AppMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Dog dog1 = new Dog();        
        Dog dog2 = new Dog();        
    }
}

 

對象實例化的過程解釋

我們來看一下定義並實例化一個對象的具體過程。Dog dog1 = new Dog();這樣一條語句,可以分成3個步驟來理解。在具體講解之前,我們先要弄清一個事實:在運行程序的時候,數據是放在內存中的。

首先,Dog dog1部分,會在計算機棧內存中分配一塊空間,這塊空間的名稱是dog1,並且dog1的值只能是一個內存地址,該地址里存放的必須是一個Dog對象。

其次,new Dog()部分,會在堆內存里面分配一塊區域(在右邊圖中用兩個橙色方格來表示,白色部分表示的是內存其它區域),假設剛分配到的這塊內存區域的地址是0x1000(至於真實地址是什么,我們就沒必要知道了,cpu自己知道就行了)。這塊內存區域用來存放Dog對象的成員變量,即一個name成員變量和一個age成員變量。 

最后,第三個步驟是賦值操作,賦值操作就是將剛才new出來的Dog對象所在的內存空間地址0x1000賦值給變量dog1。 

請注意:這里將dog1稱為變量,只是為了表述方便。其實dog1所代表的是內存中一個Dog對象,所以以后我們直接稱dog1為對象dog1,或者實例dog1,不再稱為變量。

 

現在我們還沒有給dog1的成員變量賦任何值,也就是說我們只知道dog1是一條狗,但還不知道他叫什么,不知道他幾歲。我們現在在程序里給dog1設置一些數據。 

public class AppMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Dog dog1 = new Dog();
        dog1.name = "旺財";
        dog1.age = 3;
        
        Dog dog2 = new Dog();
        dog2.name = "小強";
        dog2.age = 2;            
    }
}

這里有一點需要說明一下,對象dog1的成員name和age沒有賦過任何值的時候,他們有一個默認值。任何基礎數據類型的變量,如果從來沒有給這個變量賦過值的話,JVM會給變量分配一個默認值。Int類型的默認值是0,string類型默認值是空字符串,空字符串用一對雙引號表示。其它數據類型的默認值我們以后會慢慢接觸到。如果給對象dog1的成員變量賦值,比如dog1.name=“旺財”;默認值就會更改為“旺財“。

 

剛才我們分析的是dog1對象在實例化時成員變量的內存結構,現在我們再分析一下成員方法的內存分配情況。JVM將成員變量存放在堆內存中,成員方法存放在另一塊內存空間中,叫做“方法區” 這塊區域存放了Dog類的信息,其中就包括成員方法。方法區中的成員方法會和堆內存中的成員變量關聯對應起來嗎?當然會,JVM會幫助我們完成這個工作。JVM會按照一定機制將這塊方法區中的方法與堆內存區中的成員變量相互關聯起來,當執行dog1.name = “旺財”這條語句的時候,會將字符串“旺財”送到dog1的”堆內存“區的成員變量name中,當執行dog1.Introduce()這條語句的時候,會調用dog1的方法區中introduce方法。注意:下面這幾條語句都是錯誤的語句,大家想一想為社么錯誤。 

這就是單個對象的實例化過程。這種用圖形的方式描述對象的內存結構,對理解類和對象的一些重要概念有很大幫助,希望大家能夠理解並掌握這種畫圖方法,以后如果遇到難以理解的問題,可以按這個方法自己畫一畫,往往很容易就能幫助我們理清思路。

現在再看看新增dog2對象的內存結構。

dog1對象表示的是堆內存中創建出來的一個Dog對象,成員變量在堆內存,方法在方法區。現在看看新增一個dog2對象時發生了什么。與剛才的分析一樣,棧內存會有一個對象名稱dog2,代表的是在堆內存中創建的另一個Dog對象,成員變量放在堆內存中,這塊成員變量區域與上面dog1的內存區域是兩個不同的區域,dog2的成員變量與dog1的成員變量之間相互獨立,互不影響。這就叫做每個對象都擁有一份成員變量的拷貝。 

然而,dog2的方法區卻和dog1的方法區是共用的。也就是說,introduce方法只有一份,dog1.introduce()語句所調用的代碼和dog2.introduce()語句所調用的代碼是同一份代碼,但是為什么輸出結果不一樣呢?原因是在程序運行過程中,JVM可以根據是由哪個對象發起對introduce方法的調用,方法中所用到的成員變量數據就使用哪個對象的數據,這樣,對象dog1調用introduce(),方法里的name和age用的是dog1的name和p的age,而dog2調用introduce時,方法里的name和age用的就是dog2對象的name和age。

 搞清楚上述概念后,剩余的代碼請讀者自己完成。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM