本篇文章透過實例對java程式編譯時與運行時進行了詳解,需要的朋友可以參考下
Java編譯時與運行時很重要的概念,但是一直沒有明晰,這次專門部落格寫明白概念.
基礎概念
#編譯時顧名思義就是正在編譯的時候.那啥叫編譯呢?就是編譯器幫你把源代碼翻譯成機器能識別的代碼.(當然只是一般意義上這麼說,實際上可能只是翻譯成某個中間狀態#的語言.例如Java只有JVM識別的字節碼,.另外還有啥連結器.彙編器.為了了便於理解我們可以統稱為編譯器)
那編譯時就是簡單的作一些翻譯工作,比如檢查老兄你有沒有粗心寫錯啥關鍵字了啊.有啥詞法分析,語法分析之類的過程.就像個老師檢查學生的作文中有沒有錯字和病句一樣.如果發現啥錯誤編譯器就告訴你.所以有時有些人說編譯時還分配內存啥的肯定是錯誤的說法.
運行時
所謂運行時就是程式碼跑起來了.被裝載到內存中去了.(你的程式碼保存在磁碟上沒裝入內存之前是個死傢伙.只有跑到內存中才變成活的).而運行時類型檢查就與前面講的編譯時類型檢查(或靜態類型檢查)不一樣.不是簡單的掃描程式碼.而是在記憶體中做些操作,做些判斷.(這樣很多編譯時無法發現的錯誤,在運行就可以發現報錯了,最好還是寫的的時候就避免這個邏輯錯誤就好了)
#舉列子
int arr[] = {1,2,3}; int result = arr[4]; System.out.println(result); Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 4
上面的程式碼你一瞧你知道是錯誤的程式碼,陣列越界了.但用編譯器沒有報錯,run後才出現了ArrayIndexOutOfBoundsException.可見編譯器其實還是挺笨的,還沒你腦瓜子那麼聰明啊,於是你想雖然編譯器笨了點,但運行起來時發現了錯誤也還不算太壞.
#面試題
理解这几个概念可以更好地帮助你去了解一些基本的原理。下面是初学者晋级中级水平需要知道的一些问题。 Q.下面的代码片段中,行A和行B所标识的代码有什么区别呢? 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 } }
A.在行A的程式碼中,product的值是在編譯期計算的,行B則是在執行時計算的。如果你使用Java反編譯器(例如,jd-gui)來反編譯ConstantFolding.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 = 30; int product2 = number3 * number4; } }
常數折疊是種Java編譯器使用的最佳化技術。由於final變數的值不會改變,因此就可以對它們進行最佳化。 Java反編譯器和javap指令都是檢視編譯後的程式碼(例如,字節碼)的利器。
方法重載:這個是發生在編譯時的。方法重載也稱為編譯時多態,因為編譯器可以根據參數的型別來選擇使用哪個方法。
public class { public static void evaluate(String param1); // method #1 public static void evaluate(int param1); // method #2 }
如果編譯器要編譯下面的語句的話:
1evaluate(“My Test Argument passed to param1”);
#它會根據傳入的參數是字串常數,產生呼叫#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; } }
子類別B中的compute(..)方法重寫了父類別的compute(..)方法。如果編譯器遇到下面的程式碼:
public int evaluate(A reference, int arg2) { int result = reference.compute(arg2); }
編譯器是沒辦法知道傳入的參數reference的型別是A還是B。因此,只能夠在運行時,根據賦給輸入變數“reference”的物件的類型(例如,A或B的實例)來決定呼叫方法#3還是方法# 4
泛型(又稱型別檢定):這個是發生在編譯期的。編譯器負責檢查程式中類型的正確性,然後把使用了泛型的程式碼翻譯或重寫成可以執行在目前JVM上的非泛型程式碼。這個技術被稱為「類型擦除」。
換句話說,編譯器會擦除所有在尖括號裡的類型信息,來保證和版本1.4.0或更早版本的JRE的兼容性。
1List myList = new ArrayList(10);
編譯後變成了:
1List myList = new ArrayList( 10);
異常(Exception):你可以使用執行時間異常或編譯時異常。
執行時期異常(RuntimeException)也稱為未偵測的異常(unchecked exception),這表示這種異常不需要編譯器來偵測。
RuntimeException是所有可以在執行時間拋出的例外的父類別。一個方法除要捕獲異常外,如果它執行的時候可能會拋出
RuntimeException的子類,那麼它就不需要用throw語句來宣告拋出的例外。
例如:NullPointerException,ArrayIndexOutOfBoundsException,等等
受檢查異常(checked exception)都是編譯器在編譯時進行校驗的,透過throws語句或try {}cathch{} 語句區塊來處理偵測異常。編譯器會分析哪些異常會在執行一個方法或建構函數的時候拋出。
【相關推薦】
1. Java免費影片教學
#2. 阿里巴巴Java開發手冊
#3. JAVA初級入門影片教學
以上是實例詳解java執行編譯與執行兩種概念的詳細內容。更多資訊請關注PHP中文網其他相關文章!