CodeGym /Java Blog /Toto sisi /探索 Java 開發人員職位面試中的問題和答案。第12部分
John Squirrels
等級 41
San Francisco

探索 Java 開發人員職位面試中的問題和答案。第12部分

在 Toto sisi 群組發布
你好!知識就是力量。您在第一次面試中掌握的知識越多,您就會越有信心。 探索 Java 開發人員職位面試中的問題和答案。 第 12 - 1 部分如果你有一個充滿知識的大腦,你的面試官會發現很難讓你困惑,並且很可能會給你帶來驚喜。因此,言歸正傳,今天我們將透過複習 Java 開發人員的問題來繼續加強您的理論基礎。

103. 繼承過程中的異常檢查適用哪些規則?

如果我正確理解這個問題,他們會問繼承過程中處理異常的規則。相關規則如下:
  • 後代/實作中的重寫或實作的方法不能拋出在層次結構中比超類別/介面方法中的異常更高的已檢查異常。
例如,假設我們有一些Animal接口,其方法會拋出IOException
public interface Animal {
   void speak() throws IOException;
}
在實作該介面時,我們無法暴露出更通用的可拋出異常(例如ExceptionThrowable),但我們可以用子類別取代現有異常,例如FileNotFoundException
public class Cat implements Animal {
   @Override
   public void speak() throws FileNotFoundException {
// Some implementation
   }
}
  • 子類別建構子的throws子句必須包含為建立物件而呼叫的超類別建構子所拋出的所有異常類別。
假設Animal類別的建構子拋出了很多異常:
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關鍵字標記的第三個程式碼區塊可以取代catch 區塊或位於catch 區塊之後。該區塊背後的想法是,無論trycatch區塊中發生什麼(無論是否存在異常),它的程式碼總是會被執行。不執行該區塊的情況並不常見,並且屬於異常情況。最簡單的例子是在finally區塊之前呼叫System.exit(0),從而終止程式:
try {
   throw new IOException();
} catch (IOException e) {
   System.exit(0);
} finally {
   System.out.println("This message will not be printed on the console");
}
還有一些其他情況導致finally區塊不會運作:
  • 例如,嚴重系統錯誤導致的程式異常終止,或導致應用程式崩潰的錯誤(例如,當應用程式堆疊溢位時發生StackOverflowError )。

  • 另一種情況是守護線程進入try-finally區塊,但隨後程式的主執行緒終止。畢竟,守護線程用於非高優先級或強制性的後台工作,因此應用程式不會等待它們完成。

  • 最愚蠢的例子是trycatch區塊內的無限循環 - 一旦進入,線程將永遠卡在那裡:

    try {
       while (true) {
       }
    } finally {
       System.out.println("This message will not be printed on the console");
    }
這個問題在初級開發人員面試中很常見,因此最好記住一些特殊情況。 探索 Java 開發人員職位面試中的問題和答案。 第 12 - 2 部分

105. 寫一個在單一 catch 區塊中處理多個異常的範例。

