java核心技術卷I-接口

接口

接口不是類,而是對類的一組需求描述,這些類要遵從接口描述的統一格式進行定義。接口可能包含多個方法。在接口中還可以定義常量,然而, 更爲重要的是要知道接口不能提供哪些功能。接口絕不能含有實例域,在 JavaSE 8之前,也不能在接口中實現方法。提供實例域和方法實現的任務應該由實現接口的那個類來完成。因此,可以將接口看成是沒有實例域的抽象類,但是這兩個概念還是有一定區別的。
爲了讓類實現一個接口,通常需要下面兩個步驟:
1 ) 將類聲明爲實現給定的接口。
2 ) 對接口中的所有方法進行定義。
要將類聲明爲實現某個接口, 需要使用關鍵字 implements:

class Employee implements Comparable

接口的特性

接口不是類,尤其不能使用 new 運算符實例化一個接口:

x = new Comparable(. . .); // ERROR

然而, 儘管不能構造接口的對象,卻能聲明接口的變量:

Comparable x; // OK

接口變量必須弓I用實現了接口的類對象:

x = new Employee(. . .); // OK provided Employee implements Comparable

可以使用instance 檢查一個對象是否實現了某個特定的接口

if (anObject instanceof Comparable) { . . . }

與可以建立類的繼承關係一樣,接口也可以被擴展。這裏允許存在多條從具有較高通用
性的接口到較高專用性的接口的鏈

public interface Powered extends Moveable
{
double milesPerCallon();
double SPEED.LIHIT = 95; // a public static final constant
}

與接口中的方法都自動地被設置爲 public—樣,接口中的域將被自動設爲 public static final。
儘管每個類只能夠擁有一個超類,但卻可以實現多個接口。如果希望自己設計的類擁有克隆和比較的能力,只要實現這兩個接口就可以了,使用逗號將實現的各個接口分隔開

class Employee implements Cloneable, Comparable

接口和抽象類的區別

(1)抽象類可以有構造方法,接口中不能有構造方法。
(2)抽象類中可以有普通成員變量,接口中沒有普通成員變量
(3)抽象類中可以包含靜態方法,接口中不能包含靜態方法
(4) 一個類可以實現多個接口,但只能繼承一個抽象類。
(5)接口可以被多重實現,抽象類只能被單一繼承
(6)如果抽象類實現接口,則可以把接口中方法映射到抽象類中作爲抽象方法而不必實現,而在抽象類的子類中實現接口中方法

接口和抽象類的共性

(1) 都可以被繼承
(2) 都不能被實例化
(3) 都可以包含方法聲明
(4) 派生類必須實現未實現的方法

靜態方法

在 Java SE 8 中,允許在接口中增加靜態方法。

默認方法

可以爲接口方法提供一個默認實現。 必須用 default 修飾符標記這樣一個方法。當然, 這並沒有太大用處, 因爲 Comparable 的每一個實際實現都要覆蓋這個方法。不過有些情況下, 默認方法可能很有用。
默認方法的一"t•重要用法是“‘ 接口演化” (interface evolution。) 以 Collection 接口爲例,這個接口作爲 Java 的一部分已經有很多年了。假設很久以前你提供了這樣一個類:

public class Bag implements Collection

後來,在 JavaSE 8 中,又爲這個接口增加了一個 stream 方法。假設 stream 方法不是一個默認方法。那麼 Bag 類將不能編譯,因爲它沒有實現這個新方法。爲接口增加一個非默認方法不能保證“源代碼兼容”
不過,假設不重新編譯這個類,而只是使用原先的一個包含這個類的 JAR 文件。這個類
仍能正常加載,儘管沒有這個新方法。程序仍然可以正常構造 Bag 實例,不會有意外發生。 ( 爲接口增加方法可以保證“ 二進制兼容”)。不過,如果程序在一個 Bag 實例上調用 stream方法,就會出現一個 AbstractMethodError。
將方法實現爲一個默認方法就可以解決這兩個問題。Bag 類又能正常編譯了。另外如果
沒有重新編譯而直接加載這個類,並在一個 Bag 實例上調用 stream 方法,將調用 Collection.stream 方法。

解決默認方法衝突

如果先在一個接口中將一個方法定義爲默認方法,然後又在超類或另一個接口中定義了
同樣的方法,會發生什麼情況? 諸如 Scala 和 C++ 等語言對於解決這種二義性有一些複雜的規則。幸運的是,Java 的相應規則要簡單得多。規則如下:
1 ) 超類優先。如果超類提供了一個具體方法,同名而且有相同參數類型的默認方法會
被忽略。
2 ) 接口衝突。 如果一個超接口提供了一個默認方法,另一個接口提供了一個同名而且
參數類型(不論是否是默認參數)相同的方法,必須覆蓋這個方法來解決衝突。
一個類擴展了一個超類,同時實現了一個接口,並從超類和接口繼承了相同的方法,“ 類優先” 規則,優先使用超類的方法

對象克隆

一個包含對象引用的變量建立副本時會發生什麼。原變量和副本都是同一個對象的引用。這說明, 任何一個變量改變都會影響另一個變量。

Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10); // oops-also changed original

如果希望 copy 是一個新對象,它的初始狀態與 original 相同,但是之後它們各自會有自己不同的狀態,這種情況下就可以使用 clone 方法。

Employee copy = original,clone();
copy.raiseSalary(10); // OK original unchanged

拷貝和克隆示意圖
對於每一個類,需要確定:
1 ) 默認的 clone 方法是否滿足要求;
2 ) 是否可以在可變的子對象上調用 clone 來修補默認的 clone 方法;
3 ) 是否不該使用 clone。
實際上第 3 個選項是默認選項。如果選擇第 1 項或第 2 項,類必須:
1 ) 實現 Cloneable 接口;
2 ) 重新定義 clone 方法,並指定 public 訪問修飾符。
即使 clone 的默認(淺拷貝)實現能夠滿足要求,還是需要實現 Cloneable 接口, 將 clone重新定義爲 public,再調用 super.clone()。

class Employee implements Cloneable{
// raise visibility level to public, change return type
	public Employee clone() throws CloneNotSupportedException{
	return (Employee) super.clone();
	}
}

與 Object.clone提供的淺拷貝相比,前面看到的 clone 方法並沒有爲它增加任何功能。這裏只是讓這個方法是公有的。要建立深拷貝,還需要做更多工作,克隆對象中可變的實例域。

class Employee implements Cloneable{
	public Employee cloneO throws CloneNotSupportedException
	{
	    // call Object,clone0
		Employee cloned = (Employee) super.clone() ; 
		// clone mutable fields
	    cloned.hireDay = (Date) hireDay.clone();
	    return cloned;
	}
}

所有數組類型都有一個 public 的 clone 方法,而不是 protected: 可以用這個方法
建立一個新數組,包含原數組所有元素的副本

int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
int[] cloned = luckyNumbers.clone();
cloned[5] = 12; // doesn't change luckyNumbers[5]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章