最开始接触java接口时候,有个疑问『例如我定义了一个接口,但是我在继承这个接口的类中还要写接口的实现方法,那我不如直接就在这个类中写实现方法岂不是更便捷,还省去了定义接口?』。
Object-C类似与 interface的特性叫 protocol即协议,意思更加明确。遵守协议就必须实现协议。
一.基础知识
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
1.接口与类相似点:
一个接口可以有多个方法。
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
接口的字节码文件保存在 .class 结尾的文件中。
接口相应的字节码文件必须在与包名称相匹配的目录结构中。
2.接口与类的区别:
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多重继承。
3.接口特性:
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误。
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
4.抽象类和接口的区别:
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
二.个人理解
接口是一种规范,是一种统一的标准。
统一标准的目的,是大家都知道这个是做什么的,但是具体不用知道具体怎么做。比如说:我知道 Comparable 这个接口是用来比较两个对象的,那么如何去比较呢?数字有数字的比较方法,字符串有字符串的比较方法,学生(自己定义的类)也有自己的比较方法。然后,在另外一个负责对象排序(不一定是数字喔)的代码里面,肯定需要将两个对象比较。
这两个对象是什么类型呢?Object a,b?肯定不行,a > b 这样的语法无法通过编译。int a,b?也不行?一开始就说了,不一定是数字。....所以,Comparable 就来了。他告诉编译器,a b 两个对象都满足 Comparable 接口,也就是他们是可以进行比较的。具体怎么比较,这段程序不需要知道。所以,他需要一些具体的实现,Comparable 接口有一个方法,叫 compareTo。那么这个方法就是用来取代 <、> 这样的运算符。因为运算符是编译器保留给内置类型(整数、浮点数)进行比较用的,而不是一个广义的比较运算。如果你可以明白 JDK 自身库里面诸如 Comparable 这样已经有的接口,那么就很容易理解自己在开发程序的时候为什么需要用到接口了。
接口是一种规范,适合多人合作开发。当你需要调用别人的代码时候,可以规定好接口。别人看到你的接口就知道该实现哪些内容了。我写个接口,再把调用函数写好,你把接口实现了,传个实例进来,任务就完成了,双方的工作都不会收到影响。interface在OC里面叫做protocol,即协议的意思。实现了这个接口,代表完成接口规定的任务。A需要一些能做某些事情的东西,于是A要求,必须实现了xx接口,才能被我调用。实际上也就是个“规范”。
比如发送短信业务。一般发送短信的场景包括注册用户,找回密码,重要通知,修改交易密码等。短信现在的结构是先接上各家短信通道公司,再经由联通移动等发送出去。一般公司备用多个短信通道。我们想要实现在更换短信通道方的时候,不更改其他模块中被引入的代码?接口就能完美的实现了这一点。在开始我们要规定好接口,每个短信通道都实现这些接口。对于我们上层我们不需要管各个通道是如何实现的,只需要在用到这个业务时候调用这个接口就可以了。
3.为什么@Autowired使用在interface上而不是实现类上?
Firstly, it is always a good practice to code to interfaces in general. Secondly, in case of spring, you can inject any implementation at runtime. A typical use case is to inject mock implementation during testing stage.
首先,一般使用接口是很常用并且有益的变成技术。其次,在spring中,你可以在运行过程中注入各种实现。一个很经典的情况就是在测试阶段,注入模拟的实现类。
如果你是一个有了那么点经验的程序员,如果你还没有习惯TDD的开发。可以体验一下这种写法。还是拿短信为例。先写一个SMSServiceTest。然后写一个Test方法。 这个时候什么都没有,不用管。先直接这么写。int code=SMSSevice.sendTextMessage(mobile,content,type);
这个时候IDE会提示你没有这个SMSService,用代码自动生成工具去创建这么一个接口出来。再根据提示把方法创建出来。再写 SMSService smsService=new SMSServiceCorpaImpl();再根据代码把实现类生成了。一般来说IDE会自动留一个空的方法。不用管。这里只是一个简单的例子,但是你发现,当你用TDD的这种方式去写代码的时候,完全不用关系SMSService是怎么内部实现的。你只需要继续写你的单元测试代码好了,明确的知道这个SMSService要做的功能是发送短信,需要传递手机号,内容,类型,返回一个状态码。那么接着说为什么对单元测试很方便?一般而言会用Spring配置Bean,所以实际上你的单元测试代码也不用有改动,无论是测试哪一个实现类,都只通过更改配置文件就可以完成。想想,如果没有接口呢?是不是要对每一个短信通道单独写一个单元测试的方法?
3.对于不需要频繁更变实现类的方法,是不是就可以不用写接口了?答案是No。整个系统架构的代码可以单纯认为有四部分构成。Model+Interface+Service+UtilModel是纯粹的Pojo,贫血模型,Inteface和Service是接口和实现分开的,Util是全项目通用,或者是跨项目通用的,跟业务逻辑没有任何关系的。写接口最大的好处就是在你写的Controller代码,或者是Service里的主要业务逻辑代码的时候,屏蔽掉细节。写一个业务逻辑的时候,比如说修真院的加入班级。第一步,做校验,用户是否为空,班级是否不存在,是否已经加入了班级等等。第二步,更新班级和用户的关系表,更新班级总人数,更新职业总人数,更新用户的最新班级ID。第三步,发送系统通知,告知用户加入班级成功。如果说不用接口,只用实现类的话,第一种方式就是把所有的代码都写在这个Controller里去,代码会非常非常繁琐,一个函数突破几千行轻轻松松,而且改动起来很麻烦。第二种方式就是抽象出来函数。这种方式在某种程度上能够解决代码块大的问题,但是你必须要New一个实现类出来,想想在上述逻辑中,需要new几个实现类?这些实现类就会被New的各处都是,甚至改个名字都很蛋疼。但是如果你使用接口的话,你会发现,接口是强制于你去将复杂的业务逻辑抽象成具体做的事儿。比如说,if(user==null){ // to do something}就变成了CheckUser(uid)这么一个接口。实现类也明确了自已要做的事情。从某种程度上来说,抽象成一个私有方法也能解决这个问题,但是一般都会推荐,如果你发现你写了很多私有方法,要么是他们可以继续演化成一个util,要么是可以成为一个Service。