
97. 重寫 equals() 時是否適用任何規則?
重寫 equals() 方法時,必須遵守以下規則:-
自反性— 對於任何值x,x.equals(x)必須始終傳回true(其中x != null)。
-
對稱性— 對於任何值x和y,只有當y.equals(x)傳回true時, x.equals(y)才必須回傳true。
-
傳遞性— 對於任何值x、y和z,如果x.equals(y)回傳true且y.equals(z)也回傳true,則x.equals(z)必須傳回true。
-
一致性- 對於任何值x和y ,只要用於比較兩個物件的欄位在每次呼叫之間沒有更改,重複呼叫x.equals(y)將始終傳回相同的值。
-
null 比較— 對於任何值x,呼叫x.equals(null)必須回傳false。
98. 如果不重寫 equals() 和 hashCode() 會發生什麼事?
在這種情況下,hashCode() 將傳回根據儲存物件的記憶體單元的位址所產生的數字。換句話說,當對兩個具有完全相同欄位的物件呼叫原始的hashCode()方法時,結果將會不同(因為它們儲存在不同的記憶體位置)。原來的equals()方法比較引用,即指示引用是否指向同一個物件。換句話說,比較使用==運算符,對於不同的物件它總是傳回false,即使它們的欄位相同。僅當比較對同一物件的參考時才傳回true 。有時不重寫這些方法是有意義的。例如,您希望某個類別的所有物件都是唯一的 - 重寫這些方法只會破壞唯一雜湊碼的現有保證。重要的是要了解這些方法的細微差別(無論是否被覆蓋),並根據情況使用任何方法。99. 為什麼只有x.equals(y)回傳true才符合對稱性要求?
這個問題有點奇怪。如果物件 A 等於物件 B,則物件 B 等於物件 A。如果 B 不等於物件 A,那麼反過來又怎麼可能呢?這是常識。100.什麼是HashCode衝突?你如何解決?
當兩個不同的物件具有相同的HashCode時,就會發生HashCode衝突。這怎麼可能?好吧,哈希碼被映射到一個整數,其範圍從 -2147483648 到 2147483647。也就是說,它可以是大約 40 億個不同整數之一。這個範圍很大,但不是無限的。這意味著在某些情況下,兩個完全不同的物件可能具有相同的雜湊碼。這是極不可能的,但也是有可能的。實作不當的雜湊函數可以透過傳回小範圍內的數字來使相同的雜湊碼更加頻繁,從而增加衝突的機會。為了減少衝突,您需要良好地實現HashCode方法,以均勻地分散值並最大限度地減少重複值的機會。101. 如果參與 hashCode 合約的元素的值發生變化,會發生什麼情況?
如果參與雜湊碼計算的元素發生變化,那麼該物件的雜湊碼也應該發生變化(如果雜湊函數良好)。這就是為什麼您應該使用不可變物件作為HashMap中的鍵,因為它們的內部狀態(欄位)在創建後無法更改。由此可見,它們的雜湊碼在創建後確實會改變。如果你使用可變物件作為鍵,那麼當物件的欄位發生變化時,它的雜湊碼也會改變,你可能會遺失HashMap中對應的鍵值對。畢竟,它會儲存在與原始雜湊碼關聯的儲存桶中,但物件發生變化後,您將在不同的儲存桶中搜尋它。102. 為具有 String name 和 intage 欄位的 Student 類別編寫 equals() 和 hashCode() 方法。
public class Student {
int age;
String name;
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final Student student = (Student) o;
if (this.age != student.age) {
return false;
}
return this.name != null ? this.name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = this.age;
result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
return result;
}
}
等於():
-
首先,我們直接比較引用,因為如果引用指向同一個對象,那麼繼續檢查相等性有什麼意義呢?我們已經知道結果將是真實的。
-
我們檢查 null 以及類別類型是否相同,因為如果參數為 null 或其他類型,則物件不能相等,結果必定為false。
-
我們將參數轉換為相同的類型(畢竟,如果它是父類型的物件怎麼辦)。
-
我們比較原始欄位(使用=!進行比較就足夠了)。如果它們不相等,我們回傳false。
-
我們檢查非原始欄位是否為 null 並使用equals方法(String類別重寫了該方法,因此它將正確執行比較)。如果兩個欄位都為 null,或equals 傳回true,我們將停止檢查,並且該方法傳回true。
-
我們將哈希碼的初始值設定為等於物件的年齡欄位的值。
-
我們將當前雜湊碼乘以 31(以獲得更大的值分佈),然後添加非原始 String 欄位的雜湊碼(如果它不為空)。
-
我們返回結果。
-
以這種方式重寫方法意味著具有相同名稱和int值的物件將始終傳回相同的雜湊碼。
103.使用「if(objinstanceofStudent)」和「if(getClass()==obj.getClass())」有什麼不同?
讓我們看看每個表達式的作用:-
instanceof檢查左側的物件參考是否是右側類型或其子類型之一的實例。
-
“getClass() == ...”檢查類型是否相同。
104. 簡述clone()方法。
clone ()方法屬於Object類別。其目的是創建並返回當前物件的克隆(副本)。
Student implements Cloneable
並重寫clone()方法本身:
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
畢竟,它在Object類別中是受保護的,即它只在Student類別中可見,而對外部類別不可見。
105. 關於物件中的clone()方法和引用變量,需要牢記哪些特殊注意事項?
複製物件時,僅複製原始值和物件參考的值。這意味著,如果一個物件有一個引用另一個物件的字段,那麼只有該引用將被克隆——其他引用的物件將不會被克隆。這就是所謂的淺拷貝。那麼,如果您需要一個完整的副本,其中每個嵌套物件都被克隆,該怎麼辦?如何確保這些不僅僅是引用的副本,而是佔用堆中不同記憶體位址的不同物件的完整副本?實際上,這一切都非常簡單——對於內部引用的每個類,您需要重寫clone()方法並添加Cloneable標記介面。一旦執行此操作,克隆操作將不會複製對現有對象的引用,而是複製引用的對象,因為現在它們也具有複製自身的能力。例外情況
106. 錯誤和異常有什麼差別?
異常和錯誤都是Throwable的子類別。然而,它們也有不同之處。此錯誤表明主要是由於系統資源不足而發生的問題。我們的應用程式不應該看到這些類型的問題。這些錯誤的範例包括系統崩潰和記憶體不足錯誤。錯誤大多發生在運行時,因為它們未經檢查。
107. 檢查、非檢查、異常、拋出和拋出之間有什麼區別?
正如我之前所說,異常是開發人員編寫的程式碼中發生的執行時間或編譯時錯誤(由於某些異常情況)。 檢查是我們所說的異常,方法必須始終使用try-catch機制處理或重新拋出到呼叫方法。throws關鍵字在方法頭中使用,指示該方法可能拋出的異常。換句話說,它為我們提供了一種向呼叫方法拋出異常的機制。 未經檢查的異常不需要處理。它們往往難以預測,而且發生的可能性也較小。也就是說,如果您願意,您可以處理它們。我們在手動拋出異常時使用throw,例如:throw new Exception();
108.什麼是異常層次結構?
異常層次結構非常廣泛。這裡有太多的東西無法充分描述。因此,我們只考慮它的關鍵分支:
- 錯誤——嚴重的、未經檢查的問題。
- 異常——可以檢查的異常。
109.什麼是檢查異常和非檢查異常?
正如我之前所說:-
檢查異常是您必須以某種方式處理的異常。也就是說,您必須在try-catch區塊中處理它們,或將它們扔給上面的方法。為此,在方法簽章中列出方法參數後,使用throws <異常類型>來指示方法可以引發該異常。這有點像是警告,通知呼叫方法它必須承擔處理該異常的責任。
-
未經檢查的異常不需要處理,因為它們在編譯時不會被檢查,通常更難預測。它們與檢查異常的主要區別在於,透過使用try-catch區塊或重新拋出來處理它們是可選的,而不是強制的。
101. 寫一個使用 try-catch 區塊來擷取和處理異常的範例。
try{ // Start of the try-catch block
throw new Exception(); // Manually throw an exception
} catch (Exception e) { // Exceptions of this type and its subtypes will be caught
System.out.println("Oops! Something went wrong =("); // Display the exception
}
102. 編寫一個捕獲並處理您自己的自訂異常的範例。
首先,讓我們編寫自己的異常類,它繼承Exception並重寫其以錯誤訊息作為參數的建構子:public class CustomException extends Exception {
public CustomException(final String message) {
super(message);
}
}
接下來,我們將手動拋出一個並捕獲它,就像我們在上一個問題的範例中所做的那樣:
try{
throw new CustomException("Oops! Something went wrong =(");
} catch (CustomException e) {
System.out.println(e.getMessage());
}
再次,當我們運行程式碼時,我們得到以下輸出:

GO TO FULL VERSION