# Java進階-類別大全、物件導向(已刪除作業部份) ★Java 物件導向程式語言設計三大特性: ### 1.繼承性 ( Inheritance ) 1. 定義:類與類的一種關係,Java中是單繼承,一個子類只能有一個父類,,一個父類可以有多個子類。 1. 作用 : 避免代碼重複,有利於代碼的重用。 2. 父類 : 所有子類所共有的(不管子類延續幾層,都會有這個父類別,多層繼承的概念)。 3. 子類 : 子類所特有的屬性和方法。(只有這個級別的子類及他延伸的子類才會有的) 4. 子繼承(extends)父後 : 子具有 : 父 + 子。 1. 單一繼承、多接口實現、傳遞性。 2. 子類有自己的屬性和方法, 3. 子類可以重寫父類的方法; 4. 子類擁有父類非private的屬性和方法 ### 2.封裝性 ( Encapsulation ) 1. 定義: 封裝就是將類的資訊隱藏在類內部,不允許外部程式直接訪問,而是通過該類的方法實現對隱藏資訊的操作和訪問。 1. Class : 封裝的是Class的屬性和行為。 2. Function: 封裝的是具體的邏輯功能實現。 3. 訪問控制修飾符 : 封裝的是訪問的權限。(EX: public private protected default) 2. 實現方法:使用private關鍵字修飾 類內部的屬性; 3. 如何訪問:在類內部定義get、set方法,實行使用物件呼叫方法對內部屬性進行操作; ### 3.多型性 ( Polymorphism ) 1. 定義:指的是是同一個方法呼叫,由於物件的不同可能會有不同的行為; 1. 意義 : 行為的多型。Class的多型。 2. 向上轉型、強制類型轉換、instanceof。 3. 多型的表現形式 : 重寫 Override + 多載 Overload。 2. 多型是方法的多型,不是屬性的多型,(多型與屬性無關) 3. 多型的存在有三個必要條件:繼承、方法重寫、父類引用指向子類; 4. 父類引用指向子類後,該父類引用呼叫子類重寫的方法,此時多型就出現; **多型記憶體分析**: 在將父類引用指向子類之後,反編譯之後卻發現呼叫的仍然是父類方法,其原因是因為 將父類引用指向子類物件後,在堆中產生的子類物件擁有父類的非私有屬性,同時在編譯時,在方法區產生一個方法表,頂部存有物件的地址,其餘是父類和子類可以呼叫的方法;在編譯時,將子類方法的地址賦值給父類方法,因此呼叫其實父類方法的地址是指向了子類的方法 ## 1.類別:基礎概念 1. Student s1 = new Student(), () 是代表建構子的意思,裡面放的就是要初始化的值, 建構子是不能有回傳值(連void都不行),一定要跟類別同名。 如果只有一個類別,系統會預設建構子,但一旦有建立一個,系統就不會預設了,所以需要寫空引數時的情況。 2. 建構子沒有傳回值,也不可以用void修飾、且名稱必須跟類別名稱相同 3. java 純物件導向,所有東西都會放在類別裡面(class),類別是一個參考資料型態, 4. 如果要同時儲存多種資料型態或是在重複當作參考資料的資料都可以另外打成class 5. 類別是一個模板,定義做出來的東西是什麼,一個類別裡面的函式就是方法(EX:貓的行為,跑,跳)。一個類別裡面的屬性就是屬性(EX:貓的毛、顏色)。 6. java 的每一個檔案,只會有一個class(netbeans 每一個頁面只有一個class) ,但可以在class裡面新增class的名稱,稱為內部類別(下面會詳述)。 7. 通常類別都是public ,但類別屬性95%都會是private。 8. 基本上是物件去呼叫function ,而不是類別,除非是static的function ( 會被歸類為成員方法,因此使用類別名稱 . 成員方法名稱呼叫) 9. this 是當前操作的物件本身 10. 因為每個物件存取時都需要建立一個存取器,跟一個讀取器,因此在後面java延伸出來的語言,有的有修正功能,變成預設。 11. 設計的高度,就是降低各個class之間的偶和,也就是讓彼此不要相互影響太深,越獨立(但要拿的到跟存取的到)越好,最好的做法也就是把每個種類單獨用一個陣列打包,將要做的事情在裡面做完。 12. Java 物件導向程式語言設計的兩個精神 1.抽象化 ( Abstraction ) :物件抽象化有助於了解並實際掌握物件的內容,物件可以被良好的定義及描述。 2.繼承 ( Inheritance ) : 賦予程式能重複使用物件以增加其延展性。 13. ★正常來講,在設計程式時,類別中是不會放入input (Scanner)的,因為這樣會沒辦法從外部匯入資訊,除非該class是用來處理指令的 EX: Input class。 ### 範例code ```java package test; import java.util.Scanner; import java.lang.Math; public class Test { public static void main(String[] args) { Student s1 = new Student(); //new Shape() 就是物件,s1 是承受他的,所以s1 是物件 object //實體 instance 是指該類別創建出來的物件,才是那個類別的實體物件 //ex: Scanner sc = new Scanner(System.in); sc 是 Scanner 的實體物件,但不是Shape的 s1.setScore(50); Student s2 = new Student("Mark", 97); //s2 是 Shape 的 instance s2.setScore(75); System.out.println(s1.compare(s2)); } } // public static void main(String[] args) { // Student s1 = new Student(); //new Shape() 就是物件,s1 是承受他的,所以s1 是物件 object // //實體 instance 是指該類別創建出來的物件,才是那個類別的實體物件 // //ex: Scanner sc = new Scanner(System.in); sc 是 Scanner 的實體物件,但不是Shape的 // Student s2 = new Student(); //s2 是 Shape 的 instance // s1.test(); // s2.a = 4; // System.out.println(s1.a); // } package test; public class Student { // 類別跟Function 一樣,要剛剛好就好,不要太多功能也不要少,形狀就處裡形狀的事情。 // 封裝性 public int a; private int score; public Student(){ //預設的建構子,如果都沒建的話JAVA會建一個預設的建構值,但如果有創一個了,就不會有預設的 //不能在建構子內 new student 不然就會無限迴圈。 } public Student(String name){ //建構子, ()內可以放入參數 this.name = name; } public Student(String name, int score){ //建構子一樣可以overload 跟function一樣 this.name = name; this.score = score; } // mutator / setter 存取器 public void setScore(int score) { if (score < 0 || score > 100) { this.score = 0; return; } this.score = score; } // accessor / getter 讀取器 public int getScore(){ return this.score; } public void test() { // function 裡面不能有封裝等級 int a = 3; this.a = 5; // this 是當前操作物件本身 (外面如果是用s1 那this.a 就是 s1.a 如果是tmp1 那就是tmp1.a } } public void setName(String name) { this.name = name; } // accessor / getter 讀取器 public String getName(){ return this.name; } public int compare(Student student){ // 可以帶入物件 直接看需要物件的哪個屬性相比 return this.getScore() - student.getScore(); //用getScore() 是因為避免 getScore 如果裡面有修正數量,會讀取到厝的(只有原始的score),this 是可以省略的,只是拿來認明是哪一個的,要省略就大家一起省略 } ``` ## 2.Object介紹 1. 改用 @override String toString() 方式印出來將字串傳回,因為在Object類別中,印出System.out.print() ← 括號內可以放object型態(EX:參考資料型態),因此會內見自己轉成object.toString,因為只要是字串串接,就會觸發toString。 2. 改用 @override String toString() 方式印出來將字串傳回,因為在Object類別中,印出System.out.print() ← 括號內可以放object型態(EX:參考資料型態),因此會內見自己轉成object.toString,因為只要是字串串接,就會觸發toString。 1. equals(Object obj) 如果執行該方法的物件變數與引數的物件變數參考了同一個物件實體,則回傳true,否則回傳false。 1. getClass()取得物件所屬的類別,它回傳的是一個Class型態的泛型,我們可以用一個內建Class類別的物件變數來接收,並且該物件變數也可以直接由println()輸出內容←類別的名稱EX: calss java.lang.String。 1. 在Java中,除了巢狀類別及Object類別之外,所有的類別都有父類別,預設值是將Object類別(java.lang.Object類別)當作父類別。 ### Object的method詳見10-1繼承 ![](https://i.imgur.com/HLNL3oN.png) ![](https://i.imgur.com/6Jn2Pzr.png) ## 3.存取修飾詞-static 1. static(靜態資料) 會在 global 記憶體中,main class就是一種,所有在global的資料,會在程式一開始執行時,就會存在了,所有靜態資料( 變數)會在globla - 生命週期就會是程式結束,但因為java 的記憶體較特殊function 會存在別的記憶體,但生命週期是一樣的,有static 。 2. 靜態內部類別沒有this參考指標,但是如果是靜態內部類別的non-static成員,就會有this可以指向自身物件。 3. 使用static 代表宣告的是類別方法或類別變數, 沒有加的話代表是物件方法、物件變數。 4. 在import時,最後指向的一定是一個static成員(方法、屬性、類別、內部類別(ex: enum),或是萬用字元 * ex: import static java.lang.System.out; / import static java.lang.System.*; ### 存取修飾詞表格 不同pachage的非子類只有public 可存取 ![](https://i.imgur.com/NjSTkcX.jpg) ### 範例code ```java package test; public class Test2 { private static int serial = 0; //靜態資料可以在外面初始化 private int a; private final int b; private static final double PI = 3.14d; //常數 在一開始就靜態+final 讓它存在 // ,並且不讓人修改,常用於將特定數值鎖定住,避免有人手輸錯,命名規則為全大寫 public Test2(){ serial++; // Test2.serial 是省略了Test2. 再創毽子時,用this 也OK 因為創建時一定有操作到test b = 5; } public static int getSerial(){ // a--; 因為 static getSerial 這個方法在程式中會先被創建, // 但a是實體物件創造出來時才會有的值,因此在執行方法時不一定有a 要另外處裡 return serial; } } ``` ## 4.存取修飾詞-final final 修飾詞就像鎖定地的功能有三種狀況: 1. final代表最後的方法,不能再被修改,鎖定變數,但在建構子(創建的時候) 可以更改,其他時候無法。 2. 宣告變數時: - 該變數只可以設定一次,就沒辦法改了(所以要嘛在一開始初始化,不然就要在物件建構子時設定。 - 如果是用在物件變數,則只能指向第一次指向的物件實體。 - 用在成員變數時,除了設定第一次以外,包刮繼承後都不能改變其值。 3. 宣告類別的method時:繼承後不能被覆寫override。 4. 宣告類別(class)時:不能被繼承(終止繼承)。 ## 5.物件繼承 1. 物件導向的目的:避免重複的code、讓辛苦肝出來的code更有利用價值、更好閱讀及管理、更好達到效率、彈性擴充。 2. 物件導向就是一個會讓速度比較慢的方式,但是優點是好更新,最快的方法就是把全部寫在main function 裡面,但很難更新修正。 3. package的封裝等級有點像public 同個package的都可以用,可參閱08/24本周筆記存取修飾詞表格。 4. protected:封裝等級,只有繼承的人可以用,傳家之寶~ 5. Override: - override 傳回的資料可以是該類別的子類別EX:Animal → DOG/CAT/MOUSE。 但不同的就不行。 - override:覆寫 使用時前面加@ ,就像是在跟別人說,我借你的code改唷~ (但限制還是跟原本傳承一樣,覆寫的限制就是要同署名,還有可以給編譯器看(好處是編譯器可以幫你先判別是有override成功,有錯誤會跑出紅線),基本上都要強制加! - 看是繼承誰,super就抓誰的方法。 - 程式會優先執行最下層子類別的Override的Function - 覆寫override 回傳型態必須跟原方法相同、方法中的參數列不論數量、資料型別及擺放順序都必須相同 ( 也就是相同署名)、存取權限不可小於原方法。 - override 父類別有拋出例外事件時,子類別可以選擇是否要拋出例外,若要拋出例外地話要與原方法相同,或原方法例外是件類別的子類別。(例外事件尚未教到) - 子類別可以超載 overload 父類別的方法,但是就算是新創,而非override 父類別無法直接使用。 - 如果父類別的方法是用private 或是 final 設定的話,子類別是沒辦法 override的,雖然同名但是是兩個不一樣的function,因此常會用@override 讓編譯器去判別有沒有override成功,錯誤會有紅字。 6. super:就像this,只是this是指當前操作的物件,super是指你繼承的物件(一等親的爸爸喔,不是祖先!) - 會有super是因為兒子可以取跟爸爸一樣的變數名稱,爸爸會很體貼的把自己的變數名稱隱藏起來,所以如果兒子取了一樣名字,沒有用super會叫到兒子的變數(this)。 - 但是方法就不一樣了,爸爸怕你抄錯答案,所以會強制你的格式(傳進跟傳回資料型態要跟原本一樣、名稱)要跟他一樣,也就是覆寫(可以不加override),裡面的內容就可做變化, - 但如果要用不一樣的資料型態,就會變成多載,但是這個方法就會變成兒子的新方法,而非爸爸的。 - 多載必須是同名但不同署名。而覆寫則必須是同署名。 7. extends (繼承):JAVA只可以單一繼承,也可以多層繼承,但不能多個繼承(只能有一個老爸拉~ 但某些語言可以唷!EX: C) - 基底類別:父類別,爸爸:也可以用兒子的,但兒子有自己的秘密,他自己新開的變數、方法,爸爸沒辦法直接呼叫。 - 會這樣用的時候:宣告一個參考變數,宣告型態時採用父類別型態,但卻將它指向子類別物件實體。 ex: OA father = new OB(); ←OB是兒子 - 衍生類別:子類別,兒子:爸爸基因很好,爸爸的變數、方法除了private 、default、建構子、Final的不給你之外都給你。 8. 繼承的執行順序,因為兒子不會憑空出現,所以會先執行爸爸的建構子,才執行兒子的,因此通常都會把爸爸的變數初始化,放在建構子內,才會被摳到。 如果是兒子的建構子內要呼叫爸爸的,除了要加super以外,要記得放在第一行,時間錯誤你就不是他的兒子了! 9. 可以繼承Java原本的類別,但需要先import進來後才可使用。 10. 繼承雖然父類別有些東西不給你,但還是會在記憶體空間中放入這些資料,只是無法直接拿取,需要透過間接存取,也就是用return 那到值,因此雖然有protectd封裝等級,但大部分還是private 11. 多層繼承:也就是一代傳一代會越來越龐大,在執行時,會先追回到最原始的祖宗後才開始一層一層傳回來。 ### 示意圖 ![](https://i.imgur.com/uSsGFmE.png) ![](https://i.imgur.com/vJDf3I4.png) ## 6.物件多型簡介 1. 一個類別僅能繼承一個類別,但可以實作多個介面 抽象類別用繼承extends; 實現介面用介面 implements 2. ★多型的重點是唯一可以實現,在執行時期再決定物件的型態,EX:用陣列存取,但每個值都是不同型態(類別)。 3. 多型父類別可以 = 子類別,但只能使用父類別的方法,以及子類別使用改寫功能的function。 4. 加上static後,就會是該類別的成員,因此如果在類別的function前加上static 會變成該類別的成員,因此後面的子類別是沒有辦法Override 去覆寫這個方法的唷! ## 7.物件多型-介面 ```java 可以繼承+介面,介面可以有兩個以上。 public class Rectangle extends Shap implements Test1, Test2{ } ``` 1. 介面( Implements):可以生成物件(new),只能有未實作的方法(抽象方法)、常數(public static final,偶爾用,不常),其他都不能有,子類別要用的話要用介面:implements (名詞稱為實現介面)。 - 感覺主要在賦予類別行為,可達到彈性 ,宣告方式為: - 第一層(定義介面,父類別): [封裝等級] Interface 介面名稱 + [extends (可以繼承,也可以不要)。 (在netBeans 新增時要選擇new interface 才會跑出介面) 在第一層時,只可以在介面中宣告靜態變數(static),但非靜態的不行。 - 第二層(實現介面,子類別):[封裝等級] class 子類別名稱 implements 介面名稱 (類別(子類別)實現(父類別)介面。 (但是第二層以後,都跟平常一樣是選擇new class) 且在第二層時,可以宣告成員變數,不一定要靜態變數。 - 在介面中,可以省略 abstract 不寫(直接加分號)。 2. 介面的第一層 interface 的屬性一定要自行給定初始值,因為他會直接在編譯時用public static final來修飾(就像enum一樣),且interface的function沒有實作程式區塊,因此要以分號結束(因為該function 在編譯後,也會固定加上public abstract (抽象方法)來修飾),所以都可以省略public static final , public abstract。 3. 介面如果當父類別,跟繼承相同,往下指子類別時,就只能用有Override介面的方法。 4. 介面裡面的抽象方法,一定要都有被實現,才能使用方法。 5. interface 是不能直接new 物件實體的,要利用 implements 才可以開始實作(開立一個class 並做implements宣告,將方法實作區塊完成- 此class會被稱為介面類別),因為上面的interface 裡面的資料都是public (強制) ,所以implements 都可以直接使用該變數、Override該方法。 同理,因為interface的變數有final修飾,因此沒辦法去更改變數。 6. 介面可以多重繼承(只有介面可以喔!),只能繼承介面類別,宣告方式如下: 介面名稱 extends 介面類別1, 介面類別2, ...... 介面類別n 7. 要優先考慮介面設計(可擴充性高!),因為可以讓不同物件類別,但會同時使用到的動作,作為介面 EX: 移動方式 、動物類別、交通工具類別,都會需要用到move ,但動物跟交通工具是兩個不相關的類別。 - 透過介面設計讓類別擁有行為,稱為類繼承。 8. enum 可以透過implements 來實作介面類別,但自己不能被繼承 (能不能被implements不確定,但應該不能)。 9. 目前主流,會大量使用到關於介面的觀念,會在業界做到很多事情,動態細節(行為的差異)。 10. Java最新版本,是可以讓介面可以有實現的方法,但極少用到,忽略不用。 11. 可以利用Interface 製作成類別的公共父類別 EX: ![](https://i.imgur.com/lkFPrh3.jpg) ## 8.物件多型-抽象(abstract) - 與介面有很大的相關 1. 抽象類別( **Abstract**):不能生成物件(new),可以包含其他資料,子類別要用的話要用繼承:extends; 宣告方式為: [封裝等級] abstract class 類別名稱。 可以不new EX: Shape shape; 這樣就好,後面在用new Circle() or new Rectangle()即可。 陣列的話也可以 Shape[] arr = new Shape[5]; 這裡可以的原因是因為,Shape陣列裡面的每一個都還是 Shape 0 = null; 因此其實沒有new物件實體,可以宣告。 2. 當使用抽象類別時,可以僅宣告方法名稱,而不把方法寫完,也就是「抽象方法」(Abstract method);但可以是抽象類別,裡面沒有抽象方法。 3. 如果子類別繼承了父類別,沒改寫完抽象方法,在下一層的子類別,只需將目前所有的家族沒寫完的抽象方法寫完即可。 4. 要使用到抽象類別(父類別時)必須一定要用子類別呼叫(所以才不能定義抽象建構子,因為子類別執行時會先建構父類別)。 5. 如果一個類別中包括了抽象方法,則該類別稱之為「抽象類別」(Abstract class) 6. 只有抽象方法, 變數跟建構子不能用抽象,一定要可以宣告或使用。 7. 抽象類別是個未定義完全的類別,所以它不能被用來生成物件,它只能被擴充,並於擴充後完成未完成的抽象方法定義。 8. 如果是繼承抽象類別,在沒有把抽象方法的內容寫完前,都不能把abstract去掉, 9. 宣告為抽象方法時,後面不是加大括號,而是加分號 ; (沒有實作區) 10. 抽象類別有建構子,但是其封裝等級不能用private<-因為一定要被繼承。 11. 抽象類別型態的變數用途(繼承):因為物件導向程式設計的多型特性,父類別物件變數可以指向子類別物件實體,因此,抽象類別型態的變數只能**用於指向子類別物件實體**。 可以宣告一個比較上層的陣列,利用多型的特性,父類別只宣告抽象方法,子類別可以依照需求彈性變更內容。 12. enum 不可以被繼承,但可以撰寫abstract透過匿名內部類別的撰寫方式實作該列舉類別中所定義的˙abstract function。 ## 9.列舉 1. enum 是一個抽象類別,也是一個內部類別。 2. enum裡面的常數, 在程式開始時會變成 public static final (enum名稱) (變數名稱-enum裡的大寫單字) = new (變數名稱); 3. 因為enum其實是在幫忙把每一個變數名稱宣告成一個子類別,所以才會有創建子,成員屬性。 4. enum 一樣可以實現介面,因此每一個變數定義都可以放入實現的方法,以達到不用使用到switch 即可實現 5. 而抽象類別或是介面,雖然正常來說不能直接new,但如果加上大括號後,把裡面的function都實現了,就可以直接new出來。 6. 主程式要叫類別內的列舉方式: 類別.類舉名稱.類舉內容 Test2.Compare.MAX 7. 範例code ### 範例1 ```java 可以利用 .values()[i],取得列舉的值(資料型態 EX: Gender),i就是列舉陣列的位置,也就是依照命名順序來取自動為0,1,2... public static Person.Gender inputGender(boolean isRandom) { int v = input("請輸入性別", 1, 2, null, isRandom); return Person.Gender.values()[v - 1]; } ``` ### 範例2 ```java package test; public class Test2 { private int[] arr; enum Compare { MAX, MIN //命名規範,全大寫 } public Test2() { arr = new int[]{1, 4, 3, 5, 6, 9, 8}; } public void sor(Compare compare) { for (int i = 0; i < arr.length - 1; i++) { for (int n = 0; n < arr.length - i - 1; n++) { switch (Compare) { case MAX: if (arr[n] > arr[n + 1]) { int tmp = arr[n]; arr[n] = arr[n + 1]; arr[n + 1] = tmp; } break; case MIN: break; } } } } } ``` ### 範例3 ```java package test; import java.util.Scanner; import java.lang.Math; public class test { public static void main(String[] args) { Student s = new Student(); // enum == static final // 所以enum 的生命週期 == 程式的生命週期 // 所以MALE / FEMALE 的數值 會跟程式的生命週期一樣長 System.out.println(Student.Sex.MALE.getValue()); System.out.println(Student.Sex.FEMALE.getValue()); System.out.println(s.getSex().getValue()); // s.getSex() == Student.Sex.MALE } } package test; public class Student { public static final String SEX_MALE = "男"; public static final String SEX_FEMALE = "女"; public enum Sex{ //列舉一樣可以設public private 封裝等級 MALE("男"), //呼叫建構子 FEMALE("女"); //程式創建的時候建構FEMALE private String value; //一樣可以添加屬性 Sex (String value){ //建構子 this.value = value; } public String getValue(){ return value; } } private Sex sex; public Student(){ } public void setSex(Sex sex){ this.sex = sex; } public Sex getSex(){ return sex; } } ``` ## 10.內部類別(巢狀類別) ### 1.基本內部類別 1. 原本的類別 = 外部類別; 原本的類別裡面的類別或介面 = 內部類別 or 內部介面。 2. 外部類別、內部介面,本身就是靜態類別(靜態 = 存在golbal 一開始就有)。 ★介面會一定是靜態的原因,是因為介面裡面只能有常數、未實現的方法,因此介面不可能new一個外部類來拿到資料,所以java才會預設介面一定是靜態,不然會有可能介面完全無法使用(無法帶資料進去)。 內部類別,要加上static修飾後,才會是靜態類別。 3. 使用方式 、抓取資料規則: 1. 內部類別為非靜態時: 內部類別必須依賴外部類別的實體才能被創建。 - 創建方法:先建立外部類別的物件實體,在由此物件實體去呼叫內部類別(創建)。 因為內部類別可以拿到外部類別的屬性,所以一定要先創建外部類別的實體(否則該屬性不存在,會造成錯誤)。 - 抓取資料規則: 1.外部類別:如果要拿到內部類別的資料,只要先new自己的內部類別,就可以拿到屬性成員,不論任何封裝等級,因為封裝等級最低同個class都可以抓。 2.內部類別:如果要拿到外部類別的資料,不需要先new外部類別,可以直接抓取,因為非靜態時,一定會先有外部類別,才有內部類別,因此IDE會自動編輯成 該類別的this.變數 (EX: Test.this.a = 7;)。 2. 內部類別為靜態時:靜態類別的內部類別不須依賴外部類別的實體就能被創建。 (記得內部介面都是靜態唷!) - 創建方法:先透過外部類別(但不用創建實體)去呼叫,但不用new物件實體,EX: Test.interface 。 會需要透過外部類別是為了確認是哪一個類別的內部介面或內部類別。 - 抓取資料規則: 1.外部類別 - 與非靜態一樣。 2.內部類別 - 要先new外部類別,才可以直接抓取。 因為沒有外部類別的物件實體(有可能還沒被創建), 但外部類別就是靜態阿,為什麼會沒有? 因為呢外部類別原始就會存在golbal 但是他的屬性如果沒有賦予static修飾的話,就只會在該類別被建立時才會出現,而不會在一開始就出現在golbal,也就是為何原本在class要把屬性成員加入static後,才能在其他class中直接調用。 4. 如果這個類別只為了外部類別所服務時,就會創建內部類別或內部介面,但是大部分還是會設成public 因為main 或是其他地方如果需要得到這個方法處理後的資料,就會需要使用,如果不想給別人用,就可以設成private等級。 5. 為了增加程式的彈性,常用的方法會把介面的實現放在外面,在類別那只留下一個介面當作接口,這樣這個介面要怎麼樣的規則都可以在其他地方定義他,因此介面會把功能切得比較細(EX: 排序、篩選...) 6. 理想的情況就是一個類別都是被封裝起來的,讓外面的人要使用這個類別,都只能透過間接存取(包含行為),透過介面來做橋樑。 #### 範例code: ```java Main: public static void main(String[] args){ // 1.內部類別必須依賴外部類別的實體才能被創建 Test.InnerTest it = new Test().new InnerTest(); // Test test = new Test(); // Test.InnerTest it = test.new InnerTest(); // 2.靜態類別的內部類別不須依賴外部類別的實體就能被創建。 Test.InnerTest it2 = new Test.InnerTest(); } --------- public class Test{ private int a; public int b; public Test(){ this.a = 10; this.b = 5; } public static class InnerTest{ private int c; public InnerTest(){ Test t = new Test(); t.a = 7; // 在非靜態類別時: 可以不用先new t 直接寫 a = 7 ,因為 a=7 可以= Test.this.a = 7; c= 5; // = this.c = 15; } } public void funcA(){ InnerTest it = new InnerTest(); it.c = 7; } } ``` ```java public class StudentArr{ public interface Comparator{ public void compare(Student s1, Student s2); } } //開新的class 利用SeatComparator 實現學生class裡的Comparator介面方法 public class SeatComparator implements StudentArr.Comparator{ @Override public boolean compare(Student s1, Student s2){ return s1.getScore() - s2.getScore() > 0; } } ``` ### 2.建造者模式 Builder Pattern - 就是一個內部類,只是功能是拿來讓外部類可以有條件建構 [含鍊式調用(一小部分)] 可在參考商品管理系統code 1. 建造者模式是:用來建構外部類的方法 ,用於外部類別如果在建構時,需要複雜的條件時使用的,基本上都是靜態的內部類別,因為是為了要讓外部類可以依條件建構而產生的。 其中一種解決方式:建立一個內部類別並設定屬性與外部類別屬性相同,再利用set改變這些屬性,在更改完後,再利用建構子將這些屬性傳回外部類別。 2. 鍊式調用就有點像是,原本這個方法拿到了一個實體物件後,還要把這個實體物件做另一個方法時,讓他傳回下一個實體物件要用的物件實體,這樣就可以一直串接下去,不用另設變數去暫存他,程式碼也簡略。 3. 以上述的方式,可套用鍊式調用,讓每次的set都回傳實體物件,已讓程式不斷串連下去,只需一行即可完成所有動作。 #### 範例Code ```java // 例如這邊的 t 要求在建構的時候 int a,b,c 要求是可以分開,不一定要三個都有數值。 那我們就沒辦法確認 Test t = new Test.Builder().setA(4).setB(7).gen(); // 此為鍊式調用一個方式 /* 原方法: Test t; Test.Builder tmp = new Test.Builder(); tmp.setA(5); tmp.setC(7); t = tmp.gen(); */ public class Test{ public static class Builder(){ private int a; private int b; private int c; public Builder setA(int a){ this.a = a; return this; } public Builder setB(int b){ this.b = b; return this; } public Builder setC(int c){ this.c = c; return this; } public Test gen(){ return new Test(a, b, c); } } //不在上面new Test改變a,b,c 是因為如果gen 被call第二次,那會是一樣的東西不會改變。 private int a; private int b; private int c; // 這裡如果不想讓外面可以直接透過Test建構子的話,可以把等級設成private 強制其透過Builder 創建Test的實體物件 public Test(int a, int b, int c){ this.a = a; this.b = b; this.c = c; } } ``` ### 3.匿名內部類別 1. 匿名內部類別的方法: new 你要繼承的抽象類別或實現的介面名稱() + {使用的方法} + ; 2. 重點注意! 匿名內部類別 你創建出來的是一個介面或類別的子類別(沒有名字的子類別),去繼承或實現他。 3. 如果要保留匿名內部類別,可以用參考資料型態的變數來去存取他,因為某個類別被new出來後,就是物件實體,也就是一個變數。 4. 匿名內部類別可以取得方法內的變數,但是一定要是final的變數才可以放進去,因為執行的時候不確定變數是否是哪一個變數(有無經過修改)。 但如果是放入參考資料型態變數,由於參考資料型態對於變數來講,只是一個記憶體位置,因此可以改變裡面的值,對於變數來講,還是只到同個記憶體位置,所以可以讓變數改變。 5. 匿名內部類還是會在java裡面產生一個類別的檔案,會以$符號標示,只是不會產生.java檔。 #### code ```java 匿名內部類還是會在java裡面產生一個檔案,只是不會產生.java檔,會以$符號。 //new XXXXXX implements StudentArr.filter(){} final Student s1 = new Student(1,1,""); s1.name = "345345"; //該類別的內部介面 變數名稱 = 創建 該類別的子類別 回傳。 //簡單來說,就是StudentArr.Filter(){} = 一個Filter的子類別或介面,大括號內就是Override的內容 StudentArr.Filter filter = new StudentArr.Filter(){ @Overrife public boolean filter(Student student){ System.out.println(s1); // 注意如果s1是基本資料型態,會沒辦法改變,因為值是不能被改變的,參考資料型態可以是因為指向的是位置 //,所以改變值對該變數來說位置沒變,所以可以。 return student.getSeat() < 10; } ``` ### 4.lambda - 讓匿名內部類更簡潔、效能更好的方法 [線上補充資料](https://tw-hkt.blogspot.com/2019/06/java-java-8-lambda.html) 1. 只有java 8 以後才有lambda,效能會比較好,因為lambda只會產生出方法,不會產生類別檔案。 2. lambda,只有在介面的方法只有一個的時候可以使用,會自動省略方法名稱、類別名稱,如果方法比較多行時,就要用大括號包覆,及寫return,但如果方法只有一行時,可以省略大括號、return 3. 宣告方法: (宣告參數)+(箭頭->) + (方法) + ; ← 就等於 new StudentArr.Filter(){方法}; 匿名內部類 小提醒!宣告參數,也就是原本filter 方法寫的參數,不需要寫實際放進去參數。 4. 如果用lambda在寫鍊式調用時,只需要在需要放入內部介面或內部類的地方,直接以寫 = 右邊的資訊即可。 EX: StudentArr.Filter filter = (Student student) -> student.getSeat() < 10; arr.where( (Student student) -> student.getSeat() <10); 這樣即可 ```java (帶入參數)(箭頭) -> (方法) return student.getSeat() < 10; StudentArr.Filter filter = (Student student) -> {return student.getSeat() < 10}; 如果方法只有一個 且是return 的話可以節省大括號、return StudentArr.Filter filter = (Student student) -> student.getSeat() < 10; ------------- /*上述寫法如果寫成正常的匿名內部類為: StudentArr.Filter filter = new StudentArr.Filter(){ @Overrife public boolean filter(Student student){ return student.getSeat() < 10; //lambda只寫這個 } */ 如果再沒有回傳值、沒有參數、只做一件事情時,可以直接寫成這樣,但很少用到。 T t = System.out::println; /* T t = new T(){ @Override public void func(){ System.out.println(); } } */ ``` #### 範例code ##### StudentArr ```java public class StudentArr { public interface Comparator { public int compare(Student s1, Student s2); } public interface Filter { public boolean filter(Student student); } public interface Action { public void action(Student student); } private Student[] arr; private int count; public StudentArr() { arr = new Student[2]; count = 0; } public StudentArr where(Filter filter) { StudentArr tmpArr = new StudentArr(); for (int i = 0; i < count; i++) { if (filter.filter(arr[i])) { tmpArr.add(arr[i]); } } return tmpArr; } public StudentArr sort(Comparator c) { StudentArr tmpArr = new StudentArr(); for (int i = 0; i < count; i++) { tmpArr.add(arr[i]); } for (int i = 0; i < count - 1; i++) { for (int n = 0; n < count - i - 1; n++) { if (c.compare(tmpArr.get(n), tmpArr.get(n + 1)) > 0) { tmpArr.swap(n, n + 1); } } } return tmpArr; } public StudentArr each(Action action) { for (int i = 0; i < count; i++) { action.action(arr[i]); } return this; } public void add(Student student) { if (student == null) { return; } if (count == arr.length) { doubleArr(); } arr[count++] = student; } public Student get(int index) { if (index < 0 || index >= count) { return null; } return arr[index]; } public int length() { return count; } private void doubleArr() { Student[] tmpArr = new Student[arr.length * 2]; for (int i = 0; i < arr.length; i++) { tmpArr[i] = arr[i]; } arr = tmpArr; } private void swap(int i1, int i2) { Student tmp = arr[i1]; arr[i1] = arr[i2]; arr[i2] = tmp; } } ``` #### Main ```java package pkg20190819; public class Main { public interface T{ public void test(); } public static void main(String[] args) { StudentArr arr = new StudentArr(); for (int i = 0; i < 10; i++) { arr.add(new Student( Input.number("座號", 1, 100), Input.number("分數", 0, 100), Input.str("姓名", 2, 3) )); } arr.each((Student student) -> System.out.println(student)); System.out.println("-------------------"); arr.where((Student student) -> student.getSeat() < 70) .where((Student student) -> student.getScore()< 70) .each((Student student) -> student.setScore(student.getScore() + 100)) .sort((Student s1, Student s2) -> s1.getScore() - s2.getScore()) .each((Student student) -> System.out.println(student)); // 匿名內部類別 System.out.println("-------------------"); } public static void swap(int[] arr, int i1, int i2) { int tmp = arr[i1]; arr[i1] = arr[i2]; arr[i2] = tmp; ``` ## 11.補充知識 1. 如果要讓引數有多個 就是 資料型態單個的 + " ..." + 陣列名稱(可自設)( EX: Monster...monsters ) 2. 物件型別轉換: 1.隱含式轉換 Casts Up : 是系統自行轉換,將範圍較大的物件改為較小 EX: 多型 2.強制型別轉換 Downward Casts : 請搭配instanceof (向下轉型) 以判斷該物件是否屬於此類型後,再強制轉型-在物件前面加上 ( 強制轉型的資料型態 ) EX: Animal animal = (Animal) new Dog(); 3.instanceof 是用於向下轉型,EX: Animal tmp = new Dog(); tmp instance of Dog; 用以判斷tmp(Animal) 到底是不是子類別Dog 還是其他。 ###### tags: `CMoney7th-戰鬥營學習筆記`