作者: 許裕永
開發類別是為了能建構物件;一個類別能夠建構無數個物件。但如果只是一個,片段程式碼中會用到的物件,或者只是某個大物件中的一個獨有的物件。這時是否值得為了這個物件,來獨立開發一個類別,就值得商確了。
Java中的內部類別,指的是在類別之中開發類別,或是在方法之中開發類別,甚至可以開發沒有名字的類別。這些類別的寫法不難,限制也不多,在認證時卻可 以在大部份的題目中,看到它的身影。而且在開發實務上,也是佔有相當的份量,不會使用內部類別來開發程式的人,不算學會Java。
而因為宣告的類別是外部類別(Enclose Classes)的成員,所以實體內部類別成員(Inner Classes)宣告時,可以在class之前加上存取限制。而且類別之中,可以任意存取外部類別中其他的成員。
語法:[存取限制][類別形態]class ClassName{}
存取限制:和一般實體成員相同。
類別形態:和一般類別相同(final,abstract,但不可以是static)。
例一:
public class OuterClass{
private int x,y;
class InnerClass{
private int z;
void setValue(int s){
z=x=y=s;
}
}
}
示範主題:1、InnerClass宣告的內容如同一般的類別。2、在InnerClass中可以存取外部類別中的任意成員(即使宣告為private)。
1-1 在外類別中建構內部類別物件
外部類別的任何實體方法成員中,均可以建構內部類別物件。
例一:
public class OuterClass{
private int x,y;
public int getValue(){
InnerClass ic=new InnerClass();
ic.setValue(10);
return x+y;
}
class InnerClass{
private int z;
void setValue(int s){
z=x=y=s;
}
}
public static void main(String[] args){
OuterClass oc=new OuterClass();
System.out.println(oc.getValue());
}
}
列印結果:20。
示範主題:在外部類別的實體方法成員中,建構內部類別物件。
1-2 在其他類別中建構內部類別物件
在取存限制許可的範圍之中,其他類別也可以建構某一個類別的內部類別。但是必須先建構外部類別物件,並透過此外部類別物件才能建構內部類別物件。
例一:
public class OuterClass{
private int x,y;
public int getValue(){
return x+y;
}
class InnerClass{
private int z;
void setValue(int s){
z=x=y=s;
}
}
public static void main(String[] args){
OuterClass oc=new OuterClass();
OuterClass.InnerClass ic=oc.new InnerClass();
ic.setValue(20);
System.out.println(oc.getValue());
}
}
列印主題:40。
示範主題:在其他類別中建構內部類別物件的宣告及建構語法。
例二:
將例一main中的建構內部類別物件的語法修改為:
OuterClass.InnerClass ic=new OuterClass().new InnerClass();
列印結果為:0。
示範主題:1、如果不需外部類別物件時,可以用此語法建構內部類別物件。
2、因為ic的建立與oc無關,也就是ic設定的x和y不是oc的x和y,所以列印結果為0。
1-3 內部類別的this
類別開發時常使用this來代表本類別物件,在內部類別中也不例外。
例一:
public class OuterClass{
class InnerClass{
void showThis(){
System.out.println(this);
System.out.println(OuterClass.this);
}
}
public static void main(String[] args){
OuterClass.InnerClass ic=new OuterClass().new InnerClass();
ic.showThis();
}
}
列印結果:OuterClass$InnerClass@757aef OuterClass@d9f9c3。
示範主題:內部類別中,“this”指的是本類別物件;“外部類別名稱‧this”指的是外部類別物件。
宣告語法:[類別形態]class ClassName{}
類別形態:abstract 或 final。(不可以是static)
因為是宣告在方法之中,自然也只能在本方法之中建構成物件,但是必須注意宣告及建構的順序,也就是程式敍述句排列的順序。如同其他區域變數一般,必須先宣告再建構。
例:
void abc(){
class Aaa{}
aaa a=new Aaa();
}
類別Aaa的宣告,必須在建構敍述句之上。
宣告在方法中的類別,也可以在類別中存取或呼叫外部類別的任意成員(即使宣告為private的成員)。
例一:
public class OuterClass{
private int x;
public void showLocal(){
x=9;
class MethodLocal{
int y=10;
void showValue(){
System.out.println(x*y);
}
}
MethodLocal ml=new MethodLocal();
ml.showValue();
}
public static void main(String[] args){
new OuterClass().showLocal();
}
}
列印結果:90。
示範主題:Method-Local Inner Classes可以存取外部類別的任意成員。
區域內部類別有一個特別要注意的重點:「不可以存取方法中宣告的區域變數,除非該變數宣告為final」。這是因為區域變數的存活期為該方法的執行時期, 若方法結束則區域變數便不存在,但是該區域內部類別建構的物件,卻可能在方法執行完畢後,還繼續存在。如果它仍然存取已經不存在的區域變數,便會產生錯 誤。但是宣告為final的區域變數,其記憶體配置的方式與區域變數不同,其值並不會消失,所以可以被區域內部類別建構的物件存取。
例二:
public class OuterClass{
public void showLocal(){
int x=9;
class MethodLocal{
int y=10;
void showValue(){
System.out.println(x*y);
}
}
MethodLocal ml=new MethodLocal();
ml.showValue();
}
public static void main(String[] args){
new OuterClass().showLocal();
}
}
本例編譯錯誤。
示範主題:區域內部類別不可以存取非宣告為final的區域變數。(若在int x=9;之前加上final,則本例列印:90)。
匿名類別,是開發某一個類別的下層類別,或開發實作某一個界面的類別。但是,沒有辦法既繼承類別又實作界面,這是要特別注意的。
宣告語法一:new ClassName(參數列){}
建構一個匿名類別的物件,並指定呼叫上層類別的建構方法。
ClassName指的是匿名類別要繼承的上層類別名稱。但是連同參數列一起看,其實是呼叫上層類別的建構方法。而在建構方法後端,以大括號表示建立一個 新類別,大括號中可以定義下層類別的成員,一般用來overriding上層類別的方法成員。而整句的意思便是:「開發一個新類別繼承 ClassName,再以這個新類別建構成物件」。
以此語法來體會,我們應該要知道,Java這個匿名類別機制的重點不是匿名類別本身;而是要建構某一個類別的下層類別物件。因為這個下層類別物件必須擁有上層類別物件所沒有的功能,而開發一個全新的類別又沒必要的時候,便使用匿名類別機制,來建構這個下層類別物件。
例一:
import java.io.*;
public class AnoClassTest{
public static void main(String[] args){
File f=new File("AnoClassTest.java"){
public String getName(){
return "檔案名稱:" + super.getName();
}
};//請特別注意這個‘;’
System.out.println(f.getName());
}
}
列印結果:檔案名稱:AnoClassTest.java。
示範主題:1、建構下層類別物件,並指派給以上層類別名稱宣告之參考變數。(物件多型)2、下層類別中overriding上層類別的方法成員,來增加上 層類別物件所沒有的功能。3、物件執行的,是下層類別重新定義的方法內容(物件多型)。4、撰寫此語言時,一定要注意File f=…這行敍述句一直到 ‘;’才結束。撰寫時是為了方便程式碼的閱讀,才會分成數行文字,但一定不要忘記,敍述句是以‘;’結尾。
宣告語法二:new InterfaceName(){Overriding all methods}
建構實作此界面的匿名類別的物件。
和建構下層類別物件有兩大差異:1、沒有參數列(要保留小括號)。2、要overriding InterfaceName中的所有方法。
例二:
interface MyInterface{
void showText();
}
public class AnoClassTest{
public static void main(String[] args){
MyInterface mi1=new MyInterface(){
public void showText(){
System.out.println("Hello");
}
};
MyInterface mi2=new MyInterface(){
public void showText(){
System.out.println("Hi");
}
};
mi1.showText();
mi2.showText();
}
}
列印結果:Hello Hi。
示範主題:1、建構的不是界面物件,而是實作界面的匿名類別的物件。2、物件指派給以界面名稱宣告的參考變數(物件多型)。3、不同的物件執行的方法,是建構自己的匿名類別中定義的方法內容。
例一和例二中都是把物件指派給參考變數,其優點是透過參考變數,可以重複使用該物件。但並不是一定要把物件指派給參考變數,也可以把物件直接當參數使用。
例:addWindowListener(new WindowListener(){……..});
…….代表overriding WindowListener中的所有方法(請參閱本章範例二)。這種寫法要注意的還是括號及分號,請注意是整個建構匿名類別物件的語法都寫在小括號之中當參數。
匿名類別定義的時候還有一些要注意的事項:
- 财 不可以宣告建構方法。
因為匿名類別沒有名字,而建構方法的名稱必須和類別名稱完全一樣,自然無法定義。 - 财 不能加任何修飾詞。無論是final,abstract,static,或存取限制修飾詞。
因為這些修飾詞必須加在類別宣告句之前,我們剛才的範例中有寫到類別宣告句嗎?沒有。我們宣告的是參考變數。也就是說,如果你在宣告句前加上final,編譯和執行自然都沒有問題,只不過你是宣告參考變數為final,而不是類別為final。 - 财 沒有必要宣告任何下層類別的新成員。
在匿名類別中新增資料成員或方法成員都是合法的,也就是編譯沒有問題,只是沒有必要。因為不可能用得到:「以上層類別宣告的參考變數只能存取上層類別中宣告的成員,不能存取在下層類別中宣告的成員(物件多型)」。
因為宣告為static,所以和其他類別成員一樣,不屬於物件,而且不可以存取類別中的非static成員。
宣告語法:static[存取限制] [類別形態]class ClassName{}
除了一定要有static之外(若沒有即變成實體內部類別成員),其他均和一般類別相同。
例:
public class OuterClass{
static class InnerClass{}
}
1-1 在外類別中建構類別成員內部類別物件
例一:
public class OuterClass{
static int x;
static class InnerClass{
void setValue(){
x=8;
}
}
public void abc(){
InnerClass ic=new InnerClass();
ic.setValue();
}
public static void main(String[] args){
OuterClass oc=new OuterClass();
oc.abc();
System.out.println(oc.x);
}
}
列印結果:8。
示範主題:1、Static Nested Classes中只能存取外部類別的static成員。2、任何外部類別的實體方法成員(abc()),均可 以存取Static Nested Classes。3、Static Nested Classes中宣告的成員,不一定要是static成員。
1-2 在其他類別中建構類別成員內部類別物件
例二:
public class OuterClass{
static int x;
static class InnerClass{
void setValue(){
x=8;
}
}
public static void main(String[] args){
OuterClass.InnerClass oic=new OuterClass.InnerClass();
oic.setValue();
System.out.println(OuterClass.x);
}
}
列印結果:8。
示範主題:在其他類別建構Static Nested Classes物件時,只需要用外部類別的名稱來呼叫其建構方法,不需要先建構外部類別物件。而且new指令下的位置也不同,請注意。
實體內部類別成員物件建構:
Outer outer=new Outer();
Outer.Inner inner=outer.new Inner();
或
Outer.Inner inner=new Outer().new Inner();
總之,要透過外部類別物件才能建構內部類別物件。
類別靜態類別成員:
Outer.Inner inner=new Outer.Inner();
直接以外部類別名稱呼叫建構方法。
1-3 this
static成員中不可存取任何實體成員,因為和物件無關,所以也就不可以使用代表本類別物件的this。
範例一:以實體內部類別成員成式實作視窗監聽器界面。
檔案:MyWindow.java
import java.awt.*;
import java.awt.event.*;
public class MyWindow extends Frame{
public MyWindow(){
super("大笨視窗");
setBounds(200,200,180,100);
addWindowListener(new MyWindowListener());
}
class MyWindowListener implements WindowListener{
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowClosing(WindowEvent e) {
dispose();
}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
}
public static void main(String[] args){
new MyWindow().show();
}
}
範例主題:1、在內部類別中實作監聽器界面,可以讓外部類別不必實作界面的許多方法,而擁有太多方法成員。2、addListener()中的參數是內部類別的物件。
範例二:以實作視窗監聽器界面的匿名類別建構之物件為參數。
檔案:MyWindow.java
import java.awt.*;
import java.awt.event.*;
public class MyWindow extends Frame{
public MyWindow(){
super("大笨視窗");
setBounds(200,200,180,100);
addWindowListener(new WindowListener(){
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowClosing(WindowEvent e) {
dispose();
}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
});
}
public static void main(String[] args){
new MyWindow().show();
}
}
範例主題:以實作視窗監聽器界面的匿名類別建構之物件為addWindowListener()的參數,可以省略內部類別之宣告,但是僅適用於:此物件只須建構一個的狀況。
範例三:運用監聽器轉接類別來建構匿名類別物件。
檔案名稱:MyWindows.java
import java.awt.*;
import java.awt.event.*;
public class MyWindow extends Frame{
public MyWindow(){
super("大笨視窗");
setBounds(200,200,180,100);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
public static void main(String[] args){
new MyWindow().show();
}
}
範例主題:在java‧awt‧event中,Java針對常用的視窗元件的事件監聽器,均發了轉接器類別(XxxAdapter)。這些類別分別實作了 一個或一個以上的界面,也就是說已經overriding了那些界面的方法。需用到監聽器時,只須開發新類別繼承那些轉接器類別後,overriding 會用到的方法即可。這樣就不必像實作界面一般,要overriding界面中的所有方法。特別適用於匿名類別。
- 财 實體內部類別成員之中,可以任意存取外部類別中其他的成員(即使宣告為private)。
- 财 外部類別的任何實體方法成員中,均可以建構內部類別物件。
- 财 必須先建構外部類別物件,並透過此外部類別物件才能建構內部類別物件。
- 财 內部類別中,“this”指的是本類別物件;“外部類別名稱‧this”指的是外部類別物件。
- 财 因為是宣告在方法之中,就如同其他宣告在方法中的區域變數一般,不可以加存取限制修飾詞。
- 财 必須注意宣告及建構的順序,也就是程式敍述句排列的順序。如同其他區域變數一般,必須先宣告再建構。
- 财 方法中的類別,也可以在類別中存取或呼叫外部類別的任意成員(即使宣告為private的成員)。
- 财 不可以存取方法中宣告的區域變數,除非該變數宣告為final。
- 财 匿名類別是寫在方法中的程式碼,所以它也必須符合區域內部類別的規範,尤其是:不可以存取非宣告為final的區域變數。
- 财 此類別只能使用一次,也就是只能建構一個物件。
- 财 匿名類別,是開發某一個類別的下層類別,或開發實作某一個界面的類別。但是,沒有辦法既繼承類別又實作界面。
- 财 撰寫時是為了方便程式碼的閱讀,才會分成數行文字,但一定不要忘記,敍述句是以‘;’結尾。
- 财 實作界面的匿名類別的物件和建構下層類別物件有兩大差異:1、沒有參數列(要保留小括號)。2、要overriding InterfaceName中的所有方法。
- 财 不是一定要把物件指派給參考變數,也可以把物件直接當參數使用。
- 财 不可以宣告建構方法。
- 财 不能加任何修飾詞。無論是final,abstract,static,或存取限制修飾詞。
- 财 沒有必要宣告任何下層類別的新成員。
- 财 Static Nested Classes中只能存取外部類別的static成員。
- 财 任何外部類別的實體方法成員(abc()),均可以存取Static Nested Classes。
- 财 Static Nested Classes中宣告的成員,不一定要是static成員。
- 财 在其他類別建構Static Nested Classes物件時,只需要用外部類別的名稱來呼叫其建構方法,不需要先建構外部類別物件。
- 财 不可以使用代表本類別物件的this。
沒有留言:
張貼留言