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

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

在 Toto sisi 群組發布
個成員
嘿大家!程式設計充滿陷阱。幾乎沒有一個話題不會讓你絆倒並絆倒腳趾。對於初學者來說尤其如此。拯救腳趾的唯一方法就是學習。特別是,您需要深入研究最基本的主題。今天,我們將繼續回顧 Java 開發人員面試中最常見的問題。這些面試問題很好地涵蓋了基本主題。請注意,該清單還包括一些不那麼標準的問題,讓您能夠以不同的方式處理常見問題。 探索 Java 開發人員職位面試中的問題和答案。 第 7 - 1 部分

62.什麼是字串池,為什麼需要它?

Java 程式可用的記憶體的一部分稱為堆(我們稍後會討論),堆的一部分稱為字串。它用於存儲字串值。換句話說,當您建立字串時,例如使用雙引號,如下所示:
String str = "Hello world";
JVM檢查字串池是否已經有指定的值。如果是,則為str變數指派對池中該值的參考。如果沒有,則會在池中建立一個新值,並將對該值的參考指派給str變數。讓我們考慮一個例子:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true將顯示在螢幕上。請記住,==比較的是引用,這兩個變數指向字串池中的同一個值。這有助於避免在記憶體中產生許多相同的String物件。我們可以這樣做,因為您可能還記得,String是一個不可變的類,因此對同一值進行多個引用並沒有什麼問題。現在,不可能出現更改一個位置的值會導致其他多個引用發生更改的情況。 不過,如果我們使用new建立一個字串:
String str = new String("Hello world");
然後在記憶體中建立一個單獨的物件並儲存指定的字串值(該值是否已在字串池中並不重要)。為了證實這項斷言,請考慮以下內容:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
我們將得到兩行指示false,這意味著我們有三個單獨的字串。基本上,這就是為什麼您應該僅使用雙引號來建立字串的原因。也就是說,即使在使用new關鍵字建立物件時,也可以在字串池中新增(或取得對)值。為此,我們使用 String 類別的intern()方法。此方法確保我們要么在字串池中創建該值,要么獲得對該值的引用(如果該值已存在)。這是一個例子:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
此程式碼向控制台 輸出true三次,這告訴我們所有三個變數都引用記憶體中的相同字串。

63.字串池使用了哪些GoF設計模式?

在字串池中,GoF設計模式是享元模式。如果您在這裡注意到另一種設計模式,請在評論中分享。這裡我們就來談談享元設計模式。它是一種結構設計模式,在程式中的不同位置將自身表示為唯一實例的物件實際上並不唯一。享元透過儲存物件的共享狀態而不是在每個物件中儲存相同的資料來節省記憶體。要理解要點,請考慮這個基本範例。假設我們有一個Employee介面:
public interface Employee {
   void work();
}
它有一些實現,例如Lawyer類:
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
還有一個會計類:
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
這些方法完全是任意的——對於這個例子,我們只需要看到它們正在被執行。構造函數也是如此。控制台輸出告訴我們何時建立新物件。我們還有一個人力資源部門,其任務是返回所請求的員工。如果該員工尚未在職,則會僱用他們,人力資源部門會退回新員工:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
所以,邏輯很簡單:如果想要的物件存在,則傳回它;否則返回。如果沒有,則創建它,將其放入儲存中(類似於快取),然後返回它。現在讓我們看看它是如何運作的:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
這是我們將在控制台中看到的內容:
聘請了一名律師。解決法律問題...聘請了一名會計師。儲存會計記錄...解決法律問題...儲存會計記錄...解決法律問題...儲存會計記錄...解決法律問題...儲存會計記錄...解決法律問題...儲存會計記錄…
正如您所看到的,我們只創建了兩個物件並多次重複使用它們。這個範例非常簡單,但它演示了這種設計模式如何節省我們的資源。您可能已經注意到,這種模式的邏輯與保險池的邏輯非常相似。 探索 Java 開發人員職位面試中的問題和答案。 第 7 - 2 部分

64.我們如何將字串分成幾個部分?給出相關程式碼的例子

顯然,這個問題是關於split的方法。String類別有此方法的兩種變體:
String split(String regex);
String split(String regex);
regex 參數是分隔符號 - 用於將字串拆分為字串數組的一些正規表示式,例如:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
控制台將顯示:
你好,世界,我是阿米戈!
因此,我們的字串被分割成一個字串數組,使用空格作為分隔符號(我們也可以使用普通的字串表達式“”來代替正規表示式“\\s”)。第二種是重載變體,有一個附加的限制參數。 limit是結果陣列允許的最大大小。換句話說,一旦字串被分割成允許的最大數量的子字串,分割就會停止,最後一個元素將包含可能未分割的字串中的任何「剩餘部分」。例子:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
控制台輸出:
你好,世界,我是阿米戈!
正如我們所看到的,如果不是limit = 2,則陣列的最後一個元素可能會被分成三個子字串。

