接口(interface)對於面向對象編程來講是一個很是重要的概念。它是一系列方法的聲明,卻沒有具體實現。有些編程語言,好比swift,把接口解釋成「協議(protocol)」,我以爲也是很是的恰當的,接口就是對方法制定的一套規範。java
本人通過從事多年的java開發工做,以及自身對面向對象編程思想的理解,總結了如下幾點接口的常見用法,但願能夠對新手的理解有所幫助。程序員
這是接口的最根本的做用。有時候類內部流程的某一個環節,並非由此類本身決定的。類並不知道在這一步時具體會執行什麼內容。因此須要在此處設置一枚接口,提供對外界的擴展。數據庫
對於這個功能有一個很是好的範例。編程
一個秒錶類:用於測量執行某一段代碼執行所需的時間。swift
初步的代碼相似於下面這樣:數組
public class Stopwatch { private long deltaTime; public void clear() { deltaTime = 0L; } public long getDeltaTime() { return deltaTime; } public void start() { long startTime = System.currentTimeMillis(); // do something deltaTime = System.currentTimeMillis() - startTime; } }
而使用Stopwatch的代碼相似於這樣:微信
public static void main(String[] args) { Stopwatch stopwatch = new Stopwatch(); stopwatch.clear(); //時間清零 stopwatch.start(); //開始運行 System.out.printf("time: %dms\n",stopwatch.getDeltaTime()); }
那麼咱們知道對於秒錶這個類自己,它並不能決定本身在do something那個位置將會測量什麼內容的代碼。框架
也就是說此處代碼將會由Stopwatch
的外部決定。因此此處能夠商定一個協議,也就是接口。Stopwatch
決定本身開放出一個接口叫NeedMonitoringProgram
須要監控的程序,其中doProgram()方法就是執行須要被監控的代碼。外部在使用Stopwatch
時只須要去實現NeedMonitoringProgram
接口也就定義好了須要監測的代碼。編程語言
public interface NeedMonitoringProgram { void doProgram(); }
給Stopwatch
賦予NeedMonitoringProgram
對象分佈式
public class Stopwatch { private long deltaTime; private NeedMonitoringProgram program; public void setProgram(NeedMonitoringProgram program) { this.program = program; } public void start() { long startTime = System.currentTimeMillis(); program.doProgram(); deltaTime = System.currentTimeMillis() - startTime; } public interface NeedMonitoringProgram { void doProgram(); } //省略clear()和getDeltaTime()方法 }
模擬測量1到1000000疊加求和所需時間
Stopwatch stopwatch = new Stopwatch(); Stopwatch.NeedMonitoringProgram program = new Stopwatch.NeedMonitoringProgram() { @Override public void doProgram() { int sum = 0; int number = 1_000_000; for (int i = 0; i <= number; i++) { sum += i; } System.out.printf("sum: %d\n", sum); } }; stopwatch.setProgram(program); stopwatch.clear(); stopwatch.start(); System.out.printf("time: %dms\n",stopwatch.getDeltaTime());
假如說待測代碼是須要外部參數的,而且也須要返回給外部執行結果的。也能夠把接口方法改進一下。
添加一個Param
類來存儲參數。
private Param params = new Param(); public void addParam(String key, Object value) { params.put(key, value); } public interface NeedMonitoringProgram<RETURN_OBJECT> { RETURN_OBJECT doProgram(Param params); } public class Param extends HashMap<String, Object> { }
start方法也能夠直接加入NeedMonitoringProgram
對象,減小使用時的步驟。 而且NeedMonitoringProgram
類的泛型指定了返回類型。
public <T> T start(NeedMonitoringProgram<T> program) { long startTime = System.currentTimeMillis(); T returnObject = program.doProgram(params); deltaTime = System.currentTimeMillis() - startTime; return returnObject; }
在執行start()以前使用addParam()給代碼賦值。而start()也會返回代碼執行的結果。
Stopwatch stopwatch = new Stopwatch(); stopwatch.clear(); stopwatch.addParam("number", 1_000_000); int sum = stopwatch.start(new Stopwatch.NeedMonitoringProgram<Integer>() { @Override public Integer doProgram(Stopwatch.Param params) { int sum = 0; int number = (Integer)params.get("number"); for (int i = 0; i <= number; i++) { sum += i; } return sum; } }); System.out.printf("sum: %d\n", sum); System.out.printf("time: %dms\n",stopwatch.getDeltaTime());
運用JDK8的lambda表達式甚至能夠縮寫爲
int sum = stopwatch.start(params -> { int s = 0; int number = (Integer)params.get("number"); for (int i = 0; i <= number; i++) { s += i; } return s; });
雖然java沒有函數對象這一律念,可是使用lambda能夠充分表達一種函數式編程的思想,在這裏接口即等同於表明了一個函數對象。
以上是接口最最根本的功能,普遍運用在AWT、Swing、Android這類圖形化程序中。像XXXListener這些處理控件事件的接口都是運用了此功能。
能夠把具體子類對象都看成父類來看,屏蔽不一樣子類對象之間的差別。
public interface Animal { void sayHello(); }
public class Cat implements Animal{ @Override public void sayHello() { System.out.println("I'm a cat."); } }
public class Dog implements Animal{ @Override public void sayHello() { System.out.println("I'm a dog."); } }
public static void main(String[] args) { Animal[] animals = new Animal[]{new Cat(), new Dog()}; for (Animal animal : animals) { animal.sayHello(); } }
在這裏把Cat和Dog都一視同仁的放入了Animal的數組內,而且批量處理。多態的功能其實和抽象類(abstract class)相同。
一個簡單的Dao
public interface UserDao { User findUser(int id); void addUser(User user); void updateUser(User user); void deleteUser(int id); }
這個DaoFactory
工廠類表明某一框架內部生成Dao的邏輯(超簡化)
public class DaoFactory { public <T> T getDaoInstance(Class<T> clazz) { if(clazz == UserDao.class) { return new UserDaoImpl(); } else if(……) { } } }
那麼咱們在使用該框架時,只須要從DaoFactory
中經過Dao的類型得到該Dao的具體實現對象。
咱們只關心該Dao接口有哪些咱們能夠用的方法,而不會去關心這個Dao內部是怎麼實現增刪改查的。拿着這個Dao對象咱們就能夠去完成數據庫的操做。也就是說接口爲咱們屏蔽了方法的具體實現,屏蔽了具體的實現類中一些雜亂無章的,對於使用者來講無用的中間方法。
public interface XXXService { }
在大型網站項目中,底層的服務都是基於分佈式部署的。好比說Dubbo框架,在分佈式項目中,接口和實現是分離於兩個子項目中的。分佈式的優點在於,經過架設多臺服務提供者(provider)維持了服務提供的穩定性。只要不是全部服務提供者掛機,服務消費者(consumer)依然能夠獲得穩定的服務。對於服務消費者來講它並不關心服務是由誰提供的,只關心有哪些服務能夠用,因此接口既屏蔽了方法的具體實現,甚至還能夠接來自不一樣服務提供者的服務。在這裏,接口起到了一個協議的做用,消費者須要哪些服務,由接口描述,同時提供者根據接口實現了本身的處理服務邏輯。
咱們經常會遇到一些諸如XXXable,以able結尾的類
好比說java.io.Serializable
這是賦予了類可序列化的能力
咱們本身也能夠寫給類賦予能力的接口以及實現。
作過微信支付的應該都知道,對接微信支付須要發送包含xml字符串的HTTP請求。
那麼就能夠對微信支付功能封裝一個客戶端(Client)
那麼大體步驟以下:
public class WechatClient{ /** * 統一下單 */ public void unifiedOrder() { //1. 生成xml //2. 發送HTTP請求 //3. 處理請求結果 } }
接下來給客戶端類賦予可發送HTTP請求的能力,聲明一個接口:
public interface HttpSendable { //發送GET請求 HttpResponse getHttps(String url); //發送包含raw富文本的POST請求 HttpResponse postHttps(String url, String raw); }
HttpSendable
顧名思義就是可發送HTTP請求的能力
那麼給客戶端加上這個能力
public class WechatClient implements HttpSendable
這個能力的實現可使用抽象父類實現,不會形成次要代碼污染主要類的邏輯
public abstract class HttpSender implements HttpSendable{ @Override public HttpResponse getHttps(String url) { return null; } @Override public HttpResponse postHttps(String url, String raw) { return null; } }
HttpSender
就是HTTP的發送者,固然它也有發送HTTP的能力HttpSendable
而後客戶端繼承了它
public class WechatClient extends HttpSender implements HttpSendable{ /** * 統一下單 */ public void unifiedOrder() { //1. 生成xml String xml = "";//代碼略 //2. 發送HTTP請求 HttpResponse response = super.postHttps("https://", xml); //3. 處理請求結果 //代碼略 } }
像這樣寫代碼,是否是會增長代碼的可理解性了呢?
通常老java程序員都喜歡這麼用,包括JDK內部也有這麼用的。把一些常量扔在接口裏面。
//申請狀態常量 public interface ApplyStatusContants { public static final String STATIC_UNTREATED = "STATIC_UNTREATED";//未處理 public static final String STATIC_ACCEPT = "STATIC_ACCEPT"; //經過 public static final String STATIC_REJECT = "STATIC_REJECT"; //拒絕 }
不過自從JDK1.5之後新增了enum枚舉類型。咱們就不須要常量接口了,能夠以下方式:
public enum ApplyStatus { UNTREATED, ACCEPT, REJECT }
簡單粗暴。
接口的用法仍是特別靈活多變的,也許還有本人沒有列舉的用法,歡迎你們補充。