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 intnumber2 = 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{} 語句塊來處理檢測異常。編譯器會分析哪些異常會在執行一個方法或者建構函式的時候丟擲。