在jdk的源碼中,存在這樣的一些接口,他們不包含任何的(抽象)方法,可是卻普遍的存在。java
這種接口咱們稱之爲Mark Interface,也就是標記接口。數組
這些接口呢,咱們不用來實現任何的方法,他們的做用就是當某個類實現這個接口的時候,咱們就認爲這個類擁有了這個接口標記的某種功能了。dom
下面經過三個例子,分別介紹java中經常使用的三個標記接口:ide
RandomAccess 、Cloneable、java.io.Serializableui
(1)RandomAccess this
在C#中常常會有不少人在爭論,在遍歷集合時,究竟是應該用for仍是用foreach。spa
在Java中,卻徹底不用再糾結這個問題:指針
java中有這樣一個接口code
1 public interface RandomAccess { 2 }
這個接口的做用是判斷集合是否能快速訪問的。也就是傳入一個Index後,指針可否快速的移動到對應的元素上,仍是須要像訪問隊列同樣依次移動到指定元素上。server
若是咱們在實現某個容器類時,容器(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )中的元素能夠經過index快速的訪問到(通常核心的存儲接口是使用數組),那麼你在該類的定義處,就能夠像ArrayList這樣打上一個RandomAccess接口的實現標籤:
1 public class ArrayList<E> extends AbstractList<E>
2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable 3 { 4 //...
5 } 6 而LinkedList就沒有實現該標記接口 7 public class LinkedList<E>
8 extends AbstractSequentialList<E>
9 implements List<E>, Deque<E>, Cloneable, java.io.Serializable 10 { 11 //...
12 }
在使用的過程當中,經過判斷是否實現RandomAccess接口,就能夠決定採起哪一種遍歷的方式了。
以下:
1 import java.util.List; 2 import java.util.RandomAccess; 3
4 public class SourceLearning 5 { 6 public void iteratorElements(List<String> list) 7 { 8 if (list instanceof RandomAccess) 9 { 10 for (int i = 0, size = list.size(); i < size; i++) 11 { 12 String element = list.get(i); 13 } 14 } 15 else
16 { 17 for (String element : list) 18 { 19 } 20 } 21 } 22 }
這樣針對於不一樣的List採起不一樣的遍歷形式,可讓遍歷的速度更快。
(2)Cloneable
這個接口你們都很是熟悉,在深度拷貝的時候,經常用到該接口。這個接口也是一個標記接口:
1 public interface Cloneable { 2 }
他的做用是標記該對象的是否擁有克隆的能力。不少認或許會以爲疑惑,Object類自己就已經實現了 protected native Object clone() throws CloneNotSupportedException;
方法
按道理來講每個類都應該能夠運行clone方法的,爲何還須要打上這樣一個接口。這樣的好處是以接口的形式標記對象是否擁有某種能力。想想,假若不經過標記接口的形式,咱們在日常的工做中,如何實現呢?通常來講都是經過設定枚舉,或者增長變量來控制。這樣或許能解決問題,可是每每不能從面向對象的角度來優雅的解決問題。
想一想接口的做用是什麼吧?接口就是用來標記某個類擁有了哪些功能、特性。而標記接口則是在面向對象的角度來看,更高層級的一種抽象:即便你擁有這個方法也不行,由於你沒有這個功能的標記接口。
因此在調用的clone的過程當中,假若對象沒有實現Cloneable 接口,那麼虛擬就會拋出一個CloneNotSupportedException,不支持的clone的異常。
(3)java.io.Serializable
這個接口是用來標記類是否支持序列化的。所謂的序列化就是將對象的各類信息轉化成能夠存儲或者傳輸的一種形式。我記得我剛參加工做的時候,對這個序列化很是難以理解,以爲server返回一個對象,client接收便可,爲何總要(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )序列化,反序列化的折騰。後來leader告訴我這是由於不少時候,因爲通訊協議的緣由,在傳輸的過程當中,複雜的類對象是不支持傳來傳去的,因此通常來講要轉化成流的形式,放在包中傳來傳去。言歸正傳,java.io.Serializable和Cloneable 接口同樣,假若一個類沒有實現該接口,而被拿去序列化,虛擬機就會拋出不支持的異常,儘管從代碼的調用來講,不存在任何問題。
請看java源碼註釋中的第二個拋出異常:一個將要被序列化,可是未實現序列化接口的Object:
1 /**
2 * Write the specified object to the ObjectOutputStream. The class of the 3 * object, the signature of the class, and the values of the non-transient 4 * and non-static fields of the class and all of its supertypes are 5 * written. Default serialization for a class can be overridden using the 6 * writeObject and the readObject methods. Objects referenced by this 7 * object are written transitively so that a complete equivalent graph of 8 * objects can be reconstructed by an ObjectInputStream. 9 * 10 * <p>Exceptions are thrown for problems with the OutputStream and for 11 * classes that should not be serialized. All exceptions are fatal to the 12 * OutputStream, which is left in an indeterminate state, and it is up to 13 * the caller to ignore or recover the stream state. 14 * 15 * @throws InvalidClassException Something is wrong with a class used by 16 * serialization. 17 * @throws NotSerializableException Some object to be serialized does not 18 * implement the java.io.Serializable interface. 19 * @throws IOException Any exception thrown by the underlying 20 * OutputStream. 21 */
22 public final void writeObject(Object obj) throws IOException { 23 if (enableOverride) { 24 writeObjectOverride(obj); 25 return; 26 } 27 try { 28 writeObject0(obj, false); 29 } catch (IOException ex) { 30 if (depth == 0) { 31 writeFatalException(ex); 32 } 33 throw ex; 34 } 35 }
至此,經過三種經常使用的標記接口,應該已經闡述清標記接口的使用狀況了,我的認爲這是一種很是抽象的面向對象的方式。即只經過向對象以添加標籤的形式,來標記這個對象能夠或不能夠實現某種功能,這要比直接定義變量或枚舉,要優雅的多。