###### tags: `Java` `類別` # Java自學紀錄 - 物件導向的程式設計 ## 非程序導向 早期的程式語言,並沒有內儲副程式(程式庫)。當我們開發新的程式時,如果其功能和另一程式很相近,我們就會將那段已完成的程式複製加以利用。久而久之,這些程式很難弄清楚是誰複製誰的,彼此之間也難再共用某些程式碼,當遇到錯誤時或想新增功能時,更是難逐一修改所有程式。 ## 程序導向 為了解決以上程式碼公用的問題,各編譯廠商開始提供一些大家常用的函式,比較有規模的公司會將常用的函式集中在一個函式庫中,旗下的軟體產品一律呼叫這些標準的函式庫,而不是從函式庫中複製出來修改,此即為程序導向的程式設計。 ## 物件導向 當我們形容自己一個人時,會有以下幾種敘述,它叫做Lucas,身高175,體重67,具有打籃球,跑步的行為。人即為『**物件**』,名稱、身高、體重則稱為『**屬性(Property)**』,而打籃球、跑步則稱為『**行為(Behavior)**』。 既然真實世界是以物件來描述物種,程式設計亦不應侷限在狹隘的方法,而是應以物件的宏觀角度撰寫程式,所以基於物件導向的新觀念,程式開發工具即制定一種新的型別,稱為**類別**。 --- 每一個類別都有屬於自己的屬性方法。例如,程式導向的時代,關於開門的函式有**電梯開門**、**汽車開門**、**房子開門**等數種開門的方法,但要如何知道是要哪一種的開門? 撰寫程式時我們會用**電梯.開門**、**汽車.開門**、**房子.開門**(物件與方法間以(.)運算子連結)。 其次,物件導向亦提出了三個觀念,分別是物件的**封裝(Encapsulation)**、**繼承(Inheritance)**、**多型(Polymorphism)**,以解決程序導向的不足。 # 類別與物件的設計 ## 類別的設計 ```java= [存取範圍修飾字] class 類別名稱 [extends 繼承類別名稱] [implements 介面名稱]{ [存取範圍修飾字] 資料型別 資料名稱1(=初始設定); [存取範圍修飾字] 資料型別 資料名稱2(=初始設定); [存取範圍修飾字] 傳回值資料型別 方法名稱1(參數列)[throws 例為名稱1] [存取範圍修飾字] 傳回值資料型別 方法名稱2(參數列)[throws 例為名稱2] } ``` >以下程式即定義一個類別Pass,共含有兩個``資料成員``score、result及一個``方法成員``dispose()。此類別的功能是判斷成績是否及格,則設定result為**及格**、**不及格**。 ```java= class Pass{ //資料成員 int score; String result; //方法成員 void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } } } ``` ## 類別的變數 完成類別定義後,即可新增一個或數個類別變數,則此類別變數稱為**物件**、**實體**或**實例**。有三步驟,分別是物件的宣告、物件的新增、物件成員的存取。 * 物件宣告 ```java= 類別名稱 物件名稱;https: Pass pa; ``` * 物件新增 ```java= 物件名稱 = new 類別名稱(); pa = new Pass(); ``` >合併以上兩步驟 ```java= 類別名稱 物件名稱 = new 類別名稱(); Pass pa = new Pass(); ``` * 成員的存取 ```java= 物件名稱.資料名稱; pa.score; 物件名稱.方法名稱; pa.dsipose(); ``` >**Practice 1** : 寫一程式來判斷成績是否及格 ```java= public class Java_9_2 { public static void main(String[] args) { Pass pa = new Pass(); int a = 77; pa.score = a; pa.dispose(); String b = pa.result; System.out.println(b); } } class Pass{ //資料成員 int score; String result; //方法成員 void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } } } ``` >輸出結果 ``` 及格 ``` >**Practice 2** : 設計一個程式計算電費 >A.100度以下,一度以3元計算 >B.100度以上,一度以5元計算 >輸出需繳幾元電費 ```java= import java.util.Scanner; public class Java_9_2_pratice { public static void main(String[] args) { Scanner in = new Scanner(System.in); int a; a = in.nextInt(); CountMoney cm = new CountMoney(); cm.temp = a; cm.Count(); System.out.println(cm.result); } } class CountMoney{ int temp; int result; void Count(){ if(temp <= 100){ result = temp * 3; } else{ result = temp * 5; } } } ``` >輸出結果 ``` 輸入 : 100 輸出 : 300 輸入 : 500 輸出 : 2500 ``` ## 建構子(Constructors) 於類別的建置中,他使用的方法較特別,因為建構子的名稱必須和類別名稱相同。 因為於物件建立的同時,此方法亦隨之自動執行,所以此方法通常用於設定資料成員的初始值。 >沿用 **Practice 1** ```java= public class Java_9_2 { public static void main(String[] args) { Pass pa = new Pass(); //宣告與新增物件時,即設定 pa.score = 22; pa.dispose(); String b = pa.result; System.out.println(b); } } class Pass{ //資料成員 int score; String result; //建構子 Pass(){ score = 77; } //方法成員 void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } } } ``` >輸出結果 ``` 及格 ``` ### 建構子多載 ```java= Pass(){ score = 77; } Pass(int value){ score = value; } Pass pa = new Pass(); //執行Pass() Pass pa = new Pass(77); //執行Pass(int) ``` ## 解構子(Destructors) **建構子**相對應的方法稱為**解構子**,它負責歸還建構子所配置的各項資源。但因為Java會負責歸還所有德資料,因此Java就沒有解構子存在的必要。 ## 資料封裝 像電視,它用機殼把一些零件開關包裝起來,只留下螢幕跟部分開關讓使用者欣賞節目。軟體程式也是如此,所以對於類別的規劃,我們應重視所有方法,欄位及屬性封裝。 ## 存取範圍修飾字 Java提供public、< default >(default表空白)、protected、private等存取修飾字,使成員有不同得封裝等級,以避免程式與類別庫間的干擾。 | 修飾字 | 說明 | |:--------- |:------------------------------------------------ | | public | **不同套件**的所有類別皆可存取 | | < default > | **同一套件**的所有類別皆可存取 | | protected | **同一類別**與其**衍生子**或稱**子類別**才可存取 | | private | 僅供**類別內部**可存取 | 基於資料的安全性,有些資料必須先檢查是否安全與有效,再將其放入,此時就要透過函式的存取,先給予檢查再放入。運用Pass()中的score來做說明。 ```java= public class Java_9_2 { public static void main(String[] args) { Pass pa = new Pass(); boolean c = pa.Setscore(-77); if(c){ //true時執行 pa.dispose(); String b = pa.result; System.out.println(b); } } } class Pass{ private int score; public String result; public boolean Setscore(int a){ if(a>=0 & a<=100){ score = a; return true; } else { System.out.println("input error"); return false; } } void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } } } ``` ---- ## static static僅能用於修飾資料與方法成員,也就是說static不能修飾類別。當自料或方法加上static時,我們稱此資料為**類別變數(Class Variables)**,或稱為**公用變數**,而未加static的成員則稱為**實例變數(Instance Variable)** 或是 **實例方法**。 ### 實例變數(Instance Variable) 先前我們所使用的都是實例變數,均要宣告新增物件時才能使用,每個物件都分配一個記憶體,所以每個實例變數都是獨立的,彼此間不互相干擾。 ### 類別變數(Class Variables) 在資料成員前加static修飾字時,此成員就稱為類別變數。類別變數可以不用宣告新增物件即可使用。例如Math類別中的PI,可直接使用類別名稱機上成員名稱,且中間以 **(.)** 連結。 類別變數宣告新增後,用的都是同一個記憶體,所以更改其中一個的值,++其他的值也會隨之改變++。 >example ```java= public class Java_9_2_static { public static void main(String[] args) { Pass1 pa1 = new Pass1(); Pass1 pa2 = new Pass1(); Pass2 sta1 = new Pass2(); Pass2 sta2 = new Pass2(); System.out.println("執行實例變數 : "); pa1.score = 10; pa2.score = 20; System.out.println("pa1.score = "+pa1.score); System.out.println("pa2.score = "+pa2.score); System.out.println("執行類別變數"); sta1.score = 30; System.out.println("sta2.score = "+sta2.score); //40 -> 30 sta2.score = 40; System.out.println("sta1.socre = "+sta1.score); //30 -> 40 } } class Pass1{ //實例變數 int score; } class Pass2{ //類別變數 static int score; } ``` ---- ## 物件陣列 輸入n表示有n筆測資 2~n+1行分別輸入測資 輸出結果(及格或不及格) ```java= import java.util.Scanner; public class Java_9_2_array { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n; n = in.nextInt(); Pass pa[] = new Pass[n]; //宣告Pass類別裡面的陣列 for(int i=0;i<n;i++){ int tmp = in.nextInt(); pa[i] = new Pass(tmp); boolean c = pa[i].Setscore(tmp); if(c) pa[i].dispose(); //true時執行 else System.out.println("input error"); } } } class Pass{ private int score; public String result; public Pass(int a){ if(a>0 & a<=100){ score = a; } } public boolean Setscore(int a){ if(a>0 & a<=100){ a = score; return true; } else{ return false; } } public void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } System.out.println(result); } } ``` >執行結果 ![](https://i.imgur.com/MPIkVOn.png) # 繼承(Inheritance) 任何新產品的開發大多不是無中生有,都是從舊有產品中繼承某些特性,再加入新的零件而成的一項產品。軟體的開發也是如此。 Java使用extends保留字繼承既有的類別。 例如要讓Pass2來繼承Pass類別的寫法 ```java= class Pass2 extends Pass{ } ``` 此時,Pass2可繼承Pass類別中的< default >,Public,pretected成員。 上式中的Pass為基礎類別(Base class),Pass2為衍生類別(Derived class)。 * 改寫(Override) 若要改寫方法的話,可以在繼承類別下改寫 ```java= class Pass2 extends Pass { // Override dispose void dispose() { result = "不及格"; if (score >= 70) { //把上個例題的標準改為70分 result = "及格"; } } } ``` ### protected 可以繼承該類別的衍生類別。 ### final 將成員設為final的話,她就變成個常數,不能被其他衍生類別改寫和繼承。 ### super 再衍生類別中,若要呼叫基礎類別的成員,中間以 ***(.)*** 連結。 ### this 用於呼叫自己類別的成員,用法跟super相同。 >例題 >運用Cycle類別來算出圓柱底面積 >Cycle2繼承Cycle算出圓柱體體積 ```java= public class Java_Cycle_Cycle2 { public static void main(String[] args) { Cycle cy1 = new Cycle(5); System.out.println("radius = "+cy1.getradius()); System.out.println("area = "+cy1.getarea()); Cycle2 cy2 = new Cycle2(cy1.radius,10); System.out.println("volume = "+cy2.getvolume()); } } class Cycle{ int radius; Cycle(){ radius = 2; } Cycle(int r){ radius = r; } int getradius(){ //回傳半徑 return radius; } double getarea(){ //回傳底面積 return radius*radius*Math.PI; } } class Cycle2 extends Cycle{ int length; Cycle2(int r,int l){ length = l; radius = r; } double getvolume(){ return super.getarea()*length; //呼叫基礎類別的方法getarea } } ``` ---- # 介面(Interface) 有天你電腦的鍵盤會掉了,要做的就是先查看鍵盤規格,並買一個符合標準的鍵盤回來,不同品牌的鍵盤都會有各自的規格,所以產品可以如此的通用。軟體設計的概念也一樣。 Java中的**interface型別**與**類別**相似,他也有資料與方法成員,但是介面的資料成員,僅可以是常數,方法則是只能定義方法的原型,不能實作。主要是靠繼承此介面的類別來實作。 簡單來說介面就是定義規格,讓其他類別繼承此規格。 ## 介面的設計 ```java= interface 介面名稱 [extends 介面名稱]{ 資料成員; 方法成員; } ``` ## 介面的實作 ```java= class 類別名稱 implements 介面名稱{ 資料成員; 方法成員(){ 方法實作; } } ``` >例題 >輸入一分數判斷其是否達標 >Collage standard = 60 >Master standard = 70 ```java= import java.util.Scanner; public class Java_9_4_interface { public static void main(String[] args) { System.out.print("input score : "); Scanner in = new Scanner(System.in); int a; a = in.nextInt(); collage pa1 = new collage(); pa1.score = a; pa1.dispose(); master pa2 = new master(); pa2.score = a; pa2.dispose(); System.out.println("College result: "+pa1.result); //standard = 60 System.out.println("Master result: "+pa2.result); // standard = 70 } } interface Pass{ //介面宣告 void dispose(); } class collage implements Pass{ protected int score; protected String result; public void dispose(){ result = "不及格"; if(score >= 60){ result = "及格"; } } } class master implements Pass{ protected int score; protected String result; public void dispose(){ result = "不及格"; if(score >= 70){ result = "及格"; } } } ``` ## 實作多介面 一個類別可以同時實作多個介面,介面間以 **(,)** 隔開。 ```java= class ABC implements A,B,C{ //ABC類別同時擁有A,B,C介面的資料成員及方法。可以同時實作A,B,C中的方法。 } ``` ## 多介面繼承 Java的介面可以實現類別的**多重繼承**。 ```java= interface ABC extends A,B,C{ //ABC介面含有A,B,C介面的資料成員及方法。 } ``` ---- # 套件(Package) 當我們的類別越來越多時,就必須分類了。Java是使用套件package來做分類,每一個套件都會是一個資料夾。 * step 1 : 先創建一個package ![](https://i.imgur.com/TIUVcZC.png) * step 2 : 命名為相關名稱 ![](https://i.imgur.com/7f2039D.png) * step 3 : 建立一個類別 ![](https://i.imgur.com/cotw2Gp.png) * step 4 : import 套件 ![](https://i.imgur.com/xcw1X5C.png) ==:warning:import中的(.)相當於檔案的路徑(/) !==