1. 程式人生 > >Java虛擬機器常見的問題總結(基於深入理解Java虛擬機器)

Java虛擬機器常見的問題總結(基於深入理解Java虛擬機器)

1.Java中欄位的相關屬性有:作用域(包可訪問、類可訪問(private)、公共可訪問(public)、子類可訪問(protected))、例項變數還是類變數(static修飾)、可變性(final)、併發可見性volatile修飾(是否強制從記憶體讀寫)、可否被序列化(transient)、欄位的資料型別、欄位的名稱等等。

2.什麼是Class檔案?
Class檔案就是正確java檔案經過Java虛擬機器編譯子模組正常編譯以後獲得到的.class檔案。而且他還是一組以8位的位元組陣列為基本單位的二進位制留,各個資料專案之間緊湊的排列在Class檔案之中,中間沒有任何的分割符。

3.Java中方法的呼叫過程-靜態分派
public StaticDispatch{
    static class Human{
    }
    static  class Man extends Human{
    }

    static  class Woman extends Human{
    }

    public static void sayHello(Human human){
        System.out.println("this is human say");
    }

    public static void sayHello(Man man){
        System.out.println("this is man say"
); } public static void sayHello(Woman woman){ System.out.println("this is woman say"); } public static void main(String[] args){ Human man=new Man(); Human woman=new Woman(); sayHello(man); sayHello(woman); } }

在這段程式碼中正確的輸出應該是:
this is human say
this is human say
這是因為Java中在呼叫過載方法的時候,虛擬機器會根據變數的編譯型別取決定呼叫過載方法的版本。而不是根據實際型別,因為方法的過載在程式碼的編譯階段就已經確定了,而在編譯階段顯然是沒有執行時的實際型別的,所以只能依靠變數的編譯型別去決定,當然對於匿名物件他的編譯型別肯定就是他的本身嘍。
上面這段程式碼的另外一個考點就是將兩個引數變換為null,那麼這個時候編譯會報錯,因為這個時候的null引數在編譯的時候顯然可以對應多個過載方法,所以產生了二義性,只能編譯報錯了;
第三個考點是如果將傳women引數的那個方法註釋掉我們再次以null在為引數傳遞給sayHello的話那麼就會列印:this is man say
出現這種情況的原因是Java是一種面向具體的語言,顯然以Man作為引數的過載方法比以Human作為引數的過載方法更為具體,因為Man指的範圍更小更具體,而Human的範圍更大。

4.Java中方法的呼叫過程-過載方法匹配優先順序
public class Overload{
public static void sayHello(Object arg){
System.out.println("hello Object");
}
public static void sayHello(int arg){
System.out.println("hello int");
}
public static void sayHello(long arg){
System.out.println("hello long");
}
public static void sayHello(Character arg){
System.out.println("hello Character");
}
public static void sayHello(char arg){
System.out.println("hello char");
}
public static void sayHello(char……arg){
System.out.println("hello char……");
}
public static void sayHello(Serializable arg){
System.out.println("hello Serializable");
}
public static void main(String[]args){
sayHello('a');
}}

上面的程式碼執行後會輸出:hello char
如果我們再把以char為引數的方法註釋掉,那麼會輸出:hello int發生這些情況的原因是‘a’除了可以代表一個字元還可代表一個unicode碼97,因此int引數過載也是正確的

如果我們再把以int為引數的方法註釋掉,會輸出hello long,這時發生了兩次轉型‘a’轉型為97之後機一部轉型為97L;如果方法中還有以float,double為引數的,那麼還未延續這個轉型步驟。按照char->int->long->float->double依次往上轉,但是不會去匹配到short或者是byte型別的方法,因為那樣的轉型是不安全的。

如果我們再把以long為引數的方法註釋掉,會輸出hello charactor這時發生了依次自動的裝箱,‘a’被裝箱為他的包裝型別java.lang.Character

如果我們再把以Charactor為引數的方法註釋掉,hello serializable,這時因為他沒有找到他的包裝型別但是由於Character實現類serializable介面所以他就找到Serializable過載方法,同理如果有Comparable引數方法的話也能找到它,

如果我們再把以Serializable為引數的方法註釋掉,hello object:包裝類和介面都沒有找到那麼就會找他的最上面的基類Object類。實際上他會挨個搜尋他所有的父型別,而且在繼承結構越上的優先順序越低。即使是‘a’換為null也是適用的。

如果我們再把以Object為引數的方法註釋掉,hello char …如果基類都沒找到,他就會去找他的可變長引數的,這個是最低優先順序的