6 接口與內部類
接口 interface
對象克隆
內部類 inner class
代理 proxy
?
6.1 接口
public interface Comparable<T>
{int compareTo(T other);
}
Arrays.sort(Object[] a) 利用的是mergesort
?
接口也可以被擴展
public interface Moveable
{void move(double x, double y);
}
public interface Powered extends Moveable
{double milesPerGallon(); //public abstractdouble SPEED_LIMIT = 95; //public static final
}
標準庫中的SwingConstants接口只包含NORTH、SOUTH和HORIZOTAL等常量,任何實現此接口的類都自動繼承了這些常量。
class Employee implements Cloneable, Comparable
?
6.2 對象克隆
Object的clone()是protected,默認淺拷貝。
?
實現Cloneable接口(空接口,標記用)
使用public訪問修飾符重新定義clone方法
?
標記接口的唯一作用,可以使用instanceof進行類型檢查
class Employ implements Cloneable
{public Employ clone() throws CloneNotSupportedException{...}
}
只要在clone中含有未實現Cloneable接口的對象,Object類的clone方法就會拋出一個CloneNotSupportException異常。
如果是final類,也可不拋異常,而是try-catch。
?
6.3 接口與回調
回調(call back)是一種常見的設計模式。可以指出某個特定事件發生時應該采取的動作。
?
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}
?
class TimePrinter implements ActionListener{ }
new Timer(1000, new TimePrinter()).start(); ?
//Timer構造器的第一個參數是發出通告的時間間隔,單位是毫秒;第二個是監聽器對象,在時間到后執行actionPerformed();
?
javax.swing.JOptionPane
static void showMessageDialog(Component parent, Object message)
如果parent為null,則在中央顯示
javax.swing.Timer
Timer(int interval, ActionListener listener)
void start()
void stop()
java.awt.Toolkit
void beep()
?
6.4 內部類
定義在另一個類中的類。
使用原因:
內部類方法可以訪問該類定義所在的作用域中的數據,包括私有的數據
內部類可以對同一個包中的其他類隱藏起來
當想定義一個回調函數時,使用匿名內部類比較便捷
?
內部類中添加了一個外圍類引用的參數 OuterClass.this
在外部類外引用公開內部類OuterClass.InnerClass
?
內部類是一種編譯器現象,與虛擬機無關。
編譯器將內部類翻譯成用$分隔外部類名與內部類名的常規類文件,虛擬機對此一無所知
OuterClass$InnerClass
編譯器為了引用外圍類,生成了一個附加的實例域final OuterClass this$0
外圍類將添加靜態方法 static boolean access$0(OuterClass),InnerClass將調用此方法來訪問外部類的私有域。
?
?
局部內部類不能用public或private訪問說明符進行聲明
它的作用域被限定在聲明這個局部類的塊中
優勢:對外部世界可以完全隱藏起來
可以訪問外部類,和被聲明為final的局部變量。
?
匿名內部類?anonymous inner class
InnerClass ic = new InnerClass(){ 類的實現 };
InnerClass可以是一個接口,類內實現接口方法
只創建類的一個對象
?
靜態內部類
只是為了把一個類隱藏在另一個類的內部,并不需要內部類引用外圍類對象
用static 進行聲明
?
6.5 代理
在運行時創建一個實現了一組給定接口的新類。
這種功能在編譯時無法確定需要實現哪個接口時才有必要使用。
?
應用場景:
一個接口,其確切類型在編譯時無法知道。
需要在程序運行狀態定義一個實現這些接口的新類。
?
方案一:
生成代碼,將代碼放置在一個文件中,調用編譯器進行編譯,然后再加載結果類文件。
方案二:
代理機制,在運行時創建全新的類。這樣的代理類能夠實現指定接口。
指定接口有如下方法:
指定接口所需的全部方法;
Object類中的全部方法:toString equals hashcode
?
方法:
1、定義調用處理器(invocation handler),包裝基本類對象。
調用處理器是實現了InvocationHandler接口的類對象。在這個接口中只用一個方法:
Object invoke( Object proxy, Method method, Object[] args)
當調用代理對象的方法時,調用處理器的invoke方法都會被調用,并向其傳遞Method對象和原始的調用參數,調用處理器給出處理調用的方式。
2、創建代理對象。
使用Proxy類的newProxyInstance方法,有三個參數:
一個類加載器、一個class對象數組(每個元素都是需要實現的接口)、一個調用處理器
?
例:
public interface Run
{void run();
}
public class Animal implements Run
{@Overridepublic void run(){System.out.println("Animal is running");}
}
public RunHandler implements InvocationHandler
{private Object target;public RunHandler(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args){System.out.println("Proxy is running");return method.invoke(target, args);}
}
Animal animal = new Animal(); //基本類對象
InvocationHandler handler = new RunHandler(animal); //封裝基本類對象
//handler封裝的是實現Run接口的類對象。
Object proxy = Proxy.newProxyInstance(Run.class.getClassLoader(), new Class[]{Run.class}, handler);
(Run)proxy.run(); //此時代理對象proxy可以調用Run接口的方法。
代理的特性:
代理類是在運行過程中創建的,一旦被創建,就變成常規類,與虛擬機中的任何其他類沒有區別。
所有代理類都擴展于Proxy類。
一個代理類只有一個實例域——調用處理器,它定義在Proxy的超類中。
為了履行代理對象的職責,所需要的任何附加數據都必須存儲在調用處理器中。
?
代理類覆蓋了Object類中的方法toString equals hashCode,這些方法調用了調用處理器的invoke。
Object類的clone和getClass沒有被重新定義。
?
沒有定義代理類的名字,虛擬機中的Proxy類將生成一個以字符串$Proxy開頭的類名。
對于特定類的加載器和預設的一組接口來說,只能有一個代理類。即,如果使用同一個類加載器和接口數組調用了兩次newInstance方法的話,那么只能得到同一個類的兩個對象。
可以用Class proxyClass = Proxy.getProxyClass(null, interfaces);
代理類一定是public和final。
可通過Proxy類中的isProxyClass方法檢測一個特定的Class對象是否代表一個代理類。
?