接口(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 }
简单粗暴。
接口的用法仍是特别灵活多变的,也许还有本人没有列举的用法,欢迎你们补充。