學姊最棒!

無論你如何引用、使用更改此文件,必定要讓以上那句話隨著此份文件出現,並且這幾句話也要跟著出現,讓別人能夠知道引用時的規矩。


此份文件於2002 6/25 10:00 更新


Polymorphism


就是一個東西可以有多種面貌。至少要從兩個角度來看,一個是從Object的概念來看:
class A {

int m;
int n;

}

class B extends A {

int o;

}

則一個B的物件,看少一點也可以看成A的物件。(以Java Compiler的角度來看,繼承下來的變數是一直疊上去的,所以B的物件實際上按順序有m, n, o這三個變數。你若是把這三個變數都看進去,可以看成B;你也可以看少一點,只看m, n,可以看成A)

另一個是從變數的概念:
A o = new B();
o = new A();
同一個變數可以指向不同型態的物件。

Interface


說到interface要從三個方面來看,interface-type variable, interface, class(object)。為什麼要有interface?為了彌補Java沒有多重繼承(multiple inheritance)而來的。Java為了避免像C++一樣支援Multiple inheritance,卻付出了巨大的代價,所以不支援多重繼承,而要用interface。要是有多重繼承的話,會是這種狀況:

class A {

int m;

}

class B {

int n;

}

class C extends A, B {

int o;

}

則class C中的變數排列也許會是這樣:
int m;
int n;
int o;
則class C可不可以用Polymorphism的方法看成class A?可以,因為從上往下看一個變數int m;完全符合class A。但是問題來了,可不可以看成class B?當我們從上往下看C一個變數(因為class B只有一個變數),是int m,不是int n。這就不符合了。所以Java並不支援多重繼承,就是為了避免這種麻煩。所以interface捨棄了和implementation有關的instance var, static method,就是為了要Polymorphism的方便。

用法大概是這樣:
interface I {

public static final int var = 1;
public abstract void Method();

}

class B implements I {

public void Method() {

//... implementation

}

}

I v = new B();
v.method();

嗯,還有interface可以繼承別的interface,而且可以多重繼承。
interface J extends I, K {

//....

}

AWT


最重要的Event-Listener的概念。E我們一個AWT的程式跑起來了以後,JVM(Java Virtual Machine)就會幫我們產生出多個Thread出來,負責處理視窗圖形。當我們滑鼠在螢幕上動來動去、或者是有什麼動作之類的,JVM會幫我們產生Event。這個Event也是一個Object,裡面有一個instance variable叫做EventSource,記錄這個Event是從誰而來的。而Event-Listener負責的事情就是處理這些Event。例如說你的滑鼠在一個Button上按了一下,JVM就會幫你產生一個Event,並且幫你把這個Event的EventSource指向那個button,然後JVM會把這個Event塞進一個Queue裡面。此時有一隻Thread叫做AWT-EventQueue-0,會不時地檢查看看EventQueue裡面是否有Event,如果有,就把這個Event從Queue中拿出來,並且根據此Event的EventSource,把這個Event丟給EventSource的Event-Listener處理。




觀念:老師講過的另外一個小地方就是Canvas,當你invoke canvas.repaint()的時候,並不是直接去呼叫那個Canvas的paint() method,而是產生一個repaint Event,丟到EventQueue中,再由AWT-EventQueue0去把Event抓出來,找EventSource,再去invoke Canvas.paint()。


Nested Class


簡單的說就是在Class裡面宣告Class。分成以下四種:
Static Nested Class
Inner Class
Local Inner Class
Anonymous Inner Class(嚴格說起來,算是Local Inner Class)

先說說為什麼會有Inner Class的存在吧!Inner Class之所以存在,是為了解決一個在AWT程式上很麻煩的問題:

假設我現在有一個Frame,裡面有一個TextField和Button,我想要讓這個Button被按下去的時候,去動到TextField。於是我們程式就這麼寫:

