你好!知識就是力量。在第一次面試之前你掌握的知識越多,你就會越有信心。 有了足夠的知識,你就很難被迷惑,同時你也能夠帶給面試官驚喜。因此,今天,閒話少說,我們將透過檢視Java 開發人員的 250 多個問題來繼續加強您的理論基礎。
103. 繼承中檢查異常的規則是什麼?
如果我正確理解這個問題,他們是在詢問繼承過程中處理異常的規則,如下所示:- 後代/實作中的重寫或實作的方法不能拋出層次結構中比超類別/介面方法中的異常更高的已檢查異常。
public interface Animal {
void voice() throws IOException;
}
在這個介面的實作中,我們不能拋出更通用的拋出例外(例如Exception、Throwable),但我們可以用後代異常來取代它,例如FileNotFoundException:
public class Cat implements Animal {
@Override
public void voice() throws FileNotFoundException {
// некоторая реализация
}
}
- 子類別建構函式必須在其throws 區塊中包含建立物件時所呼叫的超類別建構函式拋出的所有異常類別。
public class Animal {
public Animal() throws ArithmeticException, NullPointerException, IOException {
}
那麼類別繼承人也必須在建構函式中指出它們:
public class Cat extends Animal {
public Cat() throws ArithmeticException, NullPointerException, IOException {
super();
}
或者,與方法的情況一樣,您可以指定不同的異常,而是指定更通用的異常。在我們的例子中,指定一個更通用的異常 - Exception就足夠了,因為這是所考慮的所有三個異常的共同祖先:
public class Cat extends Animal {
public Cat() throws Exception {
super();
}
104. 你能寫出finally區塊何時不執行的程式碼嗎?
首先,讓我們記住finally是什麼。先前,我們研究了捕獲異常的機制:try塊概述了捕獲區域,而catch塊是拋出特定異常時將起作用的程式碼。 finally是finally之後的第三個程式碼區塊,可與catch互換但不互斥。該區塊的本質是,無論try或catch的結果如何(無論是否拋出異常),其中的程式碼始終有效。其失敗的案例非常罕見,而且是不正常的。最簡單的失敗情況是在上面的程式碼中呼叫System.exit(0)方法,該方法終止程式(消滅它):try {
throw new IOException();
} catch (IOException e) {
System.exit(0);
} finally {
System.out.println("Данное сообщение не будет выведенно в консоль");
}
還有一些其他情況finally不起作用:
- 由於嚴重的系統問題而導致程式異常終止,或者出現一些會使應用程式「崩潰」的錯誤(錯誤的一個範例可以是堆疊記憶體已滿時發生的StackOwerflowError )。
- 當守護線程通過ry...finally區塊時,程式將隨之結束。畢竟,守護線程是用於後台操作的線程,也就是說,它不是優先順序和強制的,應用程式不會等待其工作完成。
- 最常見的無限循環,在try或catch中,一旦進入其中,流程將永遠保留在那裡:
try { while (true) { } } finally { System.out.println("Данное сообщение не будет выведенно в консоль"); }
105. 寫一個在一個catch區塊中處理多個異常的範例
1)也許這個問題問錯了。 據我了解,這個問題是指一個try塊的多個捕獲:try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
System.out.print("Упс, у вас упало исключение - " + e);
}
如果try區塊 中發生異常,則catch區塊從上到下交替嘗試捕獲該異常,如果某個catch區塊成功,則它獲得處理異常的權利,而下面的其餘區塊將不再被捕獲。能夠嘗試捕捉它並以自己的方式處理它。因此,較窄的異常被放置在catch塊鏈的較高位置,而較寬的異常則被放置在較低位置。例如,如果在我們的第一個catch 區塊中捕獲了Exception類別的異常,那麼受檢查的異常將無法進入剩餘的區塊(具有Exception後代的剩餘區塊將絕對無用)。 2)問題問得正確, 在這種情況下,我們的處理將如下所示:
try {
throw new NullPointerException();
} catch (Exception e) {
if (e instanceof FileNotFoundException) {
// некоторая обработка с сужением типа (FileNotFoundException)e
} else if (e instanceof ArithmeticException) {
// некоторая обработка с сужением типа (ArithmeticException)e
} else if(e instanceof NullPointerException) {
// некоторая обработка с сужением типа (NullPointerException)e
}
透過catch 捕獲異常後,我們嘗試透過instanceof方法找出其具體類型,該方法用於檢查物件是否屬於某種類型,以便稍後我們可以將範圍縮小到該類型,而不會產生負面後果。這兩種考慮的方法都可以在相同的情況下使用,但我說這個問題是不正確的,因為我不會認為第二個選項很好,並且在我的實踐中從未見過它,而同時第一個帶有多重捕獲的方法已經得到了廣泛的應用關注.傳播.
106. 哪一個運算子允許強制拋出例外?寫一個例子
我已經在上面多次使用過它,但儘管如此,我還是會重複這個關鍵字 - throw。用法範例(強制異常):throw new NullPointerException();
107. main 方法可以拋出 throws 異常嗎?如果是的話,會轉移到哪裡?
首先,我要指出的是,main只不過是一個常規方法,是的,它是由虛擬機器呼叫來開始執行程式的,但除此之外,它還可以從任何其他程式碼中呼叫。也就是說,它也遵循在throws之後指定檢查異常的通常規則:public static void main(String[] args) throws IOException {
相應地,其中也可能出現異常。如果main沒有在某個方法中調用,而是作為程式啟動點啟動,那麼它拋出的異常將由.UncaughtExceptionHandler攔截器處理。該處理程序是每個線程一個(即每個線程中有一個處理程序)。如有必要,您可以建立自己的處理程序並使用Thread物件上呼叫的setDefaultUncaughtExceptionHandler方法來設定它。
多執行緒
108. 您知道哪些用於多執行緒處理的工具?
Java中使用多執行緒的基礎/基礎工具:- 同步是一種當執行緒從其他執行緒進入方法/區塊時關閉(阻止)方法/區塊的機制。
- Volatile是一種確保不同執行緒對變數的存取一致的機制,也就是說,變數上存在此修飾符後,所有賦值和讀取操作都必須是原子的。換句話說,線程不會將該變數複製到本地記憶體並更改它,而是會更改其原始值。
- Runnable是可以在某個類別中實作的介面(特別是它的 run 方法):
public class CustomRunnable implements Runnable {
@Override
public void run() {
// некоторая логика
}
}
建立此類的物件後,您可以透過在新Thread物件的建構函式中設定該物件並呼叫其start()方法來啟動新執行緒:
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
start 方法在單獨的執行緒中執行已實作的run()方法。
- Thread是一個類,繼承自該類別(同時重寫run方法):
public class CustomThread extends Thread {
@Override
public void run() {
// некоторая логика
}
}
透過建立此類的物件並使用start()方法啟動它,我們將啟動一個新執行緒:
new CustomThread().start();
- 並發是一個包含在多執行緒環境中工作的工具的套件。
- 並發集合- 一組專門用於在多執行緒環境中工作的集合。
- 隊列- 用於多執行緒環境(阻塞和非阻塞)的專用佇列。
- 同步器是用於在多執行緒環境中工作的專用實用程式。
- 執行器是創建線程池的機制。
- 鎖定-執行緒同步機制(比標準同步機制更有彈性-synchronized、wait、notify、notifyAll)。
- 原子是針對多執行緒執行而最佳化的類別;每個操作都是原子的。
109.談談線程之間的同步。wait()、notify() - notifyAll() join() 方法的用途是什麼?
據我了解這個問題,線程之間的同步與關鍵修飾符 - synchronized有關。此修飾符可以直接放置在區塊旁邊:synchronized (Main.class) {
// некоторая логика
}
或直接在方法簽名中:
public synchronized void move() {
// некоторая логика}
正如我之前所說,同步是一種機制,允許您在一個執行緒已經進入某個區塊/方法時從其他執行緒關閉該區塊/方法。將塊/方法視為一個房間。一些流到達該房間後,將進入它並鎖定它,其他流到達該房間並看到它已關閉,將在它附近等待,直到它空閒。完成其任務後,第一個線程離開房間並釋放密鑰。我不斷地談論鑰匙也不是無緣無故的,因為它確實存在。這是一個具有忙碌/空閒狀態的特殊物件。該物件附加到每個 Java 對象,因此當使用同步區塊時,我們需要在括號中指出我們想要關閉其互斥鎖的物件:
Cat cat = new Cat();
synchronized (cat) {
// некоторая логика
}
您也可以使用類別互斥體,就像我在第一個範例 ( Main.class ) 中所做的那樣。當我們在方法上使用同步時,我們沒有指定要關閉的對象,對嗎?在這種情況下,對於非靜態方法,它將關閉this物件(即該類別的當前物件)的互斥鎖。靜態的將關閉目前類別的互斥體(this.getClass();)。您可以在此處閱讀有關互斥體的更多資訊。好吧,請在這裡閱讀有關同步的內容。 Wait()是一種釋放互斥體並將目前執行緒置於待機模式的方法,就像附加到目前監視器(類似於錨點)一樣。因此,該方法只能從同步區塊或方法中呼叫(否則,它應該釋放什麼以及它應該期望什麼)。另請注意,這是Object類別的方法。更準確地說,不是一個,而是三個:
-
Wait() - 將目前執行緒置於等待模式,直到另一個執行緒呼叫該物件的notify()或notifyAll()方法(我們稍後將討論這些方法)。
-
wait(長逾時) - 將目前執行緒置於等待模式,直到另一個執行緒呼叫此物件的notify()或notifyAll()方法或指定的逾時到期。
-
wait(long timeout, int nanos) - 與上一個類似,只有nanos允許您指定奈秒(更精確的時間設定)。
-
Notify()是一種允許您喚醒目前同步區塊的一個隨機執行緒的方法。再次強調,它只能在同步區塊或方法中呼叫(畢竟在其他地方它不會有人解凍)。
-
NotifyAll()是喚醒目前監視器上所有等待執行緒的方法(也僅在同步區塊或方法中使用)。
110.如何止流?
首先要說的是,當run()方法完全執行完畢後,執行緒會自動銷毀。但有時你需要在這個方法完成之前提前殺死他。那我們該怎麼辦呢?也許Thread物件應該有一個stop()方法?不管怎麼樣!這種方法被認為已經過時,並且可能導致系統崩潰。 嗯,然後呢?有兩種方法可以做到這一點: 第一種是使用內部布林標誌。讓我們來看一個例子。我們有自己的線程實現,該線程應該在螢幕上顯示某個短語直到完全停止:public class CustomThread extends Thread {
private boolean isActive;
public CustomThread() {
this.isActive = true;
}
@Override
public void run() {
{
while (isActive) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
public void stopRunningThread() {
isActive = false;
}
}
當使用stopRunning()方法時,內部標誌變成 false,run方法停止運作。讓我們在main中運行它:
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
結果,我們會在控制台中看到類似這樣的內容:
程式執行開始 執行緒正在執行某些邏輯... 執行緒正在執行某些邏輯... 執行緒正在執行某些邏輯... 執行緒正在執行某些邏輯... 執行緒正在執行某些邏輯...執行緒正在執行一些邏輯...程式執行結束線程已停止!
這意味著我們的線程工作了,向控制台輸出了一定數量的消息並成功停止。我注意到,每次運行時輸出的訊息數量都會有所不同;有時附加執行緒甚至沒有輸出任何內容。正如我注意到的,這取決於主線程的睡眠時間,時間越長,附加線程不輸出任何內容的機會就越小。當睡眠時間為 1ms 時,訊息幾乎不會輸出,但如果將其設定為 20ms,則幾乎總是有效。也許,當時間很短時,線程根本沒有時間啟動並開始其工作,並立即停止。 第二種方法是使用Thread物件上的interrupted()方法,該方法傳回內部中斷標誌的值(該標誌預設為假)和它的另一個interrupt()方法,該方法將該標誌設為true(當此標誌為true時)標誌為true則線程應該停止其工作)。讓我們來看一個例子:
public class CustomThread extends Thread {
@Override
public void run() {
{
while (!Thread.interrupted()) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
}
在main 中運行:
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
執行結果將與第一種情況相同,但我更喜歡這種方法:我們編寫更少的程式碼並使用更多現成的標準功能。 這就是我們今天要停下來的地方。
GO TO FULL VERSION