在前兩篇文章中,我們討論了面試中最常被問到的一些重要問題。是時候繼續討論剩下的問題了。
深複製和淺複製
原件的精確副本是它的克隆。在Java中,這意味著能夠建立與原始物件具有相似結構的物件。該方法clone()
提供了此功能。淺複製複製盡可能少的信息。預設情況下,Java 中的克隆是淺克隆,即 Object class
不知道它正在複製的類別的結構。克隆時,JVM 執行以下操作:
- 如果一個類別僅具有基本類型的成員,則會建立該物件的全新副本,並傳回對該物件的參考。
- 如果一個類別不僅包含基本類型的成員,還包含任何其他類別類型,則複製對這些類別的物件的參考。因此,兩個物件將具有相同的引用。
- 無需單獨複製原始資料;
- 原始類別中的所有成員類別都必須支援克隆。
super.clone()
對於每個類別成員,在方法被重寫時必須呼叫clone()
; - 如果類別的任何成員不支援克隆,則在克隆方法中,您需要建立該類別的一個新實例,並將其每個成員及其所有屬性複製到一個新的類別對象,一次一個。
什麼是同步?物件級鎖定和類別級鎖定?
同步是指多執行緒。同步程式碼區塊一次只能由一個執行緒執行。Java 可讓您同時處理多個執行緒。這可能會導致兩個或多個執行緒想要存取相同欄位。同步有助於避免記憶體資源使用不當時發生的記憶體錯誤。當一個方法被宣告為同步時,執行緒將持有其監視器。如果此時另一個執行緒嘗試存取同步方法,則該執行緒將被阻塞並等待監視器釋放。Java 中的同步是透過特殊的synchronized關鍵字來完成的。您可以透過這種方式標記類別中的各個區塊或方法。Synchronized 關鍵字不能與類別變數或屬性一起使用。 當您想要同步非靜態方法或非靜態程式碼區塊以便只有一個執行緒可以在給定的類別實例上執行程式碼區塊時,物件級鎖定是一種機制。應該始終這樣做以使類別實例線程安全。 類別級鎖定可防止多個執行緒進入該類別的所有可用實例的同步區塊。例如,如果 DemoClass 類別有 100 個實例,則在給定時間只有 1 個執行緒能夠使用其中一個變數來執行 demoMethod()。應始終這樣做以確保靜態線程安全。 在此處了解有關同步的更多資訊。sleep() 和 wait() 有什麼差別?
Sleep()
是一種用於將進程延遲幾秒鐘的方法。在 的情況下wait()
,執行緒處於等待狀態,直到我們呼叫notify()
or方法notifyAll()
。主要區別在於wait()
它釋放監視器鎖而不sleep()
釋放鎖。Wait()
用於多執行緒應用程序,sleep()
僅用於暫停執行緒執行。 Thread.sleep()
將目前執行緒置於「不可運行」狀態一段時間。該執行緒保存呼叫此方法之前監視器的狀態。如果另一個線程調用t.interrupt()
,“睡著”的線程將被喚醒。請注意,這sleep()
是一個靜態方法,這意味著它始終影響當前執行緒(執行該方法的執行緒sleep()
)。一個常見的錯誤是呼叫t.sleep()
where t
is another thread; 即使調用該方法的當前線程sleep()
不是t
線程。 Object.wait()
將當前線程發送到“不可運行”狀態一段時間,就像 一樣sleep()
,但有一些細微差別。Wait()
呼叫對象,而不是線程;我們稱這個物件為「鎖對象」。在呼叫之前lock.wait()
,當前執行緒必須與“鎖對象”同步;wait()
之後,它釋放該鎖,並將該執行緒新增至與該鎖關聯的「等待清單」。稍後,另一個執行緒可以與同一個鎖物件同步並呼叫lock.notify()
. 該方法將「喚醒」仍在等待的原始執行緒。原則上,wait()
/可以與/notify()
進行比較,只是活動線程不需要直接指向休眠線程的指針,它只需要知道共享鎖定對象。 請閱讀此處的詳細差異。sleep()
interrupt()
是否可以將 null 指派給引用變數?
你不能。在Java中,賦值運算子的左邊必須是變數。「This」是一個特殊的關鍵字,它總是給出類別的當前實例。這不僅僅是任何變數。同樣,不能使用“super”關鍵字或任何其他類似關鍵字將 null 分配給變數。&& 和 & 有什麼不同?
&
- 按位元和&&
- 邏輯。
&
評估操作的雙方;&&
評估操作的左側。如果為真,它將繼續評估右側。
如何重寫 equals() 和 hachCode() 方法?
hashCode()
和equals()
方法在類別中定義Object
,該類別是 Java 物件的父類別。因此,所有 Java 物件都會繼承方法的預設實作。此方法hashCode()
用於取得給定物件的唯一整數。當需要儲存某個物件時,該整數用於確定該物件的位置,例如儲存到HashTable
。預設情況下,hashCode()
傳回integer
儲存物件的記憶體位置的位址表示形式。equls()
顧名思義,該方法用於簡單地測試兩個物件是否相等。預設實作檢查物件引用以查看它們是否相等。以下是重新載入這些方法的重要指南:
hashCode()
生成和時始終使用相同的物件屬性equals()
;- 對稱。那些。
x
如果它對某些物件傳回 truey
x.equals(y)
,那麼它y.equals(x)
應該會傳回 true; - 反身性。對於任何物件都
x
x.equals(x)
必須傳回true; - 一致性。對於任何對象,如果比較中使用的資訊沒有改變,則返回相同的東西
x
;y
x.equals(y)
- 傳遞性。對於任何物件
x
,y
和z
,如果x.equals(y)
它傳回 true 並且y.equals(z)
傳回 true,那麼它x.equals(z)
應該傳回 true; - 每當在應用程式執行期間對相同物件呼叫方法時,它都應該傳回相同的數字,除非使用的資訊發生變化。
hashCode
可以在不同的應用程式實例中為相同的物件傳回不同的值; - 如果兩個物件相等,根據
equals
,那麼它們hashCode
必須傳回相同的值; - 相反的要求是可選的。兩個不相等的物件可以傳回相同的 hashCode。但是,為了提高效能,最好讓不同的物件傳回不同的程式碼。
告訴我們有關存取修飾符的信息
Java 類別、欄位、建構函式和方法可以有四種不同的存取修飾符之一: private 如果方法或變數被標記為private,則只有同一類別中的程式碼可以存取該變數或呼叫該方法。子類別內的程式碼不能存取變數或方法,也不能從任何其他類別存取它。private 存取修飾符最常用於建構函式、方法和變數。 default如果根本沒有指定修飾符,則宣告預設存取修飾符 。此修飾符意味著對給定類別的欄位、建構函式和方法的存取可以透過類別本身內部的程式碼、同一包中的類別內部的程式碼來取得。如果子類別宣告為default,則子類別無法存取超類別的方法和成員變量,除非子類別與超類別位於同一套件中。 protected protected 修飾符的作用與default相同,只不過子類別也可以存取超類別的 protected 方法和變數。即使子類別與超類別不在同一個套件中,這種說法也是正確的。 public public 存取修飾符意味著所有程式碼都可以存取該類別、其變數、建構子或方法,無論程式碼位於何處。什麼是垃圾收集器?我們可以打電話給他嗎?
垃圾收集是許多現代程式語言中自動記憶體管理的特性,例如Java和.NET.Framework中的語言。使用垃圾收集的語言通常在 JVM 等虛擬機器中解釋垃圾收集。垃圾收集有兩個目的:應釋放任何未使用的內存,如果程式仍在使用內存,則不應釋放內存。您可以手動運行垃圾收集嗎?不,System.gc()
它為您提供盡可能多的訪問權限。最好的選擇是呼叫該方法System.gc()
,該方法將提示垃圾收集器它需要運行。由於垃圾收集器是不確定的,因此無法立即運行它。另外,根據文檔,OutOfMemoryError
如果虛擬機器在完全垃圾收集後未能釋放內存,則不會轉發。 在此處了解有關垃圾收集器的更多資訊。
原生關鍵字是什麼意思?詳細解釋
native關鍵字用於指示該方法是用Java檔案以外的程式語言實現的。 過去曾使用本機方法。在目前版本的 Java 中,很少需要這樣做。目前,在以下情況下需要本機方法:- 您必須從 Java 呼叫用另一種語言編寫的函式庫。
- 您需要存取只能使用另一種語言(通常是 C)存取的系統或硬體資源。事實上,許多與真實電腦互動的系統函數(例如磁碟或網路資料)只能透過本機方法來呼叫。
- JNI/JNA 可能會破壞 JVM 的穩定性,尤其是當您嘗試執行複雜的操作時。如果您的本機方法發生錯誤,則 JVM 可能會崩潰。此外,如果從多個執行緒呼叫本機方法,可能會發生不好的事情。等等。
- 使用本機程式碼調試程式更加困難。
- 本機程式碼需要單獨建立框架,這可能會在移植到其他平台時產生問題。
什麼是序列化?
在電腦科學中,在資料儲存和傳輸的背景下,序列化是將資料結構或物件的狀態轉換為可以在另一個運算環境中儲存和檢索的格式的過程。收到一系列位元後,它們會根據序列化格式重新計算,並可用於建立原始物件的語義相同的克隆。Java提供了自動序列化,這需要物件實作介面java.io.Serializable
。介面實作將類別標記為“可序列化”。java.io.Serialized 介面沒有序列化方法,但可序列化類別可以選擇定義將在序列化/反序列化過程中呼叫的方法。對類別進行變更時,您需要考慮哪些類別與序列化相容,哪些不相容。您可以在此處閱讀完整說明。我將給出最重要的一點: 不相容的更改:
- 刪除一個字段;
- 在層次結構中向上或向下移動類別;
- 將非靜態欄位變更為靜態欄位或將非瞬態欄位變更為瞬態欄位;
- 更改聲明的原始資料類型;
- 更改方法
WriteObject
,ReadObject
使它們不再預設寫入或讀取欄位; - 更改類別
Serializable
或Externalizable
反之亦然; - 將枚舉類別變更為非枚舉類,反之亦然;
- 刪除
Serializable
或Externalizable
; - 向類別新增
writeReplace
方法。readResolve
- 新增字段;
- 新增/刪除類別;
- 添加方法
WriteObject/ReadObject
[方法defaultReadObject
或defaultWriteObject
必須在開頭調用]; - 去除方法
WriteObject/ReadObject
; - 添加
java.io.Serializable
; - 更改現場存取權限;
- 將靜態欄位變更為非靜態欄位或將瞬態欄位變更為非瞬態欄位。
GO TO FULL VERSION