Ch08 巢狀類別 物件導向程式設計(II)
巢狀類別-說明 在Java類別之中可以擁有其它類別的成員,這種類別稱為「巢狀類別」(Nested Classes)。 巢狀類別強調類別之間的關係,成員類別稱為「內層類別」(Inner Classes)。 例如:Employee巢狀類別擁有成員類別Phone的內層類別,Employee是巢狀類別的「外層類別」(Enclosing Class)。
巢狀類別-圖例 Employee物件的圖例,如下圖所示:
巢狀類別-範例 class Employee // Employee外層類別 { ………… class Phone // Phone內層類別 …………… }
巢狀類別-特點 巢狀類別強調類別間的關係,例如:員工擁有連絡電話的資料,所以Phone類別屬於Employee類別的一部分。 巢狀類別的內層類別也屬於外層類別的成員,所以其它成員可以存取或呼叫內層類別的成員變數和方法,就算是宣告成private的都一樣可以存取,反之內層類別的方法也可以直接存取其它成員變數和呼叫成員方法。 在Java程式檔案只允許一個宣告成public的類別,如果巢狀類別宣告成public,內層類別也能夠擁有public的存取權限。
範例1 class Employee0603 { String name; Phone0603 list; Employee0603(String name, String home, String cell) { this.name = name; list = new Phone0603(home, cell); } void printEmployee() { System.out.println("在Employee類別內"); System.out.println("===== 員工 ====="); System.out.println("姓名: " + name); System.out.println("利用呼叫內部物件的屬性來取得電話 ==>"); System.out.println("電話(住家): " + list.home); System.out.println("行動電話: " + list.cell); System.out.println("利用呼叫內部物件的方法printPhone來取得電話 ==>"); list.printPhone(); String getName() { return name; } class Phone0603 { String home; String cell; public Phone0603(String phone1, String phone2) { home = phone1; cell = phone2; void printPhone() { System.out.println("利用呼叫外部的物件方法==>"); System.out.println("姓名: " + getName()); System.out.println("電話(住家): " + home); System.out.println("行動電話: " + cell); class Ch06_03 { public static void main(String [] args) Employee0603 e1 = new Employee0603("ToTo", "07-111-1111", "0911-111-111"); System.out.println("呼叫物件方法printEmployee"); e1.printEmployee(); System.out.println("呼叫e1物件中的list物件的方法printPhone"); e1.list.printPhone(); System.out.println("呼叫e1物件中的list物件的屬性home和cell"); System.out.println("電話(住家): " + e1.list.home); System.out.println("行動電話: " + e1.list.cell); }
內層類別的使用-範例(1/2) 例如:Customer巢狀類別,如下所示: class Customer // Customer外層類別 { ……… class Address // Address內層類別 }
內層類別的使用-範例(2/2) 在Java程式需要先建立Customer物件,才能建立Address物件,如下所示: Customer c1 = new Customer(“ToTo", 29001234); Customer.Address home = joe.new Address(); 上述程式碼先使用new運算子建立joe參考的Customer物件,接著使用Customer.Address宣告物件變數home,然後使用joe.new建立Address物件,最後指定成員變數值,如下所示: home.address = "台北縣五股鄉成泰路一段"; home.zip = 248;
範例2 class Employee0604 { String name; Employee0604(String name) class Ch06_04 { public static void main(String [] args) Employee0604 e1 = new Employee0604("ToTo"); Employee0604.Phone0604 p1 = e1.new Phone0604("07-111-1111", "0911-111-111"); System.out.println("呼叫物件方法printEmployee"); e1.printEmployee(); System.out.println("呼叫p1物件中的方法printPhone"); p1.printPhone(); System.out.println("呼叫p1物件中的屬性home和cell"); System.out.println("電話(住家): " + p1.home); System.out.println("行動電話: " + p1.cell); } class Employee0604 { String name; Employee0604(String name) { this.name = name; } void printEmployee() { System.out.println("在Employee類別內"); System.out.println("===== 員工 ====="); System.out.println("姓名: " + name); } String getName() { return name; } class Phone0604 { String home; String cell; Phone0604(String phone1, String phone2) { home = phone1; cell = phone2; void printPhone() { System.out.println("利用呼叫外部的物件方法==>"); //System.out.println("姓名: " + getName()); System.out.println("電話(住家): " + home); System.out.println("行動電話: " + cell);
巢狀類別(內部類別) 位於另一個類別之內的類別稱之為內部類別(inner classes),或稱為巢狀類別(nested classes) 靜態內部類別 非靜態內部類別 區域內部類別
靜態內部類別 使用static修飾字宣告的內部類別,稱為靜態內部類別(static inner classes)。 靜態內部類別和類別內其它靜態成員一樣,都屬類別所有,所以類別方法可以直接使用靜態內部類別建立物件。 若某個類別欲使用另一個類別的靜態內部類別時,必須以點運算子(dot operator)指明靜態內部類別所屬的類別,才可使用
內層類別的使用-說明 巢狀類別的內層類別屬於外層類別的成員物件,所以不能宣告成static(???)的類別變數和方法,而且只有外層類別的物件存在時,內層類別才會存在。
範例3 class Employee0605 { String name; Phone0605 list; Employee0605(String name, String home, String cell) { this.name = name; list = new Phone0605(home, cell); } void printEmployee() { System.out.println("在Employee類別內"); System.out.println("===== 員工 ====="); System.out.println("姓名: " + name); System.out.println("利用呼叫內部物件的屬性來取得電話 ==>"); System.out.println("電話(住家): " + list.home); System.out.println("行動電話: " + list.cell); System.out.println("利用呼叫內部物件的方法printPhone來取得電話 ==>"); list.printPhone(); String getName() { return name; } static class Phone0605 { String home; String cell; public Phone0605(String phone1, String phone2) { home = phone1; cell = phone2; void printPhone() { System.out.println("利用呼叫外部的物件方法==>"); //System.out.println("姓名: " + getName()); System.out.println("電話(住家): " + home); System.out.println("行動電話: " + cell); class Ch06_05 { public static void main(String [] args) Employee0605 e1 = new Employee0605("ToTo", "07-111-1111", "0911-111-111"); System.out.println("呼叫物件方法printEmployee"); e1.printEmployee(); System.out.println("呼叫e1物件中的list物件的方法printPhone"); e1.list.printPhone(); System.out.println("呼叫e1物件中的list物件的屬性home和cell"); System.out.println("電話(住家): " + e1.list.home); System.out.println("行動電話: " + e1.list.cell); }
範例4 class Employee0606 { String name; Employee0606(String name) { this.name = name; } void printEmployee() { System.out.println("在Employee類別內"); System.out.println("===== 員工 ====="); System.out.println("姓名: " + name); } String getName() { return name; } static class Phone0606 { String home; String cell; Phone0606(String phone1, String phone2) { home = phone1; cell = phone2; void printPhone() { System.out.println("利用呼叫外部的物件方法==>"); //System.out.println("姓名: " + getName()); System.out.println("電話(住家): " + home); System.out.println("行動電話: " + cell); class Ch06_06 { public static void main(String [] args) Employee0606 e1 = new Employee0606("ToTo"); Employee0606.Phone0606 p1 = new Employee0606.Phone0606("07-111-1111", "0911-111-111"); System.out.println("呼叫物件方法printEmployee"); e1.printEmployee(); System.out.println("呼叫p1物件中的方法printPhone"); p1.printPhone(); System.out.println("呼叫p1物件中的屬性home和cell"); System.out.println("電話(住家): " + p1.home); System.out.println("行動電話: " + p1.cell); }
非靜態內部類別 沒有static修飾的內部類別稱為非靜態內部類別(non-static inner classes)。 非靜態內部類別和其它物件成員一樣,都必須建立物件才能使用。 以非靜態內部類別所建立的實體,實際上都必須與外部類別的實體結合在一起。因為沒有外部類別所建立的實體,就沒有非靜態內部類別的建構子
區域內部類別 定義於方法內的類別,稱為區域內部類別(local inner classes)。 區域內部類別不屬於外部類別的類別成員,也不是物件成員,其性質就像區域變數一樣,只能在定義的方法內使用,而且必須先定義才能使用。 區域內部類別不使用public、protected、private和static等修飾字。 區域內部類別可以取用外部方法中以final宣告的變數,不是以final宣告的變數不能取用
修飾字(1/3) 存取權限修飾字
修飾字(2/3) 被修飾者可用的存取權限修飾字 修飾字 類別 屬性 建構子 方法 內部類別 public protected (default) private
修飾字(3/3) 被修飾者可用的修飾字 修飾字 類別 屬性 建構子 方法 內部類別 abstract final native static strictfp synchronized transient volatile
類屬(is a) 和擁有(has a) class A extends B implements C, D { E e; F f; A(){ e = new E(); f = new F(); } 類屬(is a)關係為物件和類別(或介面)之間的關係。A型別的物件可以類屬於類別A、類別B、介面C和介面D。 擁有(has a)關係為物件和物件之間的關係。A型別的物件擁有E型別的物件及F型別的物件。
內部類別與繼承 父類別包含內部類別時,子類別也可以繼承內部類別,如同繼承其它成員一樣。 內部類別可以有繼承關係,而且沒有特別限制。
範例5:內部類別繼承其它類別 class EX8_23 { //靜態內部類別,繼承Super23 static class Sub1 extends Super23 {} //一般內部類別,繼承Super23 class Sub2 extends Super23 {} public static void main(String [] args) { System.out.println( new EX8_23.Sub1() ); System.out.println( new EX8_23().new Sub2() ); //區域類別,繼承Super23 class Sub3 extends Super23 {} System.out.println( new Sub3() ); } } //Super23類別定義 class Super23 { Super23() { System.out.print("Super23建構子被呼叫了\t"); } }
匿名類別(Anonymous classes)是區域內部類別的一種,顧名思義匿名類別沒有名稱,所以也不能定義建構子。 匿名類別的目的只是要建立一個物件,物件建立之後就不會再使用該類別。 匿名類別常當作覆蓋或實作抽象方法之用。
匿名類別 匿名類別的功能就是只建立一個物件,所以類別主體的定義和建立物件是一氣呵成的。 繼承類別建立匿名類別: 實作介面建立匿名類別: new 父類別(參數列) { // 類別主體 } 實作介面建立匿名類別: new 介面() { // 類別主體 }
範例6:匿名類別 class EX8_25 { public static void main(String [] args) { Sup s = new Sup() { //實體初始化區塊 { System.out.println(this); } void m() //覆蓋方法 { System.out.println(“覆蓋過的m方法”); } void n() //匿名類別自訂的方法 { System.out.println(“自訂的n方法”); } }; s.m(); //s.n(); //錯誤 } //Sup類別定義 class Sup { void m() { System.out.println("Sup實體的m方法");