你好!在過去的課程中,我們討論了轉換原始類型。讓我們簡要回顧一下討論的內容。
我們將基本類型(在本例中為數字類型)想像成嵌套的玩偶,它們的大小根據它們佔用的內存量而變化。您會記得,在現實生活和 Java 編程中,將較小的玩偶放入較大的玩偶中都很簡單。
考慮另一個更有趣的例子:

public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
short smallNumber = (short) bigNumber;
System.out.println(smallNumber);
}
}
這是一個自動轉換或加寬 的例子。它會自動發生,因此您無需編寫額外的代碼。最後,我們並沒有做任何不尋常的事情:我們只是將一個較小的娃娃放入一個較大的娃娃中。如果我們試圖反其道而行之,將較大的俄羅斯套娃放入較小的套娃中,那就另當別論了。你不能在現實生活中做到這一點,但在編程中你可以。但有一個細微差別。如果我們嘗試將 an 放入變量int
中short
,事情就不會那麼順利了。畢竟short
變量只保存了16位的信息,而an卻int
佔據了32位!結果,傳遞的值被扭曲。編譯器會給我們一個錯誤('伙計,你在做一些可疑的事情!'). 但是,如果我們明確指出要將我們的值轉換為的類型,它將繼續執行操作。
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
bigNumber = (short) bigNumber;
System.out.println(bigNumber);
}
}
這就是我們在上面的例子中所做的。該操作已執行,但由於該short
變量只能容納 32 個字節中的 16 個,因此最終值被扭曲,我們得到數字-27008。這種操作稱為顯式轉換或縮小。
擴大和縮小引用類型的例子
現在讓我們來談談同樣的運算符,但不是應用於基本類型,而是應用於對象和引用變量!這在 Java 中如何工作?其實很簡單。有些對像是不相關的。假設它們不能相互轉換是合乎邏輯的,既不能顯式地也不能自動地:
public class Cat {
}
public class Dog {
}
public class Main {
public static void main(String[] args) {
Cat cat = new Dog(); // Error!
}
}
在這裡,當然,我們得到一個錯誤。和類彼此不相關,我們還沒有編寫一個“轉換器”來從一個轉移到另一個Cat
。Dog
我們不能這樣做是有道理的:編譯器不知道如何將這些對像從一種類型轉換為另一種類型。如果對像是相關的,那又是另一回事了!相關如何?最重要的是,通過繼承。讓我們嘗試使用繼承來創建一個小的類系統。我們將有一個通用類來表示動物:
public class Animal {
public void introduce() {
System.out.println("I'm Animal");
}
}
每個人都知道動物可以被馴化(寵物)或野生:
public class WildAnimal extends Animal {
public void introduce() {
System.out.println("I'm WildAnimal");
}
}
public class Pet extends Animal {
public void introduce() {
System.out.println("I'm Pet");
}
}
例如,以犬科動物為例——我們有家犬和土狼:
public class Dog extends Pet {
public void introduce() {
System.out.println("I'm Dog");
}
}
public class Coyote extends WildAnimal {
public void introduce() {
System.out.println ("I'm Coyote");
}
}
我們特地選擇了最基本的類,使它們更容易理解。我們真的不需要任何字段,一個方法就足夠了。讓我們嘗試執行這段代碼:
public class Main {
public static void main(String[] args) {
Animal animal = new Pet();
animal.introduce();
}
}
您認為控制台上會顯示什麼?會調用類的方法還是類introduce
的方法?在繼續閱讀之前嘗試證明你的答案是正確的。這就是結果! 我是寵物 為什麼我們得到那個?一切都很簡單。我們有一個父變量和一個後代對象。通過寫作, Pet
Animal
Animal animal = new Pet();
我們擴大了一個Pet
引用並將其分配給一個Animal
變量。與原始類型一樣,引用類型在 Java 中會自動擴展。您不需要編寫額外的代碼來實現它。現在我們有一個分配給父引用的後代對象。結果,我們看到方法調用是在子類上進行的。如果您仍然不完全理解此代碼為何有效,請用通俗易懂的語言重寫它:
Animal animal = new DomesticatedAnimal();
這個沒有問題吧?想像一下這是真實的生活,參考只是一張寫有“動物”的紙質標籤。如果你把那張紙貼在任何寵物的項圈上,一切都會正確。畢竟,任何寵物都是動物!相反的過程——將繼承樹向下移動到後代——正在縮小:
public class Main {
public static void main(String[] args) {
WildAnimal wildAnimal = new Coyote();
Coyote coyote = (Coyote) wildAnimal;
coyote.introduce();
}
}
如您所見,這裡我們清楚地指出了我們要將對象轉換為的類。我們以前有一個WildAnimal
變量,現在我們有一個Coyote
,它在繼承樹上處於較低位置。如果沒有明確指示,編譯器將不允許這樣的操作,這是有道理的,但如果我們在括號中指示類型,那麼一切正常。 
public class Main {
public static void main(String[] args) {
Pet pet = new Animal(); // Error!
}
}
編譯器產生錯誤!但為什麼? 因為您正試圖將父對象分配給後代引用。 換句話說,你正在嘗試做這樣的事情:
DomesticatedAnimal domesticatedAnimal = new Animal();
好吧,如果我們明確指定我們試圖轉換成的類型,也許一切都會起作用? 這對數字有效——讓我們試一試吧!:)
public class Main {
public static void main(String[] args) {
Pet pet = (Pet) new Animal();
}
}
線程“main”中的異常 java.lang.ClassCastException: Animal cannot be cast to Pet 錯誤!編譯器這次沒有對我們大喊大叫,但我們以異常結束。我們已經知道原因:我們試圖將父對象分配給後代引用。但是為什麼你不能那樣做呢? 因為不是所有的動物都是家養動物。 您創建了一個Animal
對象並試圖將其分配給一個Pet
變量。土狼也是一個Animal
,但它不是Pet
。換句話說,當你寫
Pet pet = (Pet) new Animal();
new Animal()
可以代表任何動物,不一定是寵物!當然,您的Pet pet
變量僅適用於存儲寵物(及其後代),而不適用於任何類型的動物。ClassCastException
這就是為什麼為在轉換類時發生錯誤的情況創建特殊的 Java 異常的原因。讓我們再次回顧它以使事情更清楚。父引用可以指向子類的實例:
public class Main {
public static void main(String[] args) {
Pet pet = new Pet();
Animal animal = pet;
Pet pet2 = (Pet) animal;
pet2.introduce();
}
}
例如,在這裡我們沒有問題。我們有一個Pet
由變量引用的對象Pet
。後來,一個Animal
引用指向了同一個對象。之後,我們轉換animal
為Pet
. 順便說一句,為什麼這對我們有用?上次我們有一個例外!因為這次我們原來的對像是一個Pet
!
Pet pet = new Pet();
但在最後一個例子中,它是一個Animal
對象:
Pet pet = (Pet) new Animal();
您不能將祖先對象分配給後代變量。你可以做相反的事情。
GO TO FULL VERSION