Java 中的 Object[] 陣列可以保存什麼?
1.概述
在 Java 中,初學者面臨的一個常見挑戰是理解陣列及其與類型系統的互動方式。例如,如果我們宣告一個String[]
數組,它儲存的是字串,而宣告一個Object[]
數組則不那麼簡單。因此,為了有效地利用數組,我們需要了解它們的能力和限制。
在本教程中,我們將重點關注Object[]
陣列並探索它可以容納的內容。
2. 數組和類型系統
數組用於將相同類型的多個元素儲存在連續的記憶體區塊中。類型可以是原始型別( int, double
),也可以是參考型別( String, Array
)。
現在,由於 Java 是一種強類型語言,因此用特定類型聲明的陣列只能保存該類型或其子類別的元素:
String[] names = {"Samuel", "Bob", "Chris"};
names[0] = "Alice";
names[1] = 20;
上面, names
只能接受String
對象,因為它是一個String[]
陣列。當我們嘗試插入一個Integer
(在本例中為names[1] = 20
)或任何不相關的類型時,都會出現編譯時錯誤。
3. Java中Object
的特殊作用
在 Java 的繼承樹中, Object
類別位於最頂層。具體來說,所有其他類,無論是內建的還是自訂的,都直接或間接地擴展了Object
。因此, Object
通常被稱為類別層次結構的根。
由於上面討論的原因,宣告為Object
類型的變數可以保存對任何物件的參考:
Object obj = "Hello"; // A String stored as Object
obj = 42; // An Integer (autoboxed) stored as Object
obj = new java.util.Date(); // A Date object stored as Object
有了這種靈活性,我們可以將任何類別的實例賦值給Object
變數。然而,
一旦這樣做,我們就無法直接存取該類別的具體方法了。方便的是,如果以後需要使用特定類型的功能,我們可以將物件強制轉換回其原始類別:
Object obj = "Hello";
String str = (String) obj; // Cast back to String
System.out.println(str.toUpperCase()); // Now we can use String methods
因此, Object
可以引用任何類別實例,但如果我們打算利用原始類別的特定方法,則需要轉換。
4. 這對於Object[]
意味著什麼?
透過理解Object
的特殊作用, Object[]
陣列的概念現在變得更加清晰。
當我們宣告一個Object[]
陣列時,我們告訴 Java 該陣列可以儲存Object
類型的參考。因此, Object[]
數組可以保存對任何類型物件的引用,包括:
- 內建類別,例如
String
、Integer
、Double
或Boolean
- 我們建立的自訂類,例如
Person
- 其他數組,因為數組本身也是項目
- 基本型別的包裝對象,因為 Java 在需要時會自動將基本型別裝入其包裝型別中,例如,
42
變成Integer
-
null
引用,因為null
是任何物件引用的有效佔位符
讓我們來看一個簡單的例子,示範Object[]
如何保存不同類型的物件:
Object[] values = new Object[5];
values[0] = "Hello"; // String
values[1] = 42; // Autoboxed Integer
values[2] = 3.14; // Autoboxed Double
values[3] = new int[]{1, 2, 3}; // int[] array
values[4] = new Person("Alice", 30); // Custom class
這裡, values
數組儲存了五種不同類型的對象,顯示了Object[]
數組與特定類型的數組(如String[]
相比的靈活性。
但是,與單一Object
引用類似,當從Object[]
陣列中檢索元素時,如果我們想要使用特定於類型的方法,則需要進行反向轉換:
String greeting = (String) values[0];
System.out.println(greeting.toUpperCase());
在這個例子中,Java 只將values[0]
視為沒有強制轉換的普通對象,因此,我們不能直接呼叫toUpperCase()
之類的方法。
5. Object[]
不能直接保存什麼
儘管Object[]
陣列非常靈活,但我們仍需要記住一些限制。
5.1. 原始型別不能直接存儲
在 Java 中,像int
、 boolean
或double
這樣的原始值並不是物件。因此,它們不能直接儲存在Object[]
陣列中:
Object[] arr = new Object[1];
arr[0] = 5;
上面的命令可以編譯並運行,但這並不是因為原始值5
儲存在數組中。相反,Java 透過一個稱為自動裝箱的功能自動將該值轉換為Integer
物件。
因此,雖然 Java 看起來將一個int
儲存到陣列中,但它實際儲存的是一個 Integer 包裝器物件。
5.2. 無內建安全
另一個限制是Object[]
不強制型別一致性。例如,我們可以在同一個陣列中混合使用不同的類型:
Object[] mixed = new Object[2];
mixed[0] = "Hello"; // String
mixed[1] = 42; // Integer
雖然這種靈活性很強大,但也帶來了風險。例如,如果我們稍後進行錯誤的類型轉換,就會在運行時拋出ClassCastException
異常:
String text = (String) mixed[1]; // Throws ClassCastException
上面, mixed[1]
包含一個整數,但我們試圖將其轉換為String
。
6. 數組中的協方差
Java 中的陣列是協變的,這意味著如果一種類型是另一種類型的子類,那麼子類的陣列也被視為超類的陣列。
例如,由於Integer
和Double
是Number
的子類,因此可以在需要Number[]
的地方使用Integer[]
或Double[]
:
Number[] numbers = new Number[2];
numbers[0] = 10; // Integer fits
numbers[1] = 3.14; // Double fits
這裡,兩個值都符合要求,因為Integer
和Double
都繼承自Number
。從這個演示中,我們可以推論出兩個關鍵點:
- 使用特定類型宣告的陣列可以儲存該類型及其子類別的值,例如,
Number[]
陣列可以儲存Integer
或Double
-
Object[]
可以容納任何類,因為每個類別都是 Object 的子類
上面,我們展示了陣列如何遵循繼承規則,其中Object[]
是最靈活的,因為 Java 中的每個類別都擴展了Object
。
然而,這種靈活性也伴隨著風險。如果我們將一個更具體的數組(例如String[]
賦值給一個更廣泛的類型引用(例如Object[]
,編譯器會允許這樣做,而插入錯誤的類型只會在運行時失敗:
String[] strings = new String[2];
Object[] objs = strings;
objs[0] = 100; // Compiles fine, but throws ArrayStoreException at runtime
雖然協方差使數組變得靈活,但如果我們嘗試儲存錯誤的類型,它也會引入運行時錯誤。
7. 結論
在本文中,我們探討了Object[] Array
在 Java 中可以容納什麼。
我們了解到, Object[]
陣列可以儲存 Java 中任何類別的引用,因為 Java 中的每個類別都擴展了 Object。例如,內建類型、自訂類別、數組,甚至 null。然而,原始類型不會直接儲存。相反,Java 透過自動裝箱將它們包裝到相應的包裝類別中。
此外, Object[]
不強制型別安全,這表示不正確的轉換只能在執行時間透過ClassCastException
擷取。
值得注意的是,我們可以在 GitHub 上檢查原始碼。