1.所有類繼承Object

Java 中的所有類都隱式繼承該類Object

我們將在 Java Core 任務中分析繼承是什麼以及它在 Java 中是如何工作的。現在,我們將考慮由此得出的一個簡單事實:

任何類的對像都可以分配給Object變量。例子:

代碼 筆記
Object o = new Scanner(System.in);
變量存儲對對象o的引用Scanner
Object o = new String();
變量存儲對對象o的引用String
Object o = new Integer(15);
變量存儲對對象o的引用Integer
Object o = "Hello";
變量存儲對對象o的引用String

這是好消息結束的地方。編譯器不會跟踪保存在變量中的對象的原始類型Object,因此除了類的方法之外,您將無法 調用保存的對象的Object方法。

如果你需要調用與對象的原始類型相關聯的方法,那麼你需要先將對它的引用保存在正確類型的變量中,然後調用該變量的方法:

代碼 筆記
Object o = new Scanner(System.in);
int x = o.nextInt();
該程序將無法編譯。該類Object沒有nextInt()方法。
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
這會起作用。這裡我們使用類型轉換運算符

將對對象 的引用保存Scanner在變量中。 Scanner

您不能直接將Object變量分配給 Scanner 變量,即使該Object變量存儲了對對象的引用Scanner但是如果你使用你已經知道的類型轉換運算符,你就可以做到這一點。這是它的一般外觀:

Type name1 = (Type) name2;

其中name1是變量名Type,是存儲對象引用的變量name2名。ObjectType

類型轉換

如果變量的類型和對象的類型不匹配,則會ClassCastException拋出 a 。例子:

代碼 筆記
Object o = new Integer(5);
String s = (String) o;
運行時會報錯:這裡會拋出
aClassCastException

在 Java 中有一種方法可以避免這種錯誤:我們通過檢查存儲在變量中的對象的類型來做到這一點:

name instanceof Type

運算instanceof符檢查name變量是否為Type對象。

例如,讓我們在不同對象的數組中查找一個字符串:

代碼 筆記
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
自動裝箱會將這些值分別轉換為IntegerStringDouble

遍歷對像數組

如果對像是 aString

將其保存到String變量
在屏幕上顯示變量。


2. 為什麼會出現泛型——集合

讓我們回到集合。

Java 開發人員一創建該類ArrayList,就希望它具有通用性,以便它可以存儲任何類型的對象。所以他們使用 s 的數組Object來存儲元素。

這種方法的優勢在於您可以將任何類型的對象添加到集合中。

當然,也有幾個弱點。

缺點 1。

從集合中檢索元素時,總是需要編寫類型轉換運算符:

代碼 筆記
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
創建一個集合來存儲對Object對象的引用

用數字填充集合10, 20, ... 100;



對集合的元素求和


Typecasting 是必要的

缺點2。

不能保證集合包含特定類型的元素

代碼 筆記
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
創建一個集合來存儲對Object對象的引用

我們用表示為對象的數字填充集合Double
0.0, 2.5, 5.0, ...


對集合的元素求和


會出現錯誤:a Doublecannot be cast to anInteger

數據可以放在任何地方的集合中:

  • 用另一種方​​法
  • 在另一個程序中
  • 從一個文件
  • 通過網絡

缺點3。

集合中的數據可能會被意外更改。

您可以將充滿數據的集合傳遞給某種方法。該方法由不同的程序員編寫,將其數據添加到您的集合中。

集合的名稱並沒有明確指出其中可以存儲哪些類型的數據。即使你給你的變量一個明確的名字,對它的引用也可以傳遞給一打方法,而這些方法肯定不會知道變量的原始名稱。


3.泛型

Java 中的泛型

在 Java 中,所有這些問題都被這個叫做泛型的很酷的東西消除了。

在 Java 中,泛型意味著向類型添加類型參數的能力。結果是一個複雜的複合類型。這種複合類型的一般觀點是這樣的:

ClassName<TypeParameter>

這是一個通用類。並且它可以在您通常使用類的任何地方使用。

代碼 描述
ArrayList<Integer> list;
創建變量
list = new ArrayList<Integer> ();
創建對象
ArrayList<Integer>[] array;
創建數組

Integer這樣的集合中只能存儲變量:

代碼 描述
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListInteger帶元素的集合
這是允許的 這
也可以
自動裝箱

但這是不允許的:編譯錯誤

在 Java Collections 探索中,您將學習如何使用類型參數創建您自己的類。現在,我們將看看如何使用它們以及它們是如何工作的。


4. 泛型如何工作

實際上,泛型非常原始。

編譯器只是簡單地將泛型類型替換為普通類型。但是當使用泛型類型的方法時,編譯器會添加一個類型轉換運算符來將參數轉換為類型參數:

代碼 編譯器做什麼
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

假設我們有一個對整數集合中的數字求和的方法:

代碼 編譯器做什麼
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

換句話說,泛型是一種語法糖,就像自動裝箱一樣,但更多一點。通過自動裝箱,編譯器添加了將 an 轉換int為 an 的方法Integer,反之亦然,對於泛型,它添加了類型轉換運算符。

在編譯器編譯帶有類型參數的泛型類之後,它們只是簡單地轉換為普通類和類型轉換運算符。有關傳遞給泛型類型變量的類型參數的信息將丟失。這種效果也稱為類型擦除

有時編寫泛型類(帶有類型參數的類)的程序員確實需要有關作為參數傳遞的類型的信息。在 Java Collections 任務中,您將學習如何處理這個問題以及它需要做什麼。



5. 關於泛型的一些事實

這裡有一些關於泛型的更有趣的事實。

類可以有多個類型參數。它看起來像這樣:

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

其實,這並不奇怪。編譯器可以在任何地方添加一個運算符以轉換為一種類型,它可以添加多個類型轉換運算符。

例子:

代碼 筆記
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
put方法的第一個參數是Integer,第二個是String

泛型類型也可以用作參數。它看起來像這樣:

ClassName<TypeParameter<TypeParameterParameter>>

假設我們要創建一個列表來存儲字符串列表。在這種情況下,我們會得到這樣的東西:

// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");

// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");

// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);

泛型類型(帶有類型參數的類型)也可以用作數組類型。它看起來像這樣:

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

這裡沒有什麼神奇的事情發生:尖括號只是表示類型名稱:

代碼 非通用對應物
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];