1)我不確定這個問題是否正確提出。 據我了解,這個問題指的是幾個catch區塊和一個try
try {
  throw new FileNotFoundException();
} catch (FileNotFoundException e) {
   System.out.print("Oops! There was an exception: " + e);
} catch (IOException e) {
   System.out.print("Oops! There was an exception: " + e);
} catch (Exception e) {
   System.out.print("Oops! There was an exception: " + e);
}
如果在try區塊 中引發異常,則關聯的catch區塊將嘗試按從上到下的順序擷取該異常。一旦異常與其中一個catch塊匹配,任何剩餘的塊將不再能夠捕獲和處理它。這一切都意味著在catch塊集中,較窄的異常排列在較通用的異常之上。例如,如果我們的第一個catch區塊捕獲了Exception類,那麼任何後續區塊都不會捕獲已檢查的異常(也就是說,具有Exception子類別的剩餘區塊將完全無用)。 2)或者也許這個問題問得正確。 在這種情況下,我們可以如下處理異常:
try {
  throw new NullPointerException();
} catch (Exception e) {
   if (e instanceof FileNotFoundException) {
       // Some handling that involves a narrowing type conversion: (FileNotFoundException)e
   } else if (e instanceof ArithmeticException) {
       // Some handling that involves a narrowing type conversion: (ArithmeticException)e
   } else if(e instanceof NullPointerException) {
       // Some handling that involves a narrowing type conversion: (NullPointerException)e
   }
使用catch捕獲異常後,我們嘗試使用instanceof運算子來發現其特定類型,該運算子檢查物件是否屬於某種類型。這使我們能夠自信地執行縮小類型轉換,而不必擔心負面後果。我們可以在相同的情況下應用任一方法。我對這個問題表示懷疑只是因為我不認為第二個選項是個好方法。根據我的經驗,我從未遇到過這種情況,第一種涉及多個 catch 區塊的方法很普遍。

106.哪個運算子可以讓你強制拋出例外?寫一個例子

我已經在上面的範例中多次使用過它,但我將再次重複它:throw關鍵字。手動拋出異常的範例:
throw new NullPointerException();

107. main方法可以拋出異常嗎?如果是這樣,那麼它去哪裡了?

首先,我要說明的是,main方法只不過是一個普通的方法。是的,它由虛擬機器呼叫來開始執行程序,但除此之外,還可以從任何其他代碼呼叫它。這意味著它也遵循有關在throws關鍵字後指示已檢查異常的通常規則:
public static void main(String[] args) throws IOException {
因此,它可以拋出異常。當main被呼叫作為程式的起點(而不是透過其他方法)時,它拋出的任何異常都將由UncaughtExceptionHandler 處理。每個線程都有一個這樣的處理程序(即每個線程中有一個這樣的處理程序)。如有必要,您可以建立自己的處理程序並透過呼叫 public static void main(String[] args) throws IOException {setDefaultUncaughtExceptionHandler 方法來設定它 public static void main(String[] args) throws IOException {Thread 物件。

多執行緒

探索 Java 開發人員職位面試中的問題和答案。 第 12 - 3 部分

108. 您知道哪些在多執行緒環境中工作的機制?

Java中多執行緒的基本機制是:
  • Synchronized關鍵字,是執行緒進入時鎖定方法/區塊的一種方式,阻止其他執行緒進入

  • 易失性關鍵字確保對不同執行緒存取的變數的存取一致。也就是說,當將此修飾符應用於變數時,分配和讀取該變數的所有操作都將成為原子操作。換句話說,線程不會將變數複製到本地記憶體並更改它。它們會改變它的原始值。

  • Runnable-我們可以在某個類別中實作這個介面(它是由一個run()方法所組成):

    public class CustomRunnable implements Runnable {
       @Override
       public void run() {
           // Some logic
       }
    }

    一旦我們創建了該類別的對象,我們就可以透過將對象傳遞給Thread構造函數然後呼叫start()方法來啟動一個新線程:

    Runnable runnable = new CustomRunnable();
    new Thread(runnable).start();

    start方法在單獨的執行緒上執行已實作的run()方法。

  • Thread — 我們可以繼承這個類別並重寫它的run方法:

    public class CustomThread extends Thread {
       @Override
       public void run() {
           // Some logic
       }
    }

    我們可以透過建立此類別的物件然後呼叫start()方法來啟動一個新執行緒:

    new CustomThread().start();

  • 並發性-這是用於在多執行緒環境中工作的工具包。

    它包括:

    • 並發集合-這是為在多執行緒環境中工作而明確建立的集合的集合。

    • 佇列-多執行緒環境的專用佇列(阻塞和非阻塞)。

    • 同步器-這些是在多執行緒環境中工作的專用實用程式。

    • 執行器——創建線程池的機制。

    • 鎖定-比標準同步機制(synchronized、wait、notify、notifyAll)更靈活的執行緒同步機制。

    • 原子- 針對多執行緒優化的類別。他們的每個操作都是原子的。

109. 告訴我們線程之間的同步。wait()、notify()、notifyAll() 和 join() 方法的用途是什麼?

執行緒之間的同步與synchronized關鍵字有關。此修飾符可以直接放置在區塊上:
synchronized (Main.class) {
   // Some logic
}
或直接在方法簽名中:
public synchronized void move() {
   // Some logic }
正如我之前所說,同步是一種一旦一個執行緒進入就將區塊/方法鎖定到其他執行緒的機制。讓我們將程式碼區塊/方法視為一個房間。一些線程接近房間,進入房間,並用鑰匙鎖上門。當其他線程接近房間時,它們會看到門被鎖住並在附近等待,直到房間可用。一旦第一個線程完成了房間中的事務,它就會打開門,離開房間,並釋放鑰匙。我多次提到某個鍵是有原因的——因為類似的東西確實存在。這是一個具有忙碌/空閒狀態的特殊物件。Java中的每個物件都有這樣一個對象,因此當我們使用synchronized區塊時,我們需要使用括號來指示互斥體將被鎖定的物件:
Cat cat = new Cat();
synchronized (cat) {
   // Some logic
}
我們也可以使用與類別關聯的互斥鎖,就像我在第一個範例 ( Main.class ) 中所做的那樣。畢竟,當我們在方法上使用同步時,我們沒有指定要鎖定的對象,對嗎?在這種情況下,對於非靜態方法,將被鎖定的互斥量是this對象,即類別的當前對象。對於靜態方法,與目前類別 ( this.getClass(); )關聯的互斥鎖被鎖定。 wait()是一種釋放互斥鎖並將當前執行緒置於等待狀態的方法,就像附加到目前監視器(類似於錨點)一樣。因此,只能從同步區塊或方法中呼叫該方法。否則,它會等待什麼並釋放什麼?)。另請注意,這是Object類別的方法。好吧,不是一個,而是三個:
  • wait()將目前執行緒置於等待狀態,直到另一個執行緒呼叫該物件的notify()notifyAll()方法(我們稍後會討論這些方法)。

  • wait(long timeout)將目前執行緒置於等待狀態,直到另一個執行緒呼叫該物件的notify()notifyAll()方法或timeout指定的時間間隔到期。

  • wait(long timeout, int nanos)與前面的方法類似,但這裡nanos允許您指定奈秒(更精確的超時)。

  • notify()可以讓您喚醒一個在目前同步區塊上等待的隨機執行緒。再次強調,這個方法只能在同步區塊或方法中呼叫(畢竟在其他地方就沒有人會被喚醒)。

  • notifyAll()喚醒目前監視器上等待的所有執行緒(也僅在同步區塊或方法中使用)。

110.我們如何停止一個執行緒?

這裡首先要說的是,當run()運行完成時,執行緒會自動終止。但有時我們想在方法完成之前提前終止執行緒。那我們該怎麼辦?也許我們可以在Thread物件上使用stop()方法?沒有!該方法已被棄用,可能會導致系統崩潰。 嗯,然後呢?有兩種方法可以做到這一點: 首先,使用其內部布林標誌。讓我們來看一個例子。我們實作了一個線程,該線程應該在螢幕上顯示某個短語,直到線程完全停止: 探索 Java 開發人員職位面試中的問題和答案。 第 12 - 4 部分
public class CustomThread extends Thread {
private boolean isActive;

   public CustomThread() {
       this.isActive = true;
   }

   @Override
   public void run() {
       {
           while (isActive) {
               System.out.println("The thread is executing some logic...");
           }
           System.out.println("The thread stopped!");
       }
   }

   public void stopRunningThread() {
       isActive = false;
   }
}
呼叫stopRunningThread()方法將內部標誌設為 false,導致run()方法終止。讓我們在main中調用它:
System.out.println("Program starting...");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// As long as our main thread is asleep, our CustomThread runs and prints its message on the console
thread.stopRunningThread();
System.out.println("Program stopping...");
結果,我們會在控制台中看到類似這樣的內容:
程式開始... 線程正在執行一些邏輯... 線程正在執行一些邏輯... 線程正在執行一些邏輯... 線程正在執行一些邏輯... 線程正在執行一些邏輯...線程正在執行一些邏輯...程式停止...線程停止!
這意味著我們的線程啟動了,在控制台上列印了幾條訊息,然後成功停止。請注意,顯示的訊息數量因啟動而異。有時輔助線程可能根本不顯示任何內容。具體行為取決於主執行緒休眠的時間。它休眠的時間越長,輔助線程無法顯示任何內容的可能性就越小。如果睡眠時間為 1 毫秒,您幾乎看不到這些訊息。但如果將其設為 20 毫秒,幾乎總是會顯示訊息。當睡眠時間很短時,線程根本沒有時間啟動並完成其工作。相反,它會立即停止。第二種方法是使用Thread物件的 Interrupted ( )方法。它會傳回內部中斷標誌的值,預設為false 。或它的Interrupt()方法,該方法將此標誌設為true(當標誌為true時,執行緒應該停止運行)。讓我們來看一個例子:
public class CustomThread extends Thread {

   @Override
   public void run() {
       {
           while (!Thread.interrupted()) {
               System.out.println("The thread is executing some logic...");
           }
           System.out.println("The thread stopped!");
       }
   }
}
在main 中運行:
System.out.println("Program starting...");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Program stopping...");
運行的結果與第一種情況相同,但我更喜歡這種方法:我們編寫了更少的程式碼並使用了更多現成的標準功能。好了,今天就到此為止!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION