匿名物件
在產生一個物件時, 通常會指定類別型態, 變數名稱, 如 Pokemon p1=new Pokemon()
但有時產生物件時, 也懶的給變數名稱, 就會形成了匿名物件
while(true){
new Pikachu();
}
這種寫法在Java是沒問題的, 因為一直產生出來的皮卡丘, 會被垃圾回收機制給刪除, 所以不會造成記憶体不足. 但如果是寫在 C/C++裏, 大約二秒後, 記憶体就不足了, 連滑鼠也動不了.
一般類別
先看如下程式碼
public class App {
public static void main(String[] args) {
Pikachu p1=new Pikachu();
Pikachu p2=new Pikachu();
}
}
abstract class Pokemon{
int level;
abstract void setLevel(int l);
}
class Pikachu extends Pokemon{
@Override
void setLevel(int l) {
this.level=l;
}
}
Pikachu 繼承了Pokemon, 然後產生了二隻皮卡丘. 所以通常會先寫一個繼承Pokemon的Pikachu類別, 然後再由Pikachu類別產生p1及p2二個物件.
若需要產生一隻超級皮卡丘, 一輩子只會出現一次, 而且通常只是短暫的時光, 完成任務就就死了. 如果也要產生一個類別, 再產生一個物件, 好像有點怪怪的. 這種一輩子只產生一個物件的類別, 就成了匿名類別的重點.
匿名類別
超級皮卡丘的產生, 可由如下藍色的程式碼達成. 先使用父類別參考(Pokemon), 再產生一個匿名的類別, 並實作抽象方法. 記得最後要加上 “;”
public class App {
public static void main(String[] args) {
public static void main(String[] args) {
Pokemon p3=new Pokemon(){
@Override
public void setLevel(int l) {
this.level=l*10000;
}
};
}
}
abstract class Pokemon{
int level;
abstract void setLevel(int l);
}
class Pikachu extends Pokemon{
@Override
void setLevel(int l) {
this.level=l;
}
}
使用父類別參考
由上可知, 產生的新類別並沒有名稱, 那物件的參考該如何決定呢? 當然也只能使用父類別當作參考. 如 Pokemon p3=new Pokemon(){……}
匿名類別建構子
建構子名稱必需與類別名稱一模一樣. 但匿名類別並沒有名稱. 所以建構子當然無法寫出來. 所以如果希望在物件產生時利用建構子初始化, 那麼匿名類別就不適用了.
Pokemon p3=new Pokemon(){
public 建構子????(){
}
};
擴充新方法
匿名類別使用了父類別參考, 所以如果在匿名類別中加了擴充方法後, 雖然編譯不會出錯, 但確無法使用. 如下所示, p3.lighting()是錯誤的. 不要忘了, 使用父類別參考子類別物件時, 子類別的擴充功能是會失效的.
所以, 在匿名類別中, 通常不會再加入新的方法
Pokemon p3=new Pokemon(){ @Override public void setLevel(int l) { this.level=l*10000; } public void lighting(){ System.out.println("一萬伏特閃電攻擊"); } }; //p3.lighting(); error
繼承抽象類別的匿名類別
抽象類別是無法產生物件的, 所以依上面程式碼, Pokemon p=new Pokemon(); 是一個錯誤的寫法.
但如果是Pokemon p=new Pokemon(){….}; 則是產生一個沒有名字的類別, 然後再由此匿名類別產生物件.
繼承一般類別的匿名類別
乍看之下, 匿名類別好像都是繼承了抽象類別而來的, 但其實不然, 匿名類別也可以繼承一般類別.
public class App { public static void main(String[] args) { Pikachu p4=new Pikachu(){ //無需作任何事 //也可以覆蓋setLevel()方法 }; } } abstract class Pokemon{ int level; abstract void setLevel(int l); } class Pikachu extends Pokemon{ @Override void setLevel(int l) { this.level=l; } }
實作介面的匿名類別
介面一樣不能產生物件. 但卻也可以由介面產生新的匿名類別, 再由此匿名類別產生物件
public class App {
public static void main(String[] args) {
Land p5=new Land(){
@Override
public void setWalk() {
System.out.println("時速為5公里");
}
};
p5.setWalk();
}
}
interface Land{
void setWalk();
}
繼承類別再實作介面的匿名類別
假如有一個類別如下
class Eve extends Pokemon implements Land()
那麼是否上面的Eve也可以使用匿名類別來完成呢? 目前是不可能. 不過, Oracle改天更新版本又想出了一些怪招的話, 這也說不一定.
編譯的檔名
上面的程式碼中, 在檔案總管裏可以看出, 每個類別都會編譯出class檔, 如App.class, Pokemon.class, Pikachu.class.
注意喔, 連介面 interface Land{} 都會產生出一個Land.class. 所以由此可知, Java好像也把介面當成一個類別在看待
那匿名類別呢? 則產生出 App$1.class. $後面那個 “1”, 則是流水編號
Runnable
多執行緒的介紹, 將於另一篇章節中詳細說明, 在此僅說明匿名類別的應用
多執行緒的Runnable, 是一個介面, 系統就已經幫我們寫好了, 所以我們不需要撰寫. 裏面的程式碼如下
interface Runnable{ public void run(); }
當需要使用Runnable物件時, 可由如下方式執行.
Thread t1=new Thread(new Runnable(){ @Override public void run() { for (int i=0;i<100;i++){ System.out.printf("%s:%d\n", Thread.currentThread().getName(), i); } } });
匿名Thread物件啟動 start()
當我們產生一個Thread物件 t1, 必需使用t1.start()來啟動新的執行緒. 但當我們使用匿名物件時, 那要如何啟動呢? xxx.start();
此時可以在產生匿名物件後直接start(),
new Thread(new Runnable(){…….}).start(); 如下所說
new Thread(new Runnable(){
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.printf("%s:%d\n", Thread.currentThread().getName(), i);
}
}
}).start();