65. 為什麼字元陣列比字串更適合儲存密碼?

儲存密碼時首選數組而不是字串有幾個原因:

1. 字串池和字串不變性。

當使用陣列(char[])時,我們可以在使用完資料後明確刪除資料。我們還可以根據需要覆蓋數組,甚至在垃圾收集之前就從系統中消除密碼(將幾個單元格更改為無效值就足夠了)。相比之下,String是一個不可變的類別。這意味著,如果我們想要更改String物件的值,我們將得到一個新的,但舊的將保留在字串池中。如果我們想刪除包含密碼的字串,我們將面臨一項複雜的任務,因為我們需要垃圾收集器從字串池中刪除該值,但該字串可能會在那裡保留很長時間。也就是說,在安全儲存資料方面,String不如char數組。

2. 如果我們將String值輸出到控制台(或日誌),那麼我們得到:

String password = "password";
System.out.println("Password - " + password);
控制台輸出:
密碼-密碼
如果您碰巧將數組列印到控制台:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
控制台將顯示難以理解的亂碼:
密碼 - [C@7f31245a
事實上,這並不是胡言亂語。以下是如何理解您所看到的內容: [C是類別名稱 -字元數組, @是分隔符,然後 7f31245a是十六進位雜湊碼。

3. 官方 Java 加密體系結構 (JCA) 參考指南明確提到將密碼儲存在char[]而不是String

「在java.lang.String類型的物件中收集和儲存密碼似乎是合乎邏輯的。但是,這裡有一個警告:String類型的物件是不可變的,即沒有定義允許您更改(覆蓋)的方法或在使用後將String的內容清除。此功能使String物件不適合儲存安全敏感訊息,例如使用者密碼。您應該始終在 char 陣列中收集和儲存安全敏感資訊。 探索 Java 開發人員職位面試中的問題和答案。 第 7 - 3 部分

列舉

66.簡述Java中的Enum

Enum是 enumeration 的縮寫,它是一組由公共類型聯合起來的字串常數。我們使用enum關鍵字聲明一個。以下是enum的範例:某些校園中允許的角色:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
大寫字母的單字是枚舉常數。它們以簡化的方式聲明,無需new運算符。使用枚舉使生活變得更加輕鬆,因為它們有助於避免名稱中的錯誤和混淆(因為列表定義了唯一的有效值)。對我來說,它們在開關 結構中非常方便。

67. Enum 可以實作介面(使用implements關鍵字)嗎?

是的。畢竟,枚舉應該不僅僅代表被動集(例如校園中的角色)。在 Java 中,它們可以表示更複雜的對象,因此您可能需要為它們添加額外的功能。這還允許您透過在需要實現的介面類型的地方 替換枚舉值來利用多態性。

68. Enum 可以擴充類別(使用 extends 關鍵字)嗎?

不,不能,因為枚舉是預設Enum<T>類別的子類,其中T是枚舉型別。這無非是Java語言中所有枚舉類型的公共基底類別。從枚舉到類別的轉換是由 Java 編譯器在編譯時完成的。擴展名沒有在程式碼中明確指出,但始終是隱含的。

69. 是否可以建立一個沒有任何物件實例的 Enum?

這個問題有點令人困惑,我不確定我是否完全理解它。我有兩種解釋: 1. 可以有一個沒有任何值的枚舉嗎?是的,當然,但它就像一個空的類別——毫無意義,例如
public enum Role {
}
如果我們調用:
var s = Role.values();
System.out.println(s);
我們在控制台中得到以下資訊:
[Lflyweight.Role;@9f70c54
(角色值 的空數組) 2. 是否可以在不使用new運算子的情況下建立枚舉?是的當然。正如我上面所說,您不要對枚舉值 使用new運算符,因為它們是靜態值。

70. 我們可以重寫Enum的toString()方法嗎?

是的,當然您可以重寫toString()方法,以便定義在呼叫toString方法時如何顯示枚舉(例如, 將枚舉轉換為普通字串時,將其輸出到控制台或日誌)。
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
這就是我今天的全部內容。直到下一部分!
留言
  • 受歡迎
你必須登入才能留言
此頁面尚無留言