## polymorphism * 編譯時多型(靜態) **Method Overloading** 同一個class可以有多種同名method,但參數列表必須不同(==參數類型、個數或者參數的順序不同==)。 * 運行時多型(動態) **Method Overriding** 在子類別的覆寫 ## super 關鍵字 如果要調用已經被子類別override的父類別method時,有兩種方式: * 在子類別的method中直接調用父類別的方法:==在子類別class中的method中使用super== * 顯式轉型(Casting)到父類別類型 ```=java= class Parent { void display() { System.out.println("This is Parent class"); } } class Child extends Parent { @Override void display() { System.out.println("This is Child class"); } void callParentDisplay() { //在method裡 super.display(); // 使用 super 調用父類的 display 方法 } } public class TestCasting { public static void main(String[] args) { Child child = new Child(); // 直接調用子類別的 display 方法 child.display(); // 輸出: This is Child class // 通過子類別調用父類別的 display 方法 child.callParentDisplay(); // 輸出: This is Parent class } } ``` **顯式轉型:(down-casting)** ```=java= class Animal{ public void makesound(){ System.out.println("Some generic sound"); } } class Dog extends Animal{ public void makesound(){ System.out.println("Dog sound"); } public void fetch(){ System.out.println("Dog fetch!!"); } } public class superts { public static void main(String[] args) { System.out.println("yy"); Animal mypet=new Dog(); //隱性轉型 mypet.makesound(); mypet.fetch(); //error!!! } } ``` ``` mypet 並不能調用Dog特有的方法fetch() 想調用特有方法必須顯性轉型到Dog if (myAnimal instanceof Dog) { Dog myDog = (Dog) mypet; //顯式轉型 myDog.fetch(); } ``` **隱性轉型:(up-casting)** ```=java= class animal{ } class cat extend animal{ public void makesound(){System.out.println("meow");} } class dog extend animal{ public void makesound(){System.out.println("bark");} } public static void main(){ animal mypet = new animal(); animal mypet = new cat(); //將cat物件向上轉為animal animal mypet = new dog(); //=>先知道牠是animal,但實際上是animal,cat or dog mypet.makesound();=>看實際上是什麼動物而用該類別的method } ``` ``` ``` :::danger ▲==只能使用繼承鏈共同有的method==,假設dog class有別的method,則無法調用。因為本質上mypet 還是一個animal class的物件。 =>調用animal 的makesound(),沒想到它發出了狗叫,但會不會做其他dog的method則不知道。 ::: :::info 隱性轉型:一開始不知道要new什麼物件,依使用者的輸入而定,先從parent的角度去看一個obj ::: ## instanceof 關鍵字 >::::success >instanceof是一個關鍵字,not method!!!,用於檢查一個物件是否屬於特定的==類別==或==其子類別==。 >回傳一個布林值。 >:::: ```=java= class Animal { } class Dog extends Animal { } public class Main { public static void main(String[] args) { Animal myAnimal = new Animal(); Dog myDog = new Dog(); System.out.println(myAnimal instanceof Animal); //myAnimal屬於Animal的類別嗎>true System.out.println(myDog instanceof Animal); //myDog屬於Animal的子類別嗎>true System.out.println(myAnimal instanceof Dog); //False } } ``` 也可以用來檢查物件是否有實現某個interface ```=java= interface animal{ void eat(); void drink(); } class birds implements animal{ public void eat(){ System.out.println("ate something"); } public void drink(){ System.out.println("drank something"); } } class toy_birds{ } public class basic { public static void main(String[] args) { birds that_bird =new birds(); toy_birds this_bird=new toy_birds(); System.out.println(that_bird instanceof animal);//true System.out.println(this_bird instanceof animal);//false } } ``` ::::info A instanceof B : A是B的實例嗎? >>>true/false :::: ## Constructor建構子 >::::success >在Java中,建構子是一種特殊的方法,用於初始化新創建的物件。==它的名稱必須與類別名稱完全相同==,並且不返回任何類型(包括void) >:::: ```=java= public class Person { String name; int age; // 第一個 constructor public Person(String name) { this.name = name; this.age = 0; // 預設年齡為 0 } // 第二個 constructor public Person(String name, int age) { this.name = name; this.age = age; } } ``` 在new物件的時候會依照建構子預設的參數來初始化物件的attributes <font color="#f00">一個class可以有多個建構子。</font> ``` Person person1 = new Person("Alice"); // 調用第一個 constructor,age 將設為預設值 0 Person person2 = new Person("Bob", 25); // 調用第二個 constructor,age 將設為 25 ``` :::danger 當一個class有constructor時,繼承它的class也要有相同的constructor!! ::: --- :::warning ### **review:** 在Java中,方法的參數傳遞方式可分為 "Call by Value" 和 "Call by Reference"(或者說,都是Call by Value)。 #### **call by value:** primitive type的資料類型都是傳遞它的實際value,可以在method中自由進行修改而不會影響到原來的值。 ```=java= public class basic { public static void main(String[] args) { int a=10; int b=a; System.out.println(b-4); //6 System.out.println(a); //10 } } ``` ==基本數據類型的變數在賦值或傳遞時會創建新的資料實體的copy。== #### **call by reference:** "Call by Reference" 意味著我們將一個變數的Reference傳遞給方法。因此,如果方法修改了這個參照參照的物件,那麼這個改變會反映到原來的物件上。這種方式適用於物件。 ```=java= class MyObject { int value; } void updateValue(MyObject obj) { //傳入的是該物件的ref obj.value = 55; //那個address的.value值改變為55 } public static void main(String[] args) { MyObject obj = new MyObject(); obj.value = 22; System.out.println("Before: " + obj.value); // Output: Before: 22 updateValue(obj); System.out.println("After: " + obj.value); // Output: After: 55 } ``` ==but當我們嘗試**在method中**改變reference本身(例如,使它參照到另一個物件)時,這個改變不會反映到原來的物件上。== ```=java= class my_pet{ String name; my_pet(String name){ this.name=name; } } public class basic { static void change_dog(my_pet Dog){ Dog = new my_pet("bob"); } public static void main(String[] args) { my_pet my_dog=new my_pet("rogger"); System.out.println(my_dog.name);//rogger change_dog(my_dog); System.out.println(my_dog.name);//rogger } } //my_dog.name值不變 ``` 比較 ```=java= class my_pet{ String name; my_pet(String name){ this.name=name; } } public class basic { public static void main(String[] args) { my_pet my_dog=new my_pet("rogger"); System.out.println(System.identityHashCode(my_dog));//hash code 1421795058 System.out.println(my_dog.name);//rogger my_pet another_dog=new my_pet("bob"); System.out.println(System.identityHashCode(another_dog));//hashcode 1555009629 my_dog=another_dog; System.out.println(my_dog.name);//bob System.out.println(System.identityHashCode(my_dog));hash code 1555009629 } } my_dog的reference指向another_dog,所有my_dog.name的值會是"bob" ``` ::::info 假設我把call by reference先當成是call by value,value的值是那個address,那這個value傳遞到method中就會呼應前面primitive type的情況,value是沒辦法被更改的,所以無法改變參數value(reference的值)。 :::: ## Java Dynamic Binding ::::success >### 定義 > >在Java中,動態綁定(Dynamic Binding)是一種在運行時確定物件方法調用的機制。這意味著當你調用一個物件的方法時,被調用的方法版本(子類別或父類別中的方法)是在程序運行時基於物件的實際類型決定的,而不是在編譯時。這是Java多型的核心。 ::::info 簡單來說就是依照我做的動作而有不一樣的結果 :::: ```=java= class Animal { void eat() { System.out.println("Animal is eating"); } } class Dog extends Animal { @Override void eat() { System.out.println("Dog is eating"); } } public class TestDynamicBinding { public static void main(String[] args) { Animal myAnimal = new Dog(); myAnimal.eat(); // Prints "Dog is eating" } } ``` 雖然我的<font color="#f00">myAnimal</font>是用<font color="#f00">Animal</font> class創建,但我new出來是<font color="#f00">Dog</font>物件,所以<font color="#f00">myAnimal</font>是使用<font color="#f00">Dog</font>的已經被覆寫的method。 ``` @Override void eat() { System.out.println("Dog is eating"); } ``` 同樣 ```=java= class Animal { void eat() { System.out.println("Animal is eating"); } } class Dog extends Animal { @Override void eat() { System.out.println("Dog is eating"); } } class Cat extends Animal{ @Override void eat(){ System.out.println("cat is eating"); } } public class dynamic_bounding { public static void main(String[] args) { if(args[0].equals("Dog")){ Animal myAnimal = new Dog(); myAnimal.eat(); // Prints "Dog is eating" } else{ Animal myAnimal=new Cat(); myAnimal.eat(); } } } ``` 實際物件為何由args[0]輸入而決定。 ## this. ::::success >在Java中,this是一個關鍵字,用於參照當前物件,即正在被方法或建構子呼叫的物件。它有以下幾種用途 ><font color="#f00">●參照物件的實例變數:當方法的參數變數/區域變數與物件的Member變數名稱相同時,可以使用this來參照物件的實例Member變數。</font> ><font color="#f00">●呼叫物件的其他建構子:在一個建構子裡,你可以使用this()來呼叫物件的其他建構子></font> ><font color="#f00">●作為當前物件reference的回傳:你可以使用this來回傳當前物件的reference。</font> :::: ```=java= class Box { private int width, height, depth; // constructor,使用this區分實例變數和constructor參數 public Box(int width, int height, int depth) { this.width = width; this.height = height; this.depth = depth; } // 設置尺寸的方法,使用this區分實例變數和方法參數 public void setDimensions(int width, int height, int depth) { this.width = width; this.height = height; this.depth = depth; } // 使用this返回當前類的實例 public Box getSelf() { return this; } // 顯示尺寸的方法 public void displayDimensions() { System.out.println("Width: " + width + ", Height: " + height + ", Depth: " + depth); } } public class TestThis { public static void main(String[] args) { Box myBox = new Box(10, 20, 30); myBox.displayDimensions(); // 輸出: Width: 10, Height: 20, Depth: 30 // 使用setDimensions方法重新設置尺寸 myBox.setDimensions(50, 60, 70); myBox.displayDimensions(); // 輸出: Width: 50, Height: 60, Depth: 70 // 使用this返回當前類的實例 Box anotherBox = myBox.getSelf(); anotherBox.displayDimensions(); // 輸出: Width: 50, Height: 60, Depth: 70 } } ``` ```=java=4 // constructor,使用this區分實例變數和constructor參數 public Box(int width, int height, int depth) { this.width = width; this.height = height; this.depth = depth; } //多加以下 public Box(){ System.out.println("hellow");//這行會error!!!! this(width=1,height= 1,depth=1); system.out.println("hellow");//要在constructor後 } ``` 呼叫同名Box的建構子並賦值1,1,1 ::::danger - 如果沒有this.,建構子參數指向local var(local優先),有this.才是指到該物件的lnstance var - 物件的constructor一定要先寫(放在最前面)(先初始化),才能進行其他宣告 :::: ## interface 用法 ::::success 它是這些類別的公共==抽象方法==的集合。當一個類別實現了某個Interface,它會被要求提供Interface中所有方法的具體實現(implementation)。 Interface的主要用途: - 實現抽象:提供了一種機制,用於指定一組必須由實作此interface的類別來實現的方法。 - 實現多重繼承:Java不支持多重繼承(一個類別有多個父類別),但通過實現多個Interface,一個類別可以”類似”繼承了多個Interface的特性。 :::: **usage:** ``` interface interface_name{ method() //抽象方法只有聲明 } class name importments interface_name{ } ``` ```=java= // 定義Interface Vehicle interface Vehicle { // Interface中的抽象方法 move void move(); } // Car 類別實現 Vehicle Interface class Car implements Vehicle { // 實現 move 方法 public void move() { System.out.println("Car is moving"); } } // Bicycle 類別實現 Vehicle Interface class Bicycle implements Vehicle { // 實現 move 方法 public void move() { System.out.println("Bicycle is moving"); } } public class TestInterface { public static void main(String[] args) { // 創建一個 Vehicle 類型的reference指向 Car object Vehicle myCar = new Car(); myCar.move(); // 輸出: Car is moving // 創建一個 Vehicle 類型的reference指向 Bicycle object Vehicle myBicycle = new Bicycle(); myBicycle.move(); // 輸出: Bicycle is moving } } ``` ::::info interface定義了哪些動作,importments它的class就一定要實現出那些方法 :::: ```=java= interface flyable{ void fly(); void getWings(); } interface can_eat{ void eat(); } class bird importments flyable,animal{ //可以importments多個interface void fly... void getWings... void eat.. } public static void main(){ flyable birds =new bird(); } ``` interface 近似於parent class(我知道它會fly、會進食,但實際上不知道是什麼東西) :question: 如果你要implements兩個interface,其中都需實作一個同名的method,會發生什麼事呢? ``` A:沒有error發生 ``` ### Interface Member Variable ```=java= interface MyInterface { // 這是一個接口中的成員變數,它實際上是一個常數 int MY_CONSTANT = 10; } class MyClass implements MyInterface { public void printConstant() { System.out.println("The constant is: " + MY_CONSTANT); } } public class TestInterface { public static void main(String[] args) { MyClass myObject = new MyClass(); myObject.printConstant(); // 輸出: The constant is: 10 } } ``` 在這個例子中,<font color="#AC19C9">MyInterface</font> Interface有一個成員變數<font color="#F7A004">MY_CONSTANT</font>。MyClass類實現了該介面,並可以訪問該常數。然而,它不能改變<font color="#F7A004">MY_CONSTANT</font>的值,因為它實際上真的是一個常數 ::::warning **recap**: class 的member var是可以改變的,除非是FINAL :::: ## Abstract Class ::::success 抽象類別(Abstract Class)是一種不能直接實例化(不可透過new來生成物件)的特殊類別。我們可以通過繼承抽象類別並實現其抽象方法來創建新的類別。抽象類別通常被用來作為所有具有共同特性的類別的基礎。是一種介於Interface(不能提供任何實作內容)與class之間的物件機制。 ### 特點 - **抽象方法(abstraction method)**:抽象類別可以包含一個或多個抽象方法。抽象方法是一種只有聲明沒有實現的方法,它的實現由子類別提供。 - **實例方法(instance method)**:除了抽象方法外,抽象類別也可以包含實例方法。這些方法可以有實現,子類別可以選擇覆寫或直接使用它們。 - **不能直接實例化**:我們不能直接使用 `new` 關鍵字來創建抽象類別的實例。然而,我們可以使用抽象類別類型的reference來引用子類別的實例。 :::: ```=java= // 定義一個抽象類別 Animal abstract class Animal { private int age; //static private static int size; //Final Final int number; // 抽象方法 sound abstract void sound(); //實例方法 void make_noise(); //建構子 public Animal(int age,int size){ this.age=age; } } // Dog 類別繼承 Animal 抽象類別 class Dog extends Animal { public Dog(int age, int size) { super(age, size); //呼叫parent class的建構子 } // 實現 sound 方法 public void sound() { System.out.println("Dog says: Woof Woof!"); } } public class TestAbstractClass { public static void main(String[] args) { // 創建一個 Dog Dog myDog = new Dog();//Dog class 創建出mydog物件 myDog.sound(); // 輸出: Dog says: Woof Woof! Animal dog=new Dog();//abstrct class 也可以,實際上是Dog的物件 dog.sound(); } } ``` ::::info abstract class跟interface很像,都可以僅聲明一些method,到被extand/importments時再被實現。 這樣,我們就可以在不知道實際類別的情況下,通過一個 Animal 參照來調用 sound() 方法,這是多型的一種體現。 :::: :question:想想看,一個abstract class能不能被另一個abstract class繼承? ``` yesss!! ``` ## inner class ::::success Inner Class是一種定義在另一個類別內部的類別。這種機制允許我們將一些相關的類別組織在一起,讓程式碼更加整潔且易於維護。內部類也可以訪問其外部類別的成員(包括private member data)。 :::: ```=java class OuterClass { private String myField = "Hello, World!"; class InnerClass { void printMyField() { // 內部類可以訪問外部類的成員 System.out.println(myField); } } ``` inner class可以訪問outer class的member,即使它是private(因為還是同屬於outer class以內 ```=java= class OuterClass { private String myField = "Hello, Outer World!"; class InnerClass { private String myField = "Hello, Inner World!"; void printMyField() { // 內部類別讀取自己的變數 System.out.println(myField); // 內部類別讀取外部類別的變數 System.out.println(OuterClass.this.myField);// } } void testInnerClass() { // 創建內部類別的實例 InnerClass myInner = new InnerClass(); myInner.printMyField(); } } public class TestInnerClass { public static void main(String[] args) { // 創建外部類的實例 OuterClass myOuter = new OuterClass(); myOuter.testInnerClass(); // 輸出: Hello, Inner World! // 輸出: Hello, Outer World! } } ``` 如果inner class有自己的local member,那要存取outer class的member且同名時 使用 - 外部類別名.this.變數名 - 外部類別名.this.方法名() 來進行訪問 ::::warning **tips** 如果內部類別和外部類別有同名的變數或方法,內部類別會默認訪問其自身的變數或方法。 :::: # 補final ::::warning **recap:** - public:可以被所有package,所有class 訪問 - protected:只能在同個package內,但如果別的package有extends該class的子類別的話,也可以訪問 - default(package-private):嚴格限定在同個package - private:只能在同個class之內訪問 :::: ![image](https://hackmd.io/_uploads/HyjowvTlR.png) [來源](https://blog.csdn.net/yangbodong22011/article/details/49721691) ## Wrapper classes | primitive type | wrappered class | | -------- | -------- | | boolean | Boolean | | byte | Byte | int |Integer | double |Double | float |Float | long |Long | short |Short | char |Character ::::info primitive type必須要包成object(wappered class)才能使用一些好用的方法 像是ArrayList,Hashmap就只能放object :::: ### autoboxing/autounboxing: 在 J2SE 5.0 之後提供了自動裝箱的功能 ``` Integer num=5; //直接宣告不需要寫出new一個object ``` ```=java= public class TestThis { public static void main(String[] args) { int original_num=10; Integer boxed_num=original_num; //auto-boxing System.out.println(boxed_num+5); //auto-unboxing Integer boxed_num2=5; //auto-boxing int original_num2=boxed_num2; //auto-unboxing System.out.println(original_num2); } } ``` ::::danger ### Auto-boxing/Auto-unboxing的使用風險: ```=java Integer integerObject = null; int i = integerObject; // 會拋出NullPointerException ``` integerObject=null auto_unboxing成primitive type時,變成primitive type沒有賦值的error。 :::: ```=java= Integer a = 1000; Integer b = 1000; Integer c=50; Integer d=50; System.out.println(a == b); // 輸出 false System.out.println(c == d); // 輸出 true ``` a&b根據我們所知道,個別創立新物件,address不同,所以印出false 但c&d會印出true??? ::::success 對於介於 -128 至 127之間的整數,Java 會自動將其緩存(caching),所以當我們創建該範圍內的 Integer 物件時,Java 會直接從緩存中取得物件,而非創建新的物件。 :::: ::::warning **tips:** 用.equals()可以比較物件的value,not address :::: ::::success 既然都有了wrapper class,那為什麼java不就不要有primitive type data,都用wrapper class就好? 這是因為基本型別(Primitive Type)的效能優於封裝類別(Wrapper Class)。基本型別直接儲存值,而封裝類別則儲存一個物件,該物件包含了實際的值。因此,存取基本型別通常比存取封裝類別更快,且佔用的記憶體空間也較小。 ::::