目錄
一.為什么會有Java內存模型?
二.什么樣的情況下finally不會執行
三.鉤子是什么?
四.編譯時期的多態性和運行時期的多態性
五.談談反射機制
六.Java管道
本專欄全是博主自己收集的面試題,僅可參考,不能相信面試官就出這種題目。
一.為什么會有Java內存模型?
????????Java 內存模型存在的原因在于解決多線程環境下并發執行時的內存可見性和一致性問題,保證多線程并發操作的可見性、有序性和原子性。
Java內存模型內容:
- 主內存(Main Memory):所有線程共享的內存區域,包含了對象的字段、方法和運行時常量池等數據。
- 工作內存(Working Memory):每個線程擁有自己的工作內存,用于存儲主內存中的數據的副本,線程只能直接操作工作內存中的數據。
- 內存間交互操作:線程通過讀取和寫入操作與主內存進行交互。讀操作將數據從主內存復制到工作內存,寫操作將修改后的數據刷新到主內存。
- 原子性(Atomicity):JMM 保證基本數據類型(如 int、long)的讀寫操作具有原子性,即不會被其他線程干擾,保證操作的完整性。
- 可見性(Visibility):JMM 確保一個線程對共享變量的修改對其他線程可見。這意味著一個線程在工作內存中修改了數據后,必須將最新的數據刷新到主內存,以便其他線程可以讀取到更新后的數據。
- 有序性(Ordering):JMM 保證程序的執行順序按照一定的規則進行,不會出現隨機的重排序現象。這包括了編譯器重排序、處理器重排序和內存重排序等。
二.什么樣的情況下finally不會執行
????????正常運行的情況下,finally 中的代碼是一定會執行的,但是在某些異常情況下,就不會執行。
1.程序在 try 塊中遇到 System.exit() 方法,會立即終止程序的執行
public class FinallyExample {public static void main(String[] args) {try {System.out.println("執行 try 代碼.");System.exit(0);} finally {System.out.println("執行 finally 代碼.");}}
}
2.在 try 快中遇到 Runtime.getRuntime().halt() 代碼,強制終止正在運行的 JVM。
public class FinallyExample {public static void main(String[] args) {try {System.out.println("執行 try 代碼.");Runtime.getRuntime().halt(0);} finally {System.out.println("執行 finally 代碼.");}}
}
3.程序在 try 塊中遇到無限循環或者發生死鎖等情況時,程序可能無法正常跳出 try 塊,此時 finally 塊中的代碼也不會被執行。
4.編譯器奔潰或者硬件故障(停電等)
三.鉤子是什么?
????????在計算機編程中,"鉤子"(hook)是一種技術或機制,允許程序在特定事件發生時插入自定義代碼或邏輯。這種機制允許程序員在不修改程序源代碼的情況下,改變或擴展程序的行為。
鉤子類型:
-
關閉鉤子(Shutdown Hooks):
- 在Java中,關閉鉤子是一種特殊的鉤子機制,允許開發者注冊在JVM即將關閉之前執行的代碼塊。通過?
Runtime.addShutdownHook(Thread hook)
?方法可以注冊一個線程,在JVM關閉時執行。這種機制通常用于釋放資源、保存狀態或執行清理操作,確保程序在退出前可以進行必要的收尾工作。
- 在Java中,關閉鉤子是一種特殊的鉤子機制,允許開發者注冊在JVM即將關閉之前執行的代碼塊。通過?
-
安裝鉤子:
- 在操作系統和網絡編程中,"安裝鉤子"通常指在系統級別或特定應用程序中安裝的一種回調機制。例如,在操作系統級別,可以安裝鍵盤鉤子來截獲鍵盤輸入事件,或者安裝鼠標鉤子來截獲鼠標事件。這使得程序可以監視或修改輸入事件的行為。
-
編程鉤子:
- 在軟件開發中,"編程鉤子"指的是在代碼中留下的一些特定點,允許開發者插入自定義代碼。例如,一些框架或庫可能會定義鉤子函數(hook functions),允許用戶根據需要擴展框架的功能或修改其行為。
四.編譯時期的多態性和運行時期的多態性
? ? ? ? 編譯時期的多態性:方法重載? ,通過靜態綁定,在編譯階段就確定的具體方法,也稱靜態多態性。
? ? ? ? 運行時期的多態性:方法重寫,依賴于動態綁定,?在子類覆蓋(重寫)父類的方法時。當通過父類引用指向子類對象,并調用被子類重寫的方法時,實際執行的是子類中的方法。
class Animal {void makeSound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void makeSound() {System.out.println("Dog barks");}
}public class TestPolymorphism {public static void main(String[] args) {Animal myAnimal = new Dog(); // 父類引用指向子類對象myAnimal.makeSound(); // 調用被子類重寫的方法}
}
五.談談反射機制
? ? ? ? 反射,可以反射一個對象內的所有方法,還可以獲取所有的屬性值。
通過反射,可以得到實例化對象,也可以使用該類的的所有方法(公共、私有)。
public class User {public String name = "張三";private int age = 18;public void publicMethod() {System.out.println("do public method");}private void privateMethod() {System.out.println("do private method");}public static void staticMethod() {System.out.println("do static method");}
}
獲取對象方法和屬性:
// 1.反射得到對象
Class<?> clazz = Class.forName("User");
// 2.得到方法
Method method = clazz.getDeclaredMethod("publicMethod");
// 3.執行普通方法
method.invoke(clazz.getDeclaredConstructor().newInstance());// 得到私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
// 設置私有方法可訪問
privateMethod.setAccessible(true);
// 執行私有方法
privateMethod.invoke(clazz.getDeclaredConstructor().newInstance());// 得到靜態方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
// 執行靜態方法
staticMethod.invoke(clazz);// 得到公共屬性
Field field = clazz.getDeclaredField("name");
// 得到屬性值
String name = (String) field.get(clazz.getDeclaredConstructor().newInstance());// 得到私有屬性
Field privateField = clazz.getDeclaredField("age");
// 設置私有屬性可訪問
privateField.setAccessible(true);
// 得到屬性值
int age = (int) privateField.get(clazz.getDeclaredConstructor().newInstance());
使用場景:
- 編程開發工具的代碼提示
- Spring 中的依賴注入
- 數據庫連接框架也會使用反射來實現調用不同類型的數據庫
優缺點分析:
優點:靈活性、可擴展性;缺點:性能(獲取類信息慢)、安全(會訪問和修改類的方法和字段)
六.Java管道
? ? ? ? 或許有人沒聽說過,但是 所謂的管道 就是指:輸入流和輸出流之間的連接,用于在程序中傳輸數據。Java的管道可以用于在不同線程之間傳遞數據,或者用于對輸入流進行處理后輸出到輸出流。
Java 管道的主要類和用法
1.?PipedInputStream
?和?PipedOutputStream
- PipedOutputStream:用于向管道發送字節數據。
- PipedInputStream:用于從管道接收字節數據。
import java.io.*;public class PipeExample {public static void main(String[] args) throws Exception {PipedOutputStream pos = new PipedOutputStream();PipedInputStream pis = new PipedInputStream(pos);Thread writerThread = new Thread(() -> {try {pos.write("Hello, Pipe!".getBytes());pos.close();} catch (IOException e) {e.printStackTrace();}});Thread readerThread = new Thread(() -> {try {int data;while ((data = pis.read()) != -1) {System.out.print((char) data);}pis.close();} catch (IOException e) {e.printStackTrace();}});writerThread.start();readerThread.start();}
}
- 創建?
PipedOutputStream
?和?PipedInputStream
?實例,并將它們連接起來。 - 在?
writerThread
?中向?PipedOutputStream
?寫入數據,最后關閉輸出流。 - 在?
readerThread
?中從?PipedInputStream
?讀取數據,并輸出到控制臺,直到讀取結束后關閉輸入流。
2.?PipedReader
?和?PipedWriter
- PipedWriter:用于向管道發送字符數據。
- PipedReader:用于從管道接收字符數據。
import java.io.*;public class PipeExample {public static void main(String[] args) throws Exception {PipedWriter pw = new PipedWriter();PipedReader pr = new PipedReader(pw);Thread writerThread = new Thread(() -> {try {pw.write("Hello, Pipe!".toCharArray());pw.close();} catch (IOException e) {e.printStackTrace();}});Thread readerThread = new Thread(() -> {try {int data;while ((data = pr.read()) != -1) {System.out.print((char) data);}pr.close();} catch (IOException e) {e.printStackTrace();}});writerThread.start();readerThread.start();}
}
注意:
- 線程安全:Java的管道類提供了基本的線程安全性,可以在多線程環境下使用。
- 阻塞特性:當管道中沒有數據可讀時,讀取操作會阻塞;當管道已滿時,寫入操作會阻塞。
- 關閉管道:在使用完畢后,應該及時關閉管道,以釋放資源。