###### tags:`Java` # Java - 基礎 ## 基礎Java ### 流程控制--選擇結構 * 單向選擇 if ```java= if(判斷條件){ 執行內容; } ``` * 雙向選擇擇一 if...else ```java= if(判斷條件){ 執行內容; }else{ 執行內容; } ``` * 多向選擇擇一 if...else if...else ```java= if(判斷條件1){ 執行內容1; }else if(判斷條件2){ 執行內容2; }else{ 執行內容3; } ``` switch...case * 變數(n)只可為整數或字元(JDK 7以後可以比較字串) * 若省略break則會執行下一個case的敘述,直至遇見break跳出 * long不適用 ```java= switch (n){ case 1: 執行內容1; break; case 2: 執行內容2; break; case 3: 執行內容3; break; default: 執行內容; break; // 此處break可省略 } ``` ### 流程控制--重複結構(迴圈) * for 前測式,執行次數0~n次(通常用於次數已知) ```java= for(起始值設定; 條件判斷; 計次){ 執行內容; } ``` * while 前測式,執行次數0~n次(通常用於次數未知) ```java= while(條件判斷){ 執行內容; } ``` * do...while 後測式,執行次數1~n次(先執行1次,再判斷條件) ```java= do{ 執行內容; }while(條件判斷); ``` ### break與continue * break的作用是<font color = red>跳離迴圈</font> * continue的功能是<font color = red>跳過continue以下的敘述</font>,回到迴圈的起始點 * 通常會搭配if判斷使用 * 無窮迴圈(不知道要跑幾次),搭配break跳離迴圈 * break只能在<font color = red>迴圈</font>與<font color = red>switch case</font>使用 --- ## 方法設計與應用 ### 方法宣告 * 定義:可重複使用的程式碼片段 取名稱→存入資料→重複使用 * 範例: ```java= public static void main(String[] args){ // main方法為程式的進入點 } ``` * 宣告方式: ```java= <modifier> <return type> <methodName> (<parameter>) { 執行內容; } ``` * 目的:精簡、乾淨,也能增加程式的閱讀性 * <font color = red>注意</font>: -- 方法不可以朝狀宣告,每個方法都是各自獨立的區域 -- 方法宣告沒有順序性,看的是main方法中的執行順序 ### 方法呼叫 * 呼叫同類別的方法 -- 同一類別中,可直接使用methodName呼叫方法 -- ex. methodName(); * 呼叫<font color = red>不同類別</font>的方法 -- 不同類別,可使用該類別 . 方法名稱呼叫方法 -- ex. className.methodName(); ### 傳遞參數與回傳值 * worker method -- 可能需要有參數傳進來 -- 如果有回傳值,則不再是void,並且要加return ```java= public int sum (int x, int y){ return x + y; } ``` * calling method -- 傳遞worker method要的參數型別與值 -- 如果有回傳值,則用適當的型別去接 ```java= public static void main (String[] args){ int sum = sum (1, 2); // sum = 3 } ``` ### 方法覆載機制 * 基本觀念 -- <font color = red>Overloading</font>讓我們可以用統一的方法名稱來呼叫相同功能的方法 -- 程式設計者為<font color = red>功能類似的方法</font>提供<font color = red>相同的方法名稱</font>時,Java會自動依據<font color = red>參數的數目</font>及<font color = red>不同的資料型別</font>,自動呼叫對應的方法 * 注意事項 -- Overloading的方法無法根據回傳值型別的不同而區別 -- 以下三種方式名稱一樣、傳入參數的型別也一樣 ```java= public void method (int i){ } public int method (int i){ } public String method (int i){ } ``` <font color = red>雖然回傳型別不同,但還是會被認為是重複宣告</font> 1. 方便到的是使用這些方法的程式設計師(因為都用相同的方法名稱) 2. 方法對應上會先尋找完全符合的方法,若沒有則會尋找參數可相容的方法作執行 --- ## 認識物件 ### 類件與物件 * 五字箴言:所見及物件 * 只要是物件就醫定會有此兩項 -- 屬性(attribute)或稱為特徵(Characteristics)or表現數據 -- 行為(behavior)或稱為操作(Operation)or可以做什麼事 * Java透過類別(class)實現物件的概念,讓程式設計師能更具體化與直覺的方式進行資料處理 -- 就像是物件的"規格書"或是"設計圖" * 類別組成成員 -- 資料成員 (Data Member → 變數 Variable) -- 方法成員 (Method Member → 方法 Method) ex. class 杯子 -- 屬性:容量、材質、顏色 -- 行為:裝飲料 * <font color = red>類別產生物件</font> * 一個物件是由某類別產生的<font color = red>一個實體(instance)</font> ### 建立物件進行操作 #### 流程 * 宣告 -- <類別名稱> <變數名稱>; ```java= Pen myPen; ``` * 實體化物件 -- 欲產生該物件真正的記憶體空間,必須以new關鍵字建立 ```java= new Pen(); ``` * 初始化物件 -- 用 = (指定運算子)指派該物件至物件參考變數 ```java= myPen = new Pen(); ``` * <font color = red>注意!</font> -- 物件參考變數(Object Reference Variables)是一個儲存物件在記憶體中位址的變數 * 以上方式可縮減為一句 ```java= Pen myPen = new Pen(); ``` #### 操作物件屬性與呼叫方法 * 操作資料:最普遍的做法是利用 . 運算子來操作物件的值 ```java= myPen.brand = "SKB"; mePen.price = 10; ``` * 呼叫方法:最普遍的做法是利用 . 運算子來呼叫物件的方法 ```java= myPen.showinfo(); ``` #### 實體變數(屬性)預設初始值 * Java會再編譯時給實體變數預設初始值 | 變數型態 | 預設初始值 | | -------- | ---------- | | byte | 0 | | short | 0 | | int | 0 | | long | 0L | | float | 0.0F | | double | 0.0(D) | | boolean | false | | char | '\u0000' | | 類別型態 | null(空值) | * 變數的預設初始值比較 | 變數 | 預設初始值 | |:--------:|:----------:| | 區域變數 | × | | 實體變數 | √ | #### 類別與物件的關係 * Java程式設計師藉由類別來定義一個物件實體所該具有的屬性與行為 * 再藉由該類別來產生物件實體,進而在程式裡進行操作 * 所以對於該物件實體資料,我們就使用類別作為變數宣告 * 結論:<font color = red>類別就是物件的資料型別(type)</font> --- ## 物件導向概論 ### 物件參考(reference)變數 * 物件參考變數(Object Reference Variable)是一個儲存物件在記憶體中<font color = red>位址</font>的變數 -- 不同的記憶體空間,myPen1與myPen2不一樣 ```java= Pen myPen1 = new Pen(); Pen myPen2 = new Pen(); ``` -- 相同的記憶體空間,將myPen1的記憶體位址給myPen2 ```java= Pen myPen1 = new Pen(); Pen myPen2 = myPen1; ``` #### 變數於記憶體運作機制 圖示: ![](https://i.imgur.com/63DFvVY.png) #### 物件參考(reference)變數指定(assign)行為 圖示: ![](https://i.imgur.com/PQcNCwC.png) ### 傳值與傳址(參考) * 傳值 pass by value -- 傳給方法基本型別的資料,其實可看作<font color = red>複製一份值</font>給方法使用 * 傳址 pass by reference -- 傳給方法參考變數(reference),可看作<font color = red>複製一份門牌</font>給方法使用 -- 很有可能發生<font color = red>資料異動</font>(被修改)的結果 ### 物件導向語言(OOP) (Object-Oriented-Programing) #### 物件導向程式語言<font color = red>必定有</font>以下三種特性 * <font color = red>封裝 (Encapsulation)</font> -- 依類別成員存取權限分為private, default, protected與public 讓Java實現隱藏資料與提升資料存取安全性 * <font color = red>繼承 (Inheritance)</font> -- 子類別可繼承父類別的成員,並可以修改或是新增自有成員 讓父類別所定義的成員可以給子類別使用,子類別更能自行修改父類別定義或是進行擴充,以表現出自身的特質 * <font color = red>多形 (Polymorphism)</font> -- 父類別指向子類別物件,並對應到子類別適用的方法 讓程式設計師可用同樣方式去引用不同類別的物件,這對我們來說可以易於使用,並進一步到專案維護與功能擴充 #### OOP使用<font color = red>訊息傳遞(Message Passing)機制</font>,透過物件接受訊息、處理訊息、傳送/回傳訊息來實現功能 ### 整理與探討 * Java透過類別(class)來定義屬性與方法,並用該類別產生的物件實體(object instance)進行功能上的實現與架構設計 * 封裝、繼承與多型為物件導向程式語言的三大特性,我們必須藉由類別來達成以上三種概念,由此可知類別對Java的重要性有多大,我們可以說這是以類別為基礎(class based)的程式設計 --- ## 陣列 (Array) ### 定義 * 陣列是由<font color = red>一群相同資料型態的變數所組成</font>的一種資料結構 * 陣列裡的資料稱為元素(element),元素為有序性,<font color = red>從0開始</font>,就像是陣列裡各個元素的位置表示 ### 陣列(Array)宣告 * 必須用new關鍵字來分配陣列的儲存空間 -- 陣列也是一種<font color = red>reference資料型態</font> -- 陣列指定運算,也就是傳遞陣列的<font color = red>記憶體位址</font>(memory address) -- 注意:在new宣告的同時<font color = red>必須指定長度且不可再更改</font> * 陣列宣告時,中括號可置於陣列參考的前面或後面 ```java= int x[]; 或 int[] x; int x[], y[]; 或 int[] x, y; ``` * 一維陣列宣告方式 ```java= int[] x = new int[3]; x[0] = 10; x[1] = 20; x[2] = 30; 或是表示為 int[] x = {12, 20, 30}; ``` * 多維陣列 -- Java的多維陣列就是陣列的陣列 -- 矩形陣列與非矩形陣列 -- 巢狀結構:大陣列裡包著許多小陣列,不建議太多維,造成閱讀困難 -- 宣告方式 ```java= int[][] x = new int[2][3]; x[0] = {1, 2, 3}; x[1] = {4, 5, 6}; 或是表示為 int[][] x = { {1, 2, 3}, {4, 5, 6} } ``` ### 陣列(Array)元素的存取 * 取得陣列的長度 -- 語法:陣列名稱 . length -- 注意1:一維陣列為取得<font color = red>元素個數</font> -- 注意2:二維陣列為取得<font color = red>列數</font> -- 注意3:length後面不可以加上小括弧,因為此處的<font color = red>length並不是方法</font>,而是陣列的一種<font color = red>屬性</font> (跟String類別的length()<font color = red>不同</font>) * 取得陣列的元素 -- 可藉由索引值(index)存取陣列中儲存的資料值 -- 注意:<font color = red>索引值從0開始</font> ### 陣列元素的排序、搜尋與複製(Array類別) * 陣列的排序 -- static void Arrays.sort(欲排序的陣列名稱),可讓該陣列元素從小到大排序 ```java= Arrays.sort(欲排序的陣列名稱); ``` * 陣列的搜尋 -- static int Arrays.binarySearch(陣列名稱, 欲搜尋的值) -- 使用二分搜索法,搜索陣列內某個值的位置(<font color = red>回傳int</font>) -- 執行搜索前,必須<font color = red>先將該陣列排序</font>,如果欲搜索的值不在該陣列裡,則回傳負值 ```java= Arrays.sort(arrayName); int num = Arrays.binarySearch(arrayName, 欲搜尋的值); // 回傳index ``` * 陣列的複製(JDK 6新增方法:cppyOf()) -- ststic 複製的陣列型別Arrays.copyOf(欲複製的陣列名稱, 複製的長度) -- 複製出的新陣列可以不用預先初始化(不用new),直接回傳(複製出)一個新的陣列 ```java= int[] newArray = Arrays.copyOf(oldArray, newArrayLength); ``` ### 陣列宣告的預設初始值 | 變數型態 | 預設初始值 | | -------- | ---------- | | byte | 0 | | short | 0 | | int | 0 | | long | 0L | | float | 0.0F | | double | 0.0(D) | | boolean | false | | char | '\u0000' | | 類別型態 | null(空值) | --- ## 字串 (String) ### 不可變特性與字串池 #### String物件的特性: * 不可變的(immutable)字串 -- 使用String類別任何方法時,傳回的字串都會放在新的記憶體位置 -- String一旦<font color = red>宣告後</font>,即<font color = red>不能</font>在原所在記憶體位置改變字串內容 * String s1 = "Hello"; -- 為加快程式執行,Java會把此類字串放在字串池(String Pool)裡 -- 程式裡若有多個變數,都使用相同的字串常數(ex. "Hello"),則均會使用相同的記憶體空間(String Pool) * String s1 = new String("Hello"); -- 使用new的方式產生的字串會存放在Heap memory,有自己的獨立記憶空間 * 圖解 ![](https://i.imgur.com/bAqTNFs.png) ### 字串比較方式 #### 比較方式 ( == 與 equals ) * 比較字串若使用==,並<font color = red>不是</font>在比較字串內容,而是在比較其<font color = red>記憶體位址</font> * 比較字串內容時,應該使用String物件本身提供的方法<font color = red>equals</font> -- public boolean equals(Object anObject) ```java= stringa.equals(stringb); // 回傳boolean ``` * 範例1: ```java= String s1 = "Hello"; String s2 = "Hello"; // String Pool位址一樣 System.out.println(s1 == s2); // true 比較String Pool位址 System.out.println(s1.equals(s2)); // true 比較字串內容 ``` * 範例2: ```java= String s1 = "Hello"; // String Pool String s2 = new String("Hello"); // Heap memory System.out.println(s1 == s2); // false 記憶體位址不同 System.out.println(s1.equals(s2)); // true 比較字串內容 ``` #### 常用方法 ```java= String s1 = "Hello"; s1.charAt(1); // e 索引值從0開始 s1.length(); // 5 字串長度 s1.isEmpty(); // 回傳boolean 是否為空字串 // JDK 6以前需使用if(s1.length() == 0);判斷 s1.trim(); // 去除頭尾的空白鍵<space> s1.substring(1); // ello 從索引值1開始取值 s1.substring(1, 4); // ell 從索引值1~4取值,不含4 String s2 = "hello" s1.compareTo(s2); // 回傳int // 由左至右比較ASCII值的大小直至不同或比較結束 // 若回傳值 = 0,表示兩個字串相等 // 若回傳值 > 0,表示左邊字串大於右邊字串 // 若回傳值 < 0,表示左邊字串大於右邊字串 ``` --- ## 封裝(Encapsulation) ### 目的 * 程式設計師設計類別時,即可指定<font color = red>是否讓其他類別可以存取此類別的資料成員或是方法成員</font> * Java資料封裝的基本就是<font color = red>類別</font> * 封裝做到<font color = red>資料隱藏與存取限制</font>,當我們將設計好的資料與方法包裝在物件裡,都必須透過該方法的成員方法來對資料進行存取動作,其他程式無法直接對此物件的資料存取 * Java使用了private, default, protected與publib四種存取修飾子作為封裝權限的等級 ### 存取修飾關鍵字 #### public (公開) * Visible to the world * 所有類別皆能存取 #### protected (保護) * Visible to the package and all subclasses * 同套件底下類別或所有其子類別都可以存取 #### default (預設) * Visible to the package * 同套件底下的類別皆可存取 * 預設不是default關鍵字,而是<font color = red>沒有其他修飾字就是預設</font>(直接寫class名稱) #### private (私人) * Visible to the class only * 只有該類別內部才可存取 ### 封裝範例 #### private → public getter → public setter * 設定private實體變數 ↓ * 設定public存入資料及條件判斷邏輯,並將值給至private實體變數中 ↓ * 設置public讀取資料,從private實體變數中取值 ### 封裝設計總結 * 對於理想的程式碼來說,類別中絕大部分甚至是全部的變數(Instance Variable)都會是用private修飾子 * 表示他們無法直接被自己所屬以外的類別修改或查詢 * 這些方法應該包含程式碼和運作邏輯以確保變數不會被設定成不適當的值 --- ## 建構子 (Constructor) ### 建構子 * 名稱需<font color = red>與類別名稱相同</font> * 建構子宣告 -- 一個類別可以有多個建構子 → Overloading -- 一個建構子可以傳入0~n個參數 -- 建構子類似方法,可以有存取修飾子 -- 建構子<font color = red>沒有宣告回傳型別</font>,加了回傳型別會變成一般方法(<font color = red>連void都不能寫出來</font>) ```java= <modifier> constructorName ([arguments]){ } ``` * 必須使用new關鍵字呼叫建構子產生物件,並同時初始化該物件的實體變數 無法改值,故只會執行一次 * <font color = red>注意:Java會自動給一個不帶參數的建構子,一旦宣告其他建構子,Java會自動將此建構子移除</font> -- 若需要不帶參數的建構子則需再手動建立 -- new = Java 預設建構子,不帶參數 * 建構子參數通常配合該類別的實體變數做對應的宣告 ### this 關鍵字 * 用來代表執行時的當前物件 * 易混淆的程式碼範圍加以區分 * 在建構子覆載設計使用 ```java= public class Pen { public String brand; public double price; public Pen (String brand, double price){ this.brand = brand; this.price = price; } public static void main (String[] args){ Pen p = new Pen("SKB", 10); // 呼叫建構子建立物件p System.out.println(p.brand); // 物件p的品牌 → SKB System.out.println(p.price); // 物件p的價格 → 10 } } ``` ### 建構子覆載 (Overloading) #### 可以藉由this關鍵字呼叫同類別底下的另一個建構子 #### 建構子第一行(第一個敘述位置)只要有this則進行呼叫其他建構子 * Overloading設計是方便給使用者在呼叫時的彈性 * 搭配this(...)讓物件屬性的初始值設計也有好的彈性做法 ```java= public class Pen { public String brand; public double price; public Pen (String brand, double price){ this.brand = brand; this.price = price; } public Pen (double price){ this("SKB", price); } public Pen (String brand){ this(brand, 10); } public Pen (){ this("SKB", 10); } } ``` --- ## static 關鍵字 ### static 關鍵字 #### 實體變數和方法若是宣告為static,則此變數和方法即成為類別變數(或稱靜態變數)和類別方法(或稱靜態方法) #### 宣告為static的變數和方法,不是由任何此類別的物件單獨擁有,而是屬於<font color = red>此類別的所有物件共同擁有</font> * 補充1:實體變數由物件各自獨立維護,彼此不受干擾 * 補充2:static類別變數是屬於類別的變數,但卻可以由該類別所創造(new)出來的物件共享共用 * 補充3:儲存類別變數和方法的<font color = red>記憶體空間為global</font>,與儲存物件的記憶體空間是分開的 * 補充4:使用static變數和static方法的方式有兩種 -- 經由類別的任何實體來呼叫 (不好也不鼓勵使用) -- <font color = red>經由類別的名稱來呼叫</font> (較好的方式) 1. 常見於常數的宣告使用 (因為固定不變的資料只要一份讓大家共享共用就夠了) 2. 若是方法內容沒有直接存取實體變數或呼叫一般方法的話就可以宣告為static #### 變數比較 | 實體變數 | 類別變數 | |:--------------------------:|:------------------------------------:| | 產生幾個實體,就有幾個變數 | 不論多少個實體,只有一個變數(可更改) | ### static 機制 #### 當類別第一次被載入到JVM時,在任何實體被建構之前,靜態的變數與方法就會<font color = red>先被載入</font>,所以: * static方法不可使用this * static方法不可被複寫(Override)為非static方法 (不常見) * 宣告為靜態static方法,不可以存取該類別中non-static的變數和方法,只可以存取該類別中的static變數和方法 -- 靜態不能取用動態,除非先new一個實體變數 * 宣告為non-static的方法,可以存取該類別中non-static的變數和方法,也可以存取該類別中的static變數和方法 #### 第三、第四點流程分析 * 編譯: -- 分配變動的stack空間 ↓ * 載入: -- static項目就會存在global空間 ↓ * 執行: -- 執行時期產生的資料 (ex. 物件實體存在heap空間) --- ## 繼承 (Inheritance) ### 概念與目的 #### 概念 * 一個類別可以繼承它的父類別所有狀態及行為,我們即把此稱之為子類別(subclass)延伸自(extends)父類別(superclass) ![](https://i.imgur.com/QrT77VR.png) * is a與has a -- 當 subclass extends superclass,以 subclass is a superclass 表示 subclass 是一種 superclass subclass has a method ```java= class FullTimeEmployee extends Employee{ private double monthlySalary; } ``` FullTimeEmployee <font color = red>is a</font> Employee FullTimeEmployee <font color = red>has a</font> monthlySalary #### 目的 (優點) * 提高程式的重複使用性 * 子類別將會繼承到父類別中所有可以存取的成員(依 modifier 決定),包括變數與方法 -- 共同資料只要描述一次 -- 處理共同資料的成員也只要描述一次 * 子類別繼承父類別功能後: -- 還可以加入新方法 -- 也可以覆寫(Override)從父類別而來的方法,建立一個專屬自己類別的運作邏輯 ### 注意事項 * Java<font color = red>不支援多重繼承</font>,一個子類別只能extends一個父類別 * <font color = red>建構子(Constructor)無法被繼承</font> * java.lang.<font color = red>Object類別</font>為所有類別的<font color = red>共同父類別</font> -- (extends Object)<font color = red>自動隱藏</font> ### 方法覆寫機制 (Override) #### 目的: * 子類別繼承父類別後,不滿意父類別定義的方法,子類別可以在繼承後<font color = red>重新改寫</font>,即為Override #### 規定:當子類別宣告覆寫方法時 (Overriding) * 方法名稱 * 參數個數 * 參數類型 * 回傳值 (以上四項皆須與父類別裡被覆寫的方法<font color = red>完全相同</font>) * 存取修飾子的等級<font color = red>不可以小於原方法</font> (子類別要比父類別更開放) * 建議在Override程式碼前<font color = red>加上@Override</font>,可協助偵錯Override是否正確 ```java= @Override public void play(){ // Override play() } ``` * 子類別覆寫父類別定義有throws的方法時,<font color = red>不得</font>比父類別被覆寫的Exception還要高階 (參考例外處理(Exception)) * 定義為 final 則<font color = red>無法</font>被改變/覆寫 * final class 不可被繼承 * final method 不可被覆寫 * final variable 不可被改變 (常數(Constant)) * final reference 不可在指向另一個物件 (位址固定) * static <font color = red>不可</font>被覆寫為 non-static方法 ### 呼叫父類別 (super) #### 呼叫方法 * 子類別透過super. 可以呼叫上一層類別的方法 ```java= super.methodName(); ``` #### 呼叫建構子 * 子類別透過建構子,用super(...)將<font color = red>共同的建構子參數</font>傳給父類別 -- 目的:確保子類別物件裡的<font color = red>共同資源</font>都有完成<font color = red>初始化的動作</font> * 建構子呼叫的順序為<font color = red>先父類別在子類別</font> * 建構子中若有出現super(...),一定要放在<font color = red>第一個敘述位置</font> * 建構子中若未出現super(...),Java<font color = red>預設</font>會有一個<font color = red>隱形的super()</font> 預設自動放在第一個敘述位置 * <font color = red>super()</font>跟<font color = red>this()</font>在建構子的設計上<font color = red>只能擇一</font>使用,因為都得放在第一個敘述位置 --- ## 多形 (Polymorphism) ### is a與多型(Polomorphism) * 運用類別間繼承關係,使父類別(superclass)可以當成子類別(subclass)的<font color = red>通用型別</font> ```java= class FullTimeEmployee extends Employee{ // 正職員工是一種員工 } class Manager extends FullTimeEmployee{ // 經理是一種正職員工 // 經理也是一種員工 } ``` ### 型別多樣化 * 只要符合類別間的繼承關係,在宣告參考變數時,子類別(位階低)物件實體可以升級成父類別(位階高)型別表示 -- new後面呼叫的建構子就是產生的物件實體 ```java= Employee e1 = new FullTimeEmployee(); Employee e2 = new Manager(); ``` ### 型別轉換 (Cast) * 父類別參考變數若是要轉型回子類別,則需要靠強迫轉型(Cast),但是會在執行時期才檢查是否能夠轉回適當的子類別 ```java= Employee e1 = new FullTimeEmployee(); FullTimeEmployee f = (FullTimeEmployee)e1; // 轉型成功 Manager m = (Manager)e1; // 執行時發生 java.lang.ClassCastException // 轉型不符合類別繼承關係 // 編譯時不會有錯,執行時才會有錯誤發生 ``` * 物件為動態產生,所謂動態指的就是執行,所以在編譯階段,編譯器還無法得知<font color = red>參考變數指向哪一個物件</font>,只能從語法先檢查是否符合(=)規則 ### 有關 (instanceof) #### instanceof運算子常被用來判斷父類別參考變數真正指向何種子類別的物件實體 * 語法:物件參考變數instanceof類別名稱 * 說明:檢查左邊參考的物件是否可以轉型為右邊的類別型別,如果可以回傳true,否則為false ```java= Employee e1 = FullTimeEmployee(); e1 instanceof FullTimeEmployee; // true e1 instanceof Manager; // false ``` ### 一致性操作 * 對於不同的物件實體,我們找出這些物件實體可通用的型別作為宣告,實現資料操作上的一致性,可以讓程式碼變得更加簡潔,也易於日後資料的擴充設計與維護 ```java= public static void main (String[] args){ Employee[] e = new Employee [3]; // 使用父類別宣告 e[0] = new FullTimeEmployee(7002, "Peter", 40000.0); e[1] = new Manager(7003, "Mery", 50000.0, 10000.0); for(int i = 0; i < e.length; i++){ System.out.println(e[i].getSalary()); // getSalary(); } } ``` * 用父類別宣告,讓子類別物件都可以放進這個陣列中 * 雖然是不同種類的資料,但取出來都可以一起呼叫getSalary()方法 * 每個子類別中都有繼承父類別的getSalary()或各自有自己的覆寫getSalary() ### 動態繫節 (dynamic binding)/延遲繫節 (late binding) * 用父類別的型別(參考),指向子類別的物件,並對應到子類別overriding的方法 -- 父類別會先判斷實際的子類別物件是哪一個,再呼叫此子類別裡對應的overriding方法 ```java= Manager m = new Manager(7003, "Mery", 50000.0, 10000.0); double msalary = m.getSalary(); Employee e = new Manager(7003, "Mery", 50000.0, 10000.0); double esalary = e.getSalary(); ``` * 以上getSalary()在最後執行時,都是子類別Manager的方法 -- 但<font color = red>父類別的getSalary()還是不可以省去</font>,否則無法進行對應造成錯誤 #### 補 * 編譯時期,編譯器也不知道呼叫的方法是哪一個,而是得到執行時期,有產生物件實體再根據是否有Override才知道呼叫的方法是哪一個 * 編譯器利用我們宣告的型別來檢查存取的資料或呼叫的方法是否存在,因為身分(資料型態)會決定這個物件的特徵與行為 --- ## 抽象機制 (abstract) ### 抽象類別 (abstract class) #### 使用時機 * 在建立類別時,若有方法尚未決定如何設計內容主體時,就可將此方法加上abstract修飾子成為抽象方法,之後在由繼承的子類別來實作 #### 使用方法注意事項 * 抽象方法沒有方法主體,且必須加上abstract修飾子 ```java= public abstract void method(); ``` * 抽象類別不一定要有抽象方法,但有抽象方法的類別一定要宣告為抽象類別 ```java= public abstract class Myclass{ public void method(){ } } // OK public class Myclass{ public abstract void method(); } // not OK! 有抽象方法一定要宣告為抽象類別 ``` * 一個類別只要加上abstract修飾子(即使它裡面不包含任何abstract方法),它就無法產生實體,只能透過繼承來建立延伸子類別,而該類別若繼承了抽象父類別,除非它實作了抽象父類別的所有抽象方法,否則它仍然只是個抽象類別 --- ## 介面 (Interface) <font color = red>Java因介面而偉大</font> ### 介面的五大功能 * <font color = red>多重繼承</font> * Java只能單一繼承,而介面可以實現物件導向中的多重繼承 * class 子類別 extends 父類別 implements 介面1,...,介面n{...} * class 子類別 implements 介面1,...,介面n{...} * <font color = red>定義規格</font> * 介面可以說是一種<font color = red>所有方法皆為抽象方法的抽象類別</font>,所以子介面必須實作介面的所有抽象方法 * 介面跟介面之間是<font color = red>可以再繼承(extends)</font>的 * <font color = red>貼標籤</font> * <font color = red>型別轉換</font> * <font color = red>降低相依性</font> ### 介面方法與資料特性 * 介面裡面宣告的資料 * 預設由編譯器自動加入 public static final * 代表在介面裡宣告的資料實為常數 * 介面裡面宣告的方法 * 預設由邊義器自動加入 public abstract * 代表在介面裡宣告的方法強制為抽象方法 * 因為介面做為定義規格的用途,所以對任何實作介面的類別來說,資料須為一致,也就是所謂的"標準"(既然是實作同一個介面,理所當然地從介面得到的資料都是相同的)。但方法是可以在各個實作類別裡面自行完成 ### 主要目的 #### 因為介面對Java來說是個規格較特殊的類別,所以: * 介面也是一種參考型別,也就是說介面提供了另一種彈性,使子類別在繼承原父類別的特性之外,也能具有其他型別的特性 * 因為一個物件可以實作多個介面,所以每一個父界面都可以當作此物件的(父)多型之一,也因此用介面來幫物件做型態轉換將是一件容易的事情 ### 介面與多型、相依性關係 * 介面與多型比起繼承又更有彈性,我們將類別之間從<font color = red>is-a</font>的關係,<font color = red>轉化成有共同行為</font>的關係 ex.鳥類、飛機與超人雖然沒有現實生活上is-a的關係,但他們都具備了"飛"的行為,所以也能達到資料一致性的操作 #### 依賴反轉 * 低凝聚性,高相依性 * 一盤散沙,牽一髮動全身 ↓ * 高凝聚性,低相依性 * 尋找相關的點,宣告一個interface * 在其他子類別implements interface ### 空介面 (Tag interface) 待補充 * 沒有定義任何方法的介面叫做空介面 * 一個類別可以implements某個空介面,好消息是不需要實作任何方法,但該類別的任何實體即已經成為該介面的一個合法實體 --- ## 套件介紹 (package) ### 套件管理機制 #### Java 原始檔格式 * 在Java檔案中可能會出現三個稱為編譯單元(compilation units)的元素,這些元素皆非必要 <font color = red>但</font>如果有這些元素,則<font color = red>一定要依以下順序出現</font> 1. package 宣告 2. import 引用敘述 3. class 類別 #### 套件 package * 套件命名建議,<font color = red>全英文小寫</font> (資料夾命名) * Java提供套件(package)的機制,它就像一個管理容器,可以將所定義的名稱區隔管理在package底下,而不會有類別名稱相互衝突的情況發生 * Java的package被設計為與檔案系統結構相應,而以檔案管理的觀點著手,將性質相似之類別集合在一起 * ex. java.sql表示名稱java的目錄底下有一個子目錄名叫sql,其內存放的都是java資料庫連線相關的類別檔 * 套件宣告 * 宣告於原始檔案的第一行 * package packageName; * package com.ibm; . 代表下一層(子資料夾) ### Java API 與常用套件 * Java標準API裡有許多已經設計好的類別與其相關的內容方便我們程式設計師可以更輕鬆快速實現所需要的功能和應用,所以對於常用的類別應該熟記相關套件 * java.lang (laguage (String, Object, Math,...)) * java.io (Input/Output) * java.net (Internet 網路程式設計相關工具) * java.sql (SQL (JDBC課程) 資料庫) * java.util (Utility (工具) 集合API) * java function (Java 8新增) ### 套件編譯與執行 * 所有屬於myPackageName類別庫的.class檔案都必須儲存在myPackageName資料夾下,若不使用package宣告,Java預設會將類別檔置於目前工作環境所在的目錄中 * 因為source檔(.java檔)與.class檔<font color = red>不一定要放在同一個目錄下</font>,所以使用package宣告時,必須在編譯時透過-d option,指定類別檔要置於哪個目錄之中 * 編譯:javac -d . HelloWorld.java ( . 指編譯後的class檔置於目前的目錄位置) * 執行:java packageName.HelloWorld (指要在原來的目錄下執行) --- ## import & classpath ### import 使用目的 #### import (引用) 套件 * 給Java編譯器看的 (程式碼裡的類別們在哪些套件下) * 關鍵字中(keyword)的import可用來引入API中的功能,或是自行定義的套件(package) * Java會自動引用的兩個套件: * java.lang.* :常用的類別已置於此套件中,ex. String * 目前工作環境所在的package (<font color = red>同套件皆可</font>) * 表示方式: * import java.lang.* ; * import packageName.* ; * 若使用上述之外的其他套件,則必須用import引用敘述 * ex. import java.xxx.* ; * p.s. <font color = red>不包含其子目錄的類別</font> ex. import java.xxx.yyy.* ; * 若方法為static method,則不用import,可直接使用method #### import (引用) 套件或特定類別 * 引用套件中<font color = red>所有類別</font>: * import java.sql.<font color = red>*</font> ; ```java= Date date = new Date(); ``` * 引用套件中的<font color = red>特定類別</font>: * import java.sql.<font color = red>Date</font>; ```java= Date.date = new Date(); ``` * <font color = red>如果不使用import敘述,則必須使用類別長名稱</font> ```java= java.sql.Date date = new java.sql.Date(); java.util.Date date = new java.util.Date(); // 注意!! Date一定要使用"長名稱" ``` #### static import * 靜態引用套件 (JDK 5新增) * 靜態引用可導入類別內的所有static fields與static methods,亦即使用這些static members無需再指定其類別名稱 * 使用 * 星號字元可導入類別內所有靜態成員 ```java= import static java.lang.Math.* ; r = sin(PI * 2); // 等於 r = Math.sin(PI * 2); ``` * 屬於<font color = red>Sugar功能</font> (編譯器蜜糖) * 避免過度使用static import功能,否則<font color = red>容易造成混淆而不利於維護</font> ### 類別路徑 (classpath) #### 設定 * classpath可以讓Java應用程式在編譯和執行時,可以找到要用的相關類別 * 根據JDK文件說明,Java以下面三類classpath順序,依序尋找所需的class 1. Bootstrap classes(Core classes): * Java2 Platform核心類別函式庫 * 現有,已置於%JAVA_HOME%\jre\lib\rt.jar檔案 * JDK預設會自動載入,不必額外再做設定 2. Extension classes * Java2 Platform擴充的類別函式庫 * 指的是%JAVA_HOME%\jre\lib\ext目錄下的jar檔或是zip檔 * JDK預設會自動載入此目錄內所有的jar檔或是zip檔,不必額外再做設定 3. Users classes * 是使用者自己寫的類別函式庫(third-party的類別函式庫也可),<font color = red>必須額外作設定</font>,JDK才會載入類別 * 是指我們在環境變數classpath設定路徑下的classes檔或是jar檔 * 在作業系統的環境變數,預先新增classpath變數: C:\myLib\xxx.jar;C:\myLib\yyy.jar;C:\myClass; * 在命令列中: set classpath = %classpath%;C:\myLib2\zzz.jar;C:\myClass2; * 在編譯及執行時 編譯:javac -classpath "%classpath%;C:\myLib2\zzz.jar;C:\myClass2;" HelloWorld.java 執行:java -classpath "%classpath%;C:\myLib2\zzz.jar;C:\myClass2;" HelloWorld --- ## 類別 (Object) ### 所有Java類別的共同父類別-Object * Java的所有類別,<font color = red>全部繼承自java.lang.Object類別</font> * 若一類別無繼承關係,則<font color = red>Java會自動繼承Object類別</font>作為此類別的父類別 * Object類別常用方法 * boolean equals(Object obj): 自定類別產生的物件要進行內容比較,可模仿字串類別,對equals方法做改寫動作,改寫其實就是在定義比較的原則 * String toString(): 通常搭配String方法:通常搭配String方法對物件做列印的動作,因為print方法會自動呼叫toString方法,主要是方便於觀察物件內容或除錯時使用 * protected void finalize(): 由資源回收呼叫,當一個物件不再被參考到時,資源回收機制會呼叫該故見的finalize方法,並從記憶體做釋放動作 * final void wait() notify() notufyAll() --- ## 包裝類別 (Wrapper Class) ### 包裝類別介紹 * Java每一個基本資料型態,都有一個相對應的Wrapper類別(包裝類別) | 基本資料型態(Primitive Type) | Wrapper Class(包裝類別) | |:----------------------------:|:-----------------------| | 整數型態 | | | byte | java.lang.Byte | | short | java.lang.Shoet | | int | java.lang.Integer | | ling | java.lang.Long | | 浮點數型態 | | | float | java.lang.Float | | double | java.lang.Double | | 其他型態 | | | boolean | java.lang.Boolean | | char | java.lang.Character | ### 使用包裝類別 * boxing:將基本型別,置入對應的包裝類別中 ```java= Integer i = new Integer(1); ``` * unboxing:從相對應的包裝類別取其值 ```java= Integer i = new Integer(1); int x = i.intValue(); ``` * 字串轉成數字 ```java= int i = Integer.parseInt("1"); ``` * 字串轉成包裝類別 ```java= Integer i = Integer.valueOf("1"); ``` * 比較兩個物件是否相等 * 使用boolean equals(Object obj) * 包裝類別的equals方法都<font color = red>已經Override</font> ### 自動裝箱/拆箱機制 (Auto-boxing/Unboxing) * 於Java 5加入 * Java編譯器可根據程式碼內容自動推斷為裝箱或是拆箱動作,減少以往在轉換上造成程式碼冗長 如果我們想將像是int的基本資料型別放到Collection中的話 * Auto-boxing/Unboxing * Auto-boxing(自動裝箱): 基本資料型別自動轉換為包裝型態(Wrapper Type) ex.int轉Integer * Unboxing 包裝型態自動轉換為基本資料型別 ex.Integer轉int ```java= Integer i1 = 1; // boxing int i2 = i1; // Unboxing int sum1 = i1 + i2; // Unboxing Integer sum2 = i1 + i2; // Auto-boxing ``` --- ## 整理資料 ### 修飾子適用的場合 | 修飾子 | 類別 | 實體變數 | 區域變數 | 方法 | 建構子 | 程式區塊 | |:---------:|:----:|:--------:|:--------:|:----:|:------:|:--------:| | public | √ | √ | | √ | √ | | | protected | | √ | | √ | √ | | | default | √ | √ | | √ | √ | | | private | | √ | | √ | √ | | | final | √ | √ | √ | √ | | | | static | | √ | | √ | | √ | | abstract | √ | | | √ | | |