【面試】Java基礎05
【面試】Java基礎05
針對網上提出的常見的Java基礎面試題,在此做下學習筆記,方便後續複習檢視:
注:有些回答可能忘記標出參考出處,侵權請聯絡刪除:-)
- final 修飾 StringBuffer 後還可以 append 嗎?
- Object 的常用方法有哪些?
- final、finally、finalize 的區別?
- finally 塊中的程式碼什麼時候被執行?是不是一定會被執行到?
- try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?try-catch-finally 中那個部分可以省略?
21. final 修飾 StringBuffer 後還可以 append 嗎?
可以。final 修飾的是一個引用變數,那麼這個引用始終只能指向這個物件,但是這個物件內部的屬性是可以變化的。
官方文件解釋:
once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.
一旦一個final修飾的變數被賦值了,它總是包含相同的值。如果一個final變數持有對一個物件的引用,那麼該物件的狀態可以通過對該物件的操作而改變,但該變數將始終引用同一個物件。
22. Object 的常用方法有哪些?
- clone 方法:用於建立並返回當前物件的一份拷貝;
- getClass 方法:用於返回當前執行時物件的 Class;
- toString 方法:返回物件的字串表示形式;
- finalize 方法:例項被垃圾回收器回收時觸發的方法;
- equals 方法:用於比較兩個物件的記憶體地址是否相等,一般需要重寫;
- hashCode 方法:用於返回物件的雜湊值;
- notify 方法:喚醒一個在此物件監視器上等待的執行緒。如果有多個執行緒在等待只會喚醒一個。
- notifyAll 方法:作用跟 notify() 一樣,只不過會喚醒在此物件監視器上等待的所有執行緒,而不是一個執行緒。
- wait 方法:讓當前物件等待;
- .......
23. final、finally、finalize 的區別?
final:final可以用來修飾類,方法和變數(成員變數或區域性變數)
- 修飾類:當用final修飾類的時,表明該類不能被其他類所繼承。(final類中所有的成員方法都會隱式的定義為final方法)
- 修飾方法:把方法鎖定,以防止繼承類對其進行更改。
- 修飾變數:final成員變量表示常量,只能被賦值一次,賦值後其值不再改變。類似於C++中的const。
- 當final修飾一個基本資料型別時,表示該基本資料型別的值一旦在初始化後便不能發生變化;
- 如果final修飾一個引用型別時,則在對其初始化之後便不能再讓其指向其他物件了,但該引用所指向的物件的內容是可以發生變化的。
- 本質上是一回事,因為引用的值是一個地址,final要求值,即地址的值不發生變化。
- final修飾一個成員變數(屬性),必須要顯示初始化。這裡有兩種初始化方式,一種是在變數宣告的時候初始化;第二種方法是在宣告變數的時候不賦初值,但是要在這個變數所在的類的所有的建構函式中對這個變數賦初值。
finally:finally作為異常處理的一部分,它只能用在try/catch語句中,並且附帶一個語句塊,表示這段語句最終一定會被執行(不管有沒有丟擲異常),經常被用在需要釋放資源的情況下。
其實不然,存在finally語句不被執行的情況,見24.
finalize:Object類的一個方法,在垃圾回收時會呼叫被回收物件的finalize。
24. finally 塊中的程式碼什麼時候被執行?是不是一定會被執行到?
finally 塊中的程式碼什麼時候被執行?
在 Java 語言的異常處理中,finally 塊的作用就是為了保證無論出現什麼情況,finally 塊裡的程式碼一定會被執行。由於程式執行 return 就意味著結束對當前函式的呼叫並跳出這個函式體,因此任何語句要執行都只能在 return 前執行(除非碰到 exit 函式),因此 finally 塊裡的程式碼也是在 return 之前執行的。
此外,如果 try-finally 或者 catch-finally 中都有 return,那麼 finally 塊中的 return 將會覆蓋別處的 return 語句,最終返回到呼叫者那裡的是 finally 中 return 的值。
從位元組碼角度對其進行說明:
先看下finally在位元組碼中的體現:
public class Demo3_11_4 { public static void main(String[] args) { int i = 0; try { i = 10; } catch (Exception e) { i = 20; } finally { i = 30; } } }
位元組碼:
Code: stack=1, locals=4, args_size=1 0: iconst_0 1: istore_1 // 0 -> i 2: bipush 10 // try -------------------------------------- 4: istore_1 // 10 -> i | 5: bipush 30 // finally | 7: istore_1 // 30 -> i | 8: goto 27 // return ----------------------------------- 11: astore_2 // catch Exceptin -> e ---------------------- 12: bipush 20 // | 14: istore_1 // 20 -> i | 15: bipush 30 // finally | 17: istore_1 // 30 -> i | 18: goto 27 // return ----------------------------------- 21: astore_3 // catch any -> slot 3 ---------------------- 22: bipush 30 // finally | 24: istore_1 // 30 -> i | 25: aload_3 // <- slot 3 有這個slot3 | 26: athrow // throw 丟擲--------------------------------- 27: return Exception table: from to target type 2 5 11 Class java/lang/Exception 2 5 21 any // 剩餘的異常型別,比如 Error,監測[2, 5)中的其他異常/error 11 15 21 any // 剩餘的異常型別,比如 Error,監測[11, 15)->catch塊中的其他異常/error LocalVariableTable: Start Length Slot Name Signature 12 3 2 e Ljava/lang/Exception; 0 28 0 args [Ljava/lang/String; 2 26 1 i I
可以看到 finally 中的程式碼被複制了 3 份,分別放入 try 流程,catch 流程以及 catch 剩餘的異常型別流程
而當finally程式碼塊中出現了return
public class Demo3_12_1 { public static void main(String[] args) { int result = test(); System.out.println(result); // 20 } public static int test() { try { return 10; } finally { return 20; } } }
位元組碼:
public static int test(); descriptor: ()I flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=2, args_size=0 0: bipush 10 // <- 10 放入棧頂 2: istore_0 // 10 -> slot 0(從棧頂移除了) 3: bipush 20 // < - 20 放入棧頂 5: ireturn // 返回棧頂 int(20) 6: astore_1 // catch any -> slot 1 7: bipush 20 // <- 20 放入棧頂 9: ireturn // 返回棧頂 int(20) Exception table: from to target type 0 3 6 any
- 由於 finally 中的 ireturn 被插入了所有可能的流程,因此返回結果肯定以 finally 的為準
- 至於位元組碼中第 2 行,似乎沒啥用,且留個伏筆,看下個例子:
Demo3_12_2
- 跟上例中的 finally 相比,發現沒有 athrow 了,這告訴我們:如果在 finally 中出現了 return,會吞掉異常!可以試一下下面的程式碼
public class Demo3_12_1 { public static void main(String[] args) { int result = test(); System.out.println(result); // 20 } public static int test() { try { int i = 1/0; // 加不加結果都一樣 return 10; } finally { return 20; } } }
很危險的操作,異常被吞掉了,即使發生了異常也不知道了
finally 對返回值影響
public class Demo3_12_2 { public static void main(String[] args) { int result = test(); System.out.println(result); // 10 } public static int test() { int i = 10; try { return i; } finally { i = 20; } } }
位元組碼:
public static int test(); descriptor: ()I flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=3, args_size=0 0: bipush 10 // <- 10 放入棧頂 2: istore_0 // 10 -> i 3: iload_0 // <- i(10) 4: istore_1 // 10 -> slot1,暫存至slot1,目的是為了固定返回值 ☆ 5: bipush 20 // <- 20 放入棧頂 7: istore_0 // 20 -> i 8: iload_1 // <- slot 1(10) 載入 slot 1 暫存的值 ☆ 9: ireturn // 返回棧頂的 int(10) 10: astore_2 // 期間出現異常,儲存異常到slot2中 11: bipush 20 // <- 20 放入棧頂 13: istore_0 // 20 -> i 14: aload_2 // 異常載入進棧 15: athrow // athrow出異常 Exception table: from to target type 3 5 10 any
參考:
https://www.bilibili.com/video/BV1yE411Z7AP?p=126 ~ https://www.bilibili.com/video/BV1yE411Z7AP?p=128
finally 是不是一定會被執行到?
不一定。下面列舉執行不到的情況:
-
當程式進入 try 塊之前就出現異常時,會直接結束,不會執行 finally 塊中的程式碼;
@Test public void test08(){ int i = 1; i = i/0; try { System.out.println("try"); }finally { System.out.println("finally"); } }
-
當程式在 try 塊中強制退出時也不會去執行 finally 塊中的程式碼,比如在 try 塊中執行 exit 方法
public void test08(){ int i = 1; try { System.out.println("try"); System.exit(0); }finally { System.out.println("finally"); } }
25. try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?try-catch-finally 中那個部分可以省略?
try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?
會。程式在執行到 return 時會首先將返回值儲存在一個指定的位置(23中,對finally的位元組碼中可以體現),其次去執行 finally 塊,最後再返回。
因此,對基本資料型別,在 finally 塊中改變 return 的值沒有任何影響,直接覆蓋掉;
// 下述程式碼返回的結果為1
public static int test(){
String str = "1.1";
int i = 0;
try {
System.out.println("try");
int j = 1/0;
}catch (Exception e){
e.printStackTrace();
System.out.println("catch");
return ++i;
}finally {
System.out.println("finally");
return i;
}
}
而對引用型別是有影響的,返回的是在 finally 對 前面 return 語句返回物件的修改值
// // 下述程式碼返回的結果為def
public static String test02(){
String str = "abc";
try {
System.out.println("try");
int j = 1/0;
}catch (Exception e){
e.printStackTrace();
System.out.println("catch");
return str = "def";
}finally {
System.out.println("finally");
return str;
}
}
try-catch-finally 中那個部分可以省略?
-
catch 可以省略。try 只適合處理執行時異常,try+catch 適合處理執行時異常+普通異常。也就是說,如果你只用 try 去處理普通異常卻不加以 catch 處理,編譯是通不過的,因為編譯器硬性規定,普通異常如果選擇捕獲,則必須用 catch 顯示宣告以便進一步處理。而執行時異常在編譯時沒有如此規定,所以 catch 可以省略,你加上catch 編譯器也覺得無可厚非。
@Test public void test11(){ try { int i = 1/0; }finally { System.out.println("finally"); return; } }
-
finally也可以省略
@Test public void test11(){ try { int i = 1/0; }catch (ArithmeticException e){ e.printStackTrace(); } }