class MyFrame extends Frame {

TextField t;
MyFrame() {

addWindowListener(new WindowAdapterF());
setVisible(true);
t = new TextField(40); // Create TextField Instance
add("North", t);
Button b = new Button("Clear"); //Create Button Instance
add("South", b);
b.addActionListener(new MyActionListener());
// 給Button一個EventListener,讓Button可以去處理被按下的Event
pack();

}

}

那我們的EventListener要怎麼寫呢?

class MyActionListener implements ActionListener {

public void actionPerformed(ActionEvent e) {

???.t.setText("");
//我們要怎樣摸到那個t?沒有任何辦法!e.getSource()只能摸到Button。

}

}

當然,有另外的方法可以解決這個問題,但是我們覺得太麻煩了,所以禁不起大眾的要求,加入了Inner Class的功能。

Static Nested Class (Interface)

上課沒講,不算Inner Class,是最單純的一種Nested Class。
用法:
class A {

static class B{

//....

}

}
就像其他A的member一樣,B前面可以加Access Control(決定A以外的人能不能用到B這個Class)。而B這個class可以盡情取用A的member,就算是private也照取不誤![Sample]
A以外的class若是要使用B,必須寫全名:A.B。例如:

class C extends A.B {

//....

}

有一點要注意,這樣子C就失去了取用A的private的能力。

內部原理:(待續)

Inner Class

Non-static Nested Class叫做Inner Class,它的宣告和Static Nested Class差不多,少了static:
class A {

class B{

//....

}

}

任何Inner Class的Object生成都必須要有一個Enclosing Class(就是包在外面的Class,範例中就是A)的Object。
例如我要產生一個B的object,必須:

A a = new A();
B b = a.new A.B();

(若這段Code在A的instance method裡面,直接寫B b = new B();就好。因為這樣等於B b = this.new B();)
每個Inner Class的物件都會有一個指標(Compiler幫我們加的),指向一個Enclosing Class的物件。

Inner Class裡面不可以有static members,除非是static final。

而Inner Class可以Access所有Enclosing Class的Member,就算是Private,而Enclosing Class也可以Access所有的Inner Class的Member。

內部原理:(待續)

Local Inner Class


寫在Method裡面的Inner Class,宣告像這樣:
class A {

int m() {

class B {

//....

}

}

}

它的作用範圍就只有在那個Method裡面有效,除了可以access Enclosing Class的Member之外,它還可以access local variable。不過它只能夠access 被宣告為final的local variable喔!

內部原理:(待續)

Anonymous Inner Class

更簡化的Local Inner Class。因此和Local Inner Class一樣,可以access一樣的東西。
每次要為了產生一個Event Listener物件,就要先宣告一個Class,然後宣告完了也只有產生一個物件而已,實在太麻煩了。在這種情況下,就有了Anonymous Inner Class產生:

class A {

int m() {

Object a = new Object () {

int i;
int method (int s) {

// ....

}

};
return a;

}

}

Anonymous是沒有名字的,而且一定和new有關。宣告Anonymous Inner Class後,一定要立刻被new出來,然後以後再也用不到了。(因為沒有名字)如果它寫成這樣:
int m() {

Object a = new B(3) {

int i;
int method (int s) {

// ....

}

};
return a;

}

的話,代表這個Anonymous Inner Class extends B or implements B。(看B是class還是interface決定)而傳進去的那個3,則會invoke super的constructor,把3傳進去那個繼承自B的constructor。



關於Nested Class,若有空我會整理一張


Exception


也是物件,繼承自Throwable。
分為synchronous exception與asynchronous exception:

synchronous exception
由JVM產生的,可能是JVM發生錯誤,這時候就已經沒救了,你的Program也管不著。

asynchronous exception
由你的Program來產生。

而asynchronous exception又分成兩種:
checked exception
被定義好,明確知道在哪裡會被throw出來的exception,compiler會檢查。

unchecked exception
分成兩種:

RuntimeException
Error

不知道在哪裡會產生的,有可能隨時隨地產生也不一定。例如ArrayIndexOutOfBound Exception、ArithmeticException…等。


