1. 程式人生 > >深入理解JAVA虛擬機器——JVM的資料型別(以及按引用傳遞法則)

深入理解JAVA虛擬機器——JVM的資料型別(以及按引用傳遞法則)

Java虛擬機器是通過某些資料型別來執行計算的,資料型別可以分為兩種:基本型別和引用型別,基本型別的變數持有原始值,而引用型別的變數持有引用值。


    Java語言中的所有基本型別同樣也都是Java虛擬機器中的基本型別。但是boolean有點特別,雖然Java虛擬機器也把boolean看做基本型別,但是指令集對boolean只有很有限的支援,當編譯器把Java原始碼編譯為位元組碼時,它會用int或者byte來表示boolean。在Java虛擬機器中,false是由整數零來表示的,所有非零整數都表示true,涉及boolean值的操作則會使用int。另外,boolean陣列是當做byte陣列來訪問的,但是在“堆”區,它也可以被表示為位域

  Java虛擬機器還有一個只在內部使用的基本型別:returnAddress,Java程式設計師不能使用這個型別,這個基本型別被用來實現Java程式中的finally子句。該型別是jsr, ret以及jsr_w指令需要使用到的,它的值是JVM指令的操作碼的指標。returnAddress型別不是簡單意義上的數值,不屬於任何一種基本型別,並且它的值是不能被執行中的程式所修改的。

  Java虛擬機器的引用型別被統稱為“引用(reference)”,有三種引用型別:類型別、介面型別、以及陣列型別,它們的值都是對動態建立物件的引用。類型別的值是對類例項的引用;陣列型別的值是對陣列物件的引用,在Java虛擬機器中,陣列是個真正的物件

;而介面型別的值,則是對實現了該介面的某個類例項的引用。還有一種特殊的引用值是null,它表示該引用變數沒有引用任何物件。

深入理解引用型別在Java中的使用

public class User {
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}
public class Test {
    
    public void set(User user){
        user.setName("hello world");
    }
    
    public static void main(String[] args) {
        
        Test test = new Test();
        User user = new User();
        test.set(user);
        System.out.println(user.getName());
    }
}

上面程式碼的輸出結果是“hello world”,這不必多說,那如果將set方法改為如下,結果會是多少呢?

public void set(User user){
        user.setName("hello world");
        user = new User();
        user.setName("change");
    }

答案依然是“hello world”,下面就讓我們來分析一下如上程式碼。

  首先

User user = new User();
是在堆中建立了一個物件,並在棧中建立了一個引用,此引用指向該物件,如下圖:

test.set(user);

    是將引用user作為引數傳遞到set方法,實際上專業操作名稱就是:按引用傳遞,注意:這裡傳遞的並不是引用本身,而是一個引用的拷貝。也就是說這時有兩個引用(引用和引用的拷貝)同時指向堆中的物件,如下圖:


user.setName("hello world");
在set()方法中,“user引用的拷貝”操作堆中的User物件,給name屬性設定字串"hello world"。如下圖:


user = new User();

在set()方法中,又建立了一個User物件,並將“user引用的拷貝”指向這個在堆中新建立的物件,如下圖:


user.setName("change");

 在set()方法中,“user引用的拷貝”操作的是堆中新建立的User物件。


set()方法執行完畢,目光再回到mian()方法

System.out.println(user.getName());

    因為之前,"user引用的拷貝"已經將堆中的User物件的name屬性設定為了"hello world",所以當main()方法中的user呼叫getName()時,列印的結果就是"hello world"。如下圖: