1. 程式人生 > >java中的編譯時與執行時

java中的編譯時與執行時

----?基礎知識

    -- 編譯時

    編譯器將原始碼翻譯成機器能夠讀懂的程式碼,如java中就是翻譯成jvm能夠讀懂的位元組碼檔案。簡單說,編譯時就是機器幫我們檢查程式碼是否有出現語法錯誤,關鍵字寫錯之類的,是為之後的類載入做好準備,所以,在這個過程中並不會出現什麼分配記憶體之類的操作。

   -- 執行時

    這個過程是指將編譯好後的儲存在磁碟上的位元組碼檔案(.class檔案)加入到記憶體中執行,在執行的過程中,會進行一系列的型別檢查,如空間記憶體分配,邏輯判斷之類的。因此,在這個過程中經常會出現一些我們無法預知的錯誤。

---- 舉個栗子

public class ConstantFolding {
 
 static final int number1 = 5;
 
 static final int number2 = 6;
 
 static int number3 = 5;
 
 static int number4= 6;
 
 public static void main(String[ ] args) {
 
 int product1 = number1 * number2; //line A
 
 int product2 = number3 * number4; //
line B } }

--- 分析

    同時被static和final修飾的常量稱作編譯時常量,所以number1 和 number2在編譯時已經被載入了,即product1 在編譯期間就已經確定好了值為多少。而number3 和number4 只有在執行時,分配好了記憶體空間並且才能被成功賦值,所以product2 的值只有在執行時才能夠確定是多少。反編譯如下:

public class ConstantFolding
{
 static final int number1 = 5;
 static final int
number2 = 6; static int number3 = 5; static int number4 = 6; public static void main(String[ ] args) { int product1 = 30; int product2 = number3 * number4; } }

 

---- 舉個栗子

   方法的過載:這個是發生在編譯時的。方法過載也被稱為編譯時多型,因為編譯器可以根據引數的型別來選擇使用哪個方法。

public class Test{
 public static void A(String param1); // method #1
 public static void A(int param1); // method #2
}

   如果編譯器呼叫的方法是下面

new Test().A("classlodaer");

   那麼它就會在編譯的時候自己去尋找menthod #1的方法

 

---- 舉個栗子

   方法覆蓋:這個是在執行時發生的。方法過載被稱為執行時多型,因為在編譯期編譯器不知道並且沒法知道該去呼叫哪個方法。JVM會在程式碼執行的時候做出決定。

public class A {
 public int compute(int input) { //method #3
 return 3 * input;
 } 
}
 
public class B extends A {
 @Override
 public int compute(int input) { //method #4
 return 4 * input;
 } 
}

   如果編譯器遇到如下程式碼,就在編譯時就無法判斷究竟傳入的引數是A型別還是B型別,只有在執行時才能夠進行確定,進而來判斷要呼叫方法#3還是#4

public int evaluate(A reference, int arg2) {
 int result = reference.compute(arg2);
}

 

---- 舉個栗子

  泛型(又稱型別檢驗):這個是發生在編譯期的。編譯器負責檢查程式中型別的正確性,然後把使用了泛型的程式碼翻譯或者重寫成可以執行在當前JVM上的非泛型程式碼。這個技術被稱為“型別擦除“。

    public class Test4 {
        public static void main(String[] args) {
            ArrayList<String> arrayList1=new ArrayList<String>();
            arrayList1.add("abc");
            ArrayList<Integer> arrayList2=new ArrayList<Integer>();
            arrayList2.add(123);
            System.out.println(arrayList1.getClass()==arrayList2.getClass());
        }
    }

---- 分析

   在這個例子中,我們定義了兩個ArrayList陣列,不過一個是ArrayList<String>泛型型別,只能儲存字串。一個是ArrayList<Integer>泛型型別,只能儲存整形。最後,我們通過arrayList1物件和arrayList2物件的getClass方法獲取它們的類的資訊,最後發現結果為true。說明泛型型別String和Integer都被擦除掉了,只剩下了原始型別

 

---- 舉個栗子

  異常:分為編譯時異常和執行時異常

  執行時異常(RuntimeException)也稱作未檢測的異常(unchecked exception),這表示這種異常不需要編譯器來檢測。RuntimeException是所有可以在執行時丟擲的異常的父類。一個方法除要捕獲異常外,如果它執行的時候可能會丟擲。RuntimeException的子類,那麼它就不需要用throw語句來宣告丟擲的異常。

   例如:NullPointerException,ArrayIndexOutOfBoundsException,等等

  受檢查異常(checked exception)都是編譯器在編譯時進行校驗的,也稱為編譯時異常,通過throws語句或者try{}cathch{} 語句塊來處理檢測異常。編譯器會分析哪些異常會在執行一個方法或者建構函式的時候丟擲。