Exception Handling
try-catch-finally:

try {

//try block

} catch (XXException e) {

//catch block

} catch (Exception e) {

//catch block

} finally {

//finally block

}

在try-block裡面的程式碼就是要測試的程式碼。如果執行try-block裡面的程式碼安然無事,沒有Exception,就會忽略下面的那些catch,要是有Exception發生,就會立刻從發生處中斷,依序往下找catch block。若是Exception符合catch後面的型態(用Polymorphism的觀點,只要是那個型態的Sub-class就算符合),就會把這個Exception給assign到catch後面的變數(本例是e),然後執行catch-block裡面的東西。

而finally則是無論如何一定會執行的block,它會在這塊try-catch-finally執行將要執行完時,例如要try裡面執行完畢什麼事都沒發生,或是try執行到一半有Exception,被catch抓到然後catch block執行完畢,或是catch抓到後catch block內又產生新的Exception於是被中斷,或是catch block根本抓不到的時候…等,都一樣,當它們要離開這個try-catch-finally block時,就會執行finally裡面的程式碼,然後才離開。注意一個觀念,finally是以組合語言為單位,並非以statement為單位。例如:

int i = 1;
try {

return i++;

} finally {

System.out.println(i);

}

return i++;這行statement會先把右邊的值evaluate出來,我們知道i++ evaluate出來的值是1。此i已經變成2了。這時候return原本會離開這個block,但是因為有finally block,它會在要離開之前先做,於是就把i給印出來,印出2。等到印完了以後,才會真正把原來那個1給return回去。

而剛剛說到Exception符合catch-block後面的型態就會被catch住,但是要是Exception都沒有符合的型態,就會一路衝,任何不在try-block裡面的程式碼發生了Exception只有一個動作:中斷並且往上一層扔!這Exception除非找到一個try-block,並且被catch住才會停下來。否則的話,一直往上扔的結果,到了main也無法處理,就往上扔給JVM,JVM就幫你印出printStackTrace(),然後結束掉。舉例:

public static void main(String arg[] ) {

try {

m(); //#4

}catch (AException e) {

//.... #5

}

}

static void m() {

try {

int a = 3/0;
//.... #1

}catch(BException e) {

//.... #2

}catch(AException e) {

//.... #3

}

}

int a = 3/0;這行因為除以零,會發生ArithmeticException,而且這exception的發生點又在try-block內,因此就會中斷掉,然後往下面的catch-block找看看能不能被catch。所以#1的地方是不會被執行到的!現在假設這個ArithmeticException被紅色那行catch(BException e)給抓住了,然後現在執行#2處的程式碼,這時竟然又發生了AException,因為Exception發生之處並不在try-block內,所以它會中斷,並且立刻丟到上一層。因此並不會被#3處給catch到,而是回到上一層,也就是m()被invoke的地方,此時#4之處因為有一個AException,而且#4又在try-block內,因此會去找catch,於是就被#5處給catch到了。要是#5處又發生exception,那往main的上一層丟,就是JVM了。於是程式結束。這個觀念比較容易考trace,大家多多注意。喔,對了,try後面一定要有catch或finally,不能都兩個沒有。只要有其中任一個就可以。

throw
throw是什麼呢?throw的意思就是你要丟出一個exception了!(相當於之前講的Exception發生)你new 出一個exception的物件沒有用,它只是一個Object而已,只是一塊memory而已。要你throw它了以後,才會發生以上的事情。用法很簡單,像這樣:

throw new MyException() ;

throws
throws是一個子句,用來修飾method。是這樣用的:

int method () throws IOException, AException {

//....

}

他說明了一個method可能會丟出什麼checked Exception。一個Method若是要丟checked exception,一定要把那些exception給寫在throws後面,Compiler會檢查。至於unchecked exception就不用寫。


Thread

我好累喔…這部份我看就算了吧!


另外Virtual Method Invocation,Hiding的觀念大家一定要弄清楚喔!太累了我就不寫了