Java的介面與內部類
介面:主要用於描述類具有什麼功能,而不是給出每個功能的具體實現。
一個類可以實現一個或多個介面,並在需要介面的地方,隨時使用實現了相應的介面的物件。
克隆物件(深拷貝):指建立一個新物件,且新物件的狀態與原始物件的狀態相同
當對克隆的新物件進行修改時,不會影響原始物件的狀態。
內部類機制:內部類定義在另一個類的內部,其中的方法可以訪問包含它們的外部類的域,主要用於設計具有相互協作關係的類集合。
一、介面
在Java程式設計語言中,介面不是類,而是對類的一組需求描述,這些類要遵從介面描述的統一格式進行定義。
1>介面中的所有方法自動地屬於public,因此,在介面中宣告方法時,不必提供關鍵字public。
2>介面中可以定義常量,不能含有例項域,也不能在介面中實現方法。
3>類實現介面的步驟:
a>將類宣告為實現給定的介面。 關鍵字 implements
b>對介面中的所有方法進行定義。
注:
i>在介面宣告中,方法不需要宣告為public,因為在介面中的所有方法都自動地是public。不過,在實現介面時,必須把方法宣告為public,否則,編譯器將認為訪問屬性為包預設,即類的預設訪問屬性,之後編譯器就會給出試圖提供更弱的訪問許可權的警告資訊。
ii>如果需要呼叫底層的排序服務必須讓它實現Comparable介面的compareTo方法。
例如:使用Arrays.sort()方法對陣列進行排序,本質上必須對兩個物件進行比較,而比較的方式就是通過呼叫Comparable介面的compareTo方法。所以需要實現Comparable介面,如果直接建立compareTo方法,而不繼承介面的話,Java也無法呼叫。
( 1 )介面的特性
1>介面不是類,不能使用new運算子例項化一個介面。
2>不能構造介面的物件,卻能宣告介面的變數,但是介面變數必須引用實現了介面的類物件。
3>如同使用instanceof檢查一個物件是否屬於某個特定類一樣,也可以使用instance檢查一個物件是否實現了某個特定的介面。
4>介面可以被擴充套件,使用extends關鍵字類擴充套件某個介面。
5>介面中不能包含例項域或靜態方法,但卻可以包含常量。
6>介面中的方法都自動被設定為public,介面中的域將被自動設為public static final。
7>每個類只能擁有一個超類,但卻可以實現多個介面。
( 2 )介面與抽象類
Java不支援多繼承,因為多繼承會讓語言本身變得非常複雜,效率也會降低。
介面可以提供多重繼承,同時能避免多重繼承的複雜性和低效性。
二、物件克隆
當拷貝一個變數時,原始變數與拷貝變數引用同一個物件,改變一個變數所引用的物件將會對另一個變數產生影響。
使用clone方法可以建立一個物件的新的copy,最初狀態與原來的變數引用的物件一樣,而且可以各自改變自己的狀態。
1>clone方法時Object類的一個protected方法,使用者編寫的程式碼中不能直接呼叫。
2>預設的克隆操作是淺拷貝,資料域是數值或基本型別,沒有問題,但是如果包含子物件的引用,拷貝的結果會使兩個域引用同一個子物件。
因此需要重新定義clone方法,以便實現克隆子物件的深拷貝。
重新定義,類必須:
a>實現Cloneable介面;
b>使用public訪問修飾符重新定義clone方法。
注:
i>在這裡Cloneable介面的出現與介面的正常使用沒有任何關係,尤其是,它並沒有指定clone方法。該介面在這裡是作為一個標記,表明類設計者要進行克隆處理。如果不繼承該介面,就會產生一個已檢查異常。
ii>標記介面沒有方法,使用它的唯一目的是可以用instanceof進行型別檢查。
iii>只要在clone中含有沒有實現Cloneable介面的物件,Object類的clone方法就會丟擲一個CloneNot-SupportException異常。
iv>必須謹慎地實現子類的克隆,而且克隆並不像人們想象的普遍。在標準類庫中,只有不到5%的類實現了clone。
三、介面與回撥
回撥是一種常見的程式設計模式,在這種模式中,可以指出某個特定事件發生時應該採取的動作。
例如:
package com.interfaces; /** @version 1.00 2000-04-13 @author Cay Horstmann */ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; // to resolve conflict with java.util.Timer public class TimerTest { public static void main(String[] args) { ActionListener listener = new TimePrinter(); // construct a timer that calls the listener // once every 10 seconds Timer t = new Timer(10000, listener); t.start(); JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } } class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); Toolkit.getDefaultToolkit().beep(); } }
四、內部類
內部類:定義在另一個類中的類。
使用內部類的原因:
1>內部類方法可以訪問該類定義所在的作用域中的資料,包括私有的資料。
2>內部類可以對同一個包中的其他類隱藏起來。
3>當想定義一個回撥函式且不想編寫大量程式碼時,使用匿名內部類比較便捷。
( 1 )使用內部類訪問物件狀態
內部類即可以訪問自身的資料域,也可以訪問建立它的外圍類物件的資料域。(在呼叫內部類的建構函式時,隱式的將外圍類的引用作為傳遞)
( 2 )匿名內部類
建立一個實現介面的類的新物件,需要實現介面中的方法定義在括號{}內。
語法格式為:
其中SuperType是介面,在{}內實現相應的方法。這樣內部類就實現了這個介面。new SuperType(construction parameters){ inner class methods and data }
SuperType也可以是一個類,於是內部類就擴充套件它。
( 3 )靜態內部類
有時候,使用內部類只是為了把一個類隱藏在另一個類的內部,並不需要內部類引用外圍類物件。
為此,可以將內部類宣告為static,以便取消產生的引用。
( 4 )代理proxy
Java SE1.3新增加的特性。利用代理可以在執行時建立一個實現了一組給定介面的新類。這種功能只有在編譯時無法確定需要實現哪個介面時才有必要使用。
對於應用程式設計人員來說,遇見這種情況的機會很少,但是對於系統程式設計人員來說,代理帶來靈活性卻十分重要。
1>代理類可以在執行時建立全新的類,這樣代理類能夠實現指定的介面,而且具有下列方法:
a>指定介面所需要的全部方法。
b>Object類中的全部方法。
2>不能再執行時定義這些方法的新程式碼,而是要提供一個呼叫處理器。呼叫處理器是實現了InvocationHandler介面的類物件,介面中只有一個方法:Object invoke(Object proxy,Method method,Object[] args)
3>無論何時呼叫代理物件的方法,呼叫處理器的invoke方法都會被呼叫,並向其傳遞Method物件和原始的呼叫引數。呼叫處理器必須給出處理呼叫的方式。
建立代理物件,需要使用Proxy類的newProxyInstance方法,有三個引數:
a>一個類載入器
b>一個Class物件陣列,每個元素都是需要實現介面
c>一個呼叫處理器
4>代理類的特性
注:就目前而言,對於代理的部分感覺不理解,目前不常使用,所以暫且擱置,以後再詳細瞭解。a>代理類在程式執行過程中建立的,一旦,就變成了常規類,與虛擬機器中的任何其他類沒有什麼區別。
b>所有的代理類都擴充套件於Proxy類,一個代理類只有一個例項域------呼叫處理器,它定義在Proxy的超類中。
為了履行處理物件的職責,所需要的任何附加資料都必須儲存在呼叫處理器中。