详细分析如何在java代码中使用继承和组合
文章目錄
- 繼承與組合
- 何時在Java中使用繼承
- 何時在Java中使用組合
- 繼承與組成:兩個例子
- 用Java繼承重寫方法
- Java不具有多重繼承
- 使用super訪問父類方法
- 構造函數與繼承一起使用
- 類型轉換和ClassCastException
- 用超類型進行轉換
- 查看例子分析執行結果
繼承與組合
繼承和組合是程序員用在類和對象之間建立關系的兩種編程技術。繼承是從另一類繼承一個類,而組合將一個類定義為其部分的總和。
通過繼承創建的類和對象是強耦合,因為在繼承關系中更改父類或超類可能會破壞代碼。通過組合創建的類和對象是松耦合,這意味著可以更輕松地更改組件,而無需破壞代碼。
因此組合提供了更大的靈活性,所以許多程序員已經知道,組合是比繼承更好的選擇。
繼承與組合之間的區別,以及如何選擇使用繼承和組合。請繼續下面干貨。
何時在Java中使用繼承
在面向對象的編程中,當我們知道孩子與其父母之間存在“是”關系時,就可以使用繼承。一些示例是:
- 貓是動物。
- 汽車是車輛。
在這種情況下,子類或子類都是父類或超類的專門版本。從超類繼承是代碼重用的一個示例。為了更好地理解這種關系,請看下面例子:
在考慮使用繼承時,子類是否確實是超類的更專門的版本。在這種情況下,汽車是車輛的一種,因此繼承關系很有意義。
何時在Java中使用組合
在面向對象的編程中,我們可以在一個對象“具有”另一個對象(或屬于另一個對象)的情況下使用組合。一些示例是:
- 汽車有電池(電池是汽車的一部分)。
- 一個人有心臟(心臟是一個人的一部分)。
- 房子有一個客廳(客廳是房子的一部分)。
為了更好地理解這種類型的關系,請看下面例子:
在這種情況下,我們知道一所房子有一個客廳和一間臥室,因此我們可以使用Bedroom和 LivingRoom對象構成一個House。
繼承與組成:兩個例子
看下面的代碼。這是繼承的好例子嗎?
import java.util.HashSet;public class CharacterBadExampleInheritance extends HashSet<Object> {public static void main(String... badExampleOfInheritance) {BadExampleInheritance badExampleInheritance = new BadExampleInheritance();badExampleInheritance.add("Homer");badExampleInheritance.forEach(System.out::println);}在這種情況下,子類繼承了許多它永遠不會使用的方法,導致緊耦合的代碼既混亂又難以維護。
現在,讓我們使用組合代碼重寫上面例子:
import java.util.HashSet; import java.util.Set;public class CharacterCompositionExample {static Set<String> set = new HashSet<>();public static void main(String... goodExampleOfComposition) {set.add("Homer");set.forEach(System.out::println);}在這種情況下使用組合允許 CharacterCompositionExample類僅使用的兩個HashSet方法,而無需繼承所有方法。這樣可以簡化代碼,減少耦合,從而更易于理解和維護。
用Java繼承重寫方法
繼承使我們可以在新類中重用一個類的方法和其他屬性,這非常方便。但是要使繼承真正起作用,我們還需要能夠在新的子類中更改某些繼承的行為。請看具體例子:
class Animal {void emitSound() {System.out.println("The animal emitted a sound");}}class Cat extends Animal {@Overridevoid emitSound() {System.out.println("Meow");} }class Dog extends Animal { }public class Main {public static void main(String... doYourBest) {Animal cat = new Cat(); // MeowAnimal dog = new Dog(); // The animal emitted a soundAnimal animal = new Animal(); // The animal emitted a soundcat.emitSound();dog.emitSound();animal.emitSound();}}以上例子是方法覆蓋的Java繼承。首先,我們擴展Animal類創建一個新的Cat類。接下來,我們覆蓋Animal類的emitSound()方法來獲取Cat特定聲音。即使我們將類類型聲明為Animal,當我們實例化Cat時也會得到貓的叫聲。 方法重載是多態。
Java不具有多重繼承
與某些語言(例如C ++)不同,Java不允許對類進行多重繼承。但是,可以使用逐一繼承或實現多個接口。
下面例子使用java多重繼承,則代碼將無法編譯:
1、使用類逐一繼承:
class Animal {} class Mammal extends Animal {} class Dog extends Mammal {}2、使用接口
interface Animal {} interface Mammal {} class Dog implements Animal, Mammal {}使用super訪問父類方法
當兩個類通過繼承相關聯時,子類必須能夠訪問其父類的每個可訪問字段,方法或構造函數。在Java中,我們使用保留字super來確保子類仍然可以訪問其父類的重寫方法:
public class SuperWordExample {class Character {Character() {System.out.println("A Character has been created");}void move() {System.out.println("Character walking...");}}class Moe extends Character {Moe() {super();}void giveBeer() {super.move();System.out.println("Give beer");}}}在此示例中,Character是Moe的父類。在子類中使用super,我們可以訪問Character的 move()方法。
構造函數與繼承一起使用
當一個類繼承自另一個類時,在加載其子類之前,始終會先加載超類的構造函數。在大多數情況下,保留字super將自動添加到構造函數中。但是,如果超類在其構造函數中有一個參數,我們將手動調用該super構造函數,如下所示:
public class ConstructorSuper {class Character {Character() {System.out.println("The super constructor was invoked");}}class Barney extends Character {// No need to declare the constructor or to invoke the super constructor// The JVM will to that}}如果父類的構造函數帶有至少一個參數,則必須在子類中聲明該構造函數,并使用super它顯式調用父構造函數。super保留字不會被自動添加,沒有它的代碼將無法編譯。例如:
public class CustomizedConstructorSuper {class Character {Character(String name) {System.out.println(name + "was invoked");}}class Barney extends Character {// We will have compilation error if we don't invoke the constructor explicitly// We need to add itBarney() {super("Barney Gumble");}}}類型轉換和ClassCastException
強制轉換是一種向編譯器明確傳達確實要轉換給定類型的方式。 如果您強制轉換的類與聲明的類類型不兼容,就會產生ClassCastException。
在繼承中,我們可以在不強制轉換的情況下將子類轉換成父類,但是在不使用強制轉換的情況下不能將父類轉換成子類。
public class CastingExample {public static void main(String... castingExample) {Animal animal = new Animal();Dog dogAnimal = (Dog) animal; // We will get ClassCastExceptionDog dog = new Dog();Animal dogWithAnimalType = new Dog();Dog specificDog = (Dog) dogWithAnimalType;specificDog.bark();Animal anotherDog = dog; // It's fine here, no need for castingSystem.out.println(((Dog)anotherDog)); // This is another way to cast the object}}class Animal { } class Dog extends Animal { void bark() { System.out.println("Au au"); } }當我們將Animal實例強制轉換為Dog,會出現類型轉換異常。這是因為Animal不知道轉換成什,它可能是貓,鳥等。沒有關于特定動物的信息。
Animal animal = new Animal();然后做如下轉換:
Dog dogAnimal = (Dog) animal;由于沒有Dog實例,因此無法將Animal轉換為Dog。所以會報ClassCastException。
因此應該這樣實例化Dog:
然后賦值給Animal:
Animal anotherDog = dog;用超類型進行轉換
可以使用超類型聲明 Animal,但是如果我們要調用子類特定方法,則需要對其進行強制轉換。例如,如果我們想調用子類bark()方法怎么辦,但超類Animal不知道什么什么情況下,可以調用。請看下面例子:
Animal dogWithAnimalType = new Dog(); Dog specificDog = (Dog) dogWithAnimalType; specificDog.bark();如果不想聲明另一個變量時,也可以使用以下情況強制轉換。
System.out.println(((Dog)anotherDog)); // This is another way to cast the object查看例子分析執行結果
public class InheritanceCompositionChallenge {private static int result;public static void main(String... doYourBest) {Character homer = new Homer();homer.drink();new Character().drink();((Homer)homer).strangleBart();Character character = new Character();System.out.println(result);((Homer)character).strangleBart();}static class Character {Character() {result++;}void drink() {System.out.println("Drink");}}static class Homer extends Character {Lung lung = new Lung();void strangleBart() {System.out.println("Why you little!");}void drink() {System.out.println("Drink beer");lung.damageLungs();}}static class Lung {void damageLungs() {System.out.println("Soon you will need a transplant");}}}運行main方法后輸出是什么
A)
Drink Drink Why you little! 2 Exception in thread "main" java.lang.ClassCastException:....B)
Drink beer Soon you will need a transplant Drink Why you little! Exception in thread "main" java.lang.ClassCastException:....C)
Drink beer Soon you will need a transplant Drink Why you little! 3 Exception in thread "main" java.lang.ClassCastException:....D)
Drink beer Soon you will need a transplant Drink Why you little! 2 Why you little!正確答案是C
分析代碼原因如下:
Homer實例化對象,執行Homer方法實現,打印結果:
Drink beer Soon you will need a transplant然后,Character類中調用drink()。
new Character().drink();打印結果為:
Drink beer接著我們使用強制轉換并調用strangleBart():
((Homer)homer).strangleBart();打印結果為:
System.out.println(result);因為super構造函數每次new對象都被調用,所以我們只需要計算Character或Homer實例化幾次。
打印結果為:3
最后,我們調用類型轉換:
((Homer) character).strangleBart();因為我們要把父類轉換為子類,就好像要把動物轉換為鳥,因為會報ClassCastException異常。
總結
以上是生活随笔為你收集整理的详细分析如何在java代码中使用继承和组合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java不可变类作为参数传递遇到的坑
- 下一篇: HashMap之三问为什么及性能问题