学习Spring有一段时间了,对java也有了一点了解,最不能理解的就是接口, 即使是写了接口并实现了它,依然无法理解它到底有什么用?看了其他几篇博客,总结了一下自己的理解。

在JAVA编程语言中是一个抽象类型,是抽象方法的集合。一个类通过继承接口的方式,从而来继承接口的抽象方法。

先从一个通俗的解释看起 (原文:C# 接口《通俗解释》


如果你的工作是一个修水管的,一天客户找上你让你帮装水管,但是有个要求,就是客户喜欢管子是三角形的。

你立马买了三角形的水管回来,在墙上弄个三角形的口子,客户付了钱,你很开心今天有了收入,如下图,很好:

但是好景不长,客户过了一个星期又来找,因为他觉得三角形不好看,要让你换成正方形的水管,你不得不换,因为顾客就是上帝。好吧,继续在墙上弄个正方形的口子,然后又换成正方形的管子来接上。好了,如下图:(但是可能觉得为什么一开始不说要正方形的?因为需求总在变化。。。)

你累得满头大汗,但是还是完成了。可惜不久,客户又来找你,因为他想换成椭圆形的管子了。虽然你很无奈,但是你还是不得不花了几个小时完成。如下图:

安装完成,这时你可能在考虑,为什么换不同形状的水管,我都要大动干戈一番呢?于是你想到一个好方法,那就是墙上设计一个固定的水管并且是圆形的,当客户喜欢什么形状的水管,那么我只需要把客户喜欢的水管的一头做成圆形的,这样,以后都不需要去动墙上的水管了。这是一个好办法。就先在墙上弄个圆形的口,这个口就叫做接口。如下图:

如你所见,墙上有个圆形的口,但是按照原本的:

三角形水管两端是三角形
正方形水管两端是正方形
椭圆形水管两端是椭圆形

那是肯定接不上的,因为三角形、正方形、椭圆形的口怎么和墙壁上圆形的口对接呢?

所以先要实现接口,把:

三角形水管一端做成圆形
正方形水管一端做成圆形
椭圆形水管一端做成圆形

如图,所以,圆形接口做出来了,具体实现是客户去安装,接口本身并不会安装其他形状的水管,换句话说就是接口没有具体实现,只是告诉你,你的水管要接入,必须有一端是圆形的(接口的约束),因为我只留这个圆形的接口,你要装别的形状的管子,先把一个弄成圆形的就好了(子类去实现接口的方法),不管什么形状,都要一个必须做成圆形才能对接得上,它必须要你按照我的规范来做。这就是为什么新手会觉得接口什么都不做,只定义接口,没有任何实现,那不是多此一举吗?因为它的实现是子类去完成。这样只要客户喜欢什么形状的水管,只要实现了我的接口(圆形),都能对接得上,而且改变起来也很方便,只要把水管扭上去就行了,不用在去给墙壁挖洞了。


初步了解接口,再用代码进一步理解

需求:公司有两个人分别写了2个动物类,让你写一个类来输出它们各自喜欢的食物。

//A写的Dog类,里面有个likeFood方法
class Dog
{ public void likeFood()
{
Print("我是小狗,我喜欢吃肉");
} }
//B写的Cat类,里面有个likeFood方法,如下:
class Cat
{ public void likeFood()
{
Print("我是小猫,我喜欢吃鱼");
} }

你写的Zoo类如下:

//动物园类
class Zoo { public void show(Dog dog){
dog.likeFood();
} public void show(Cat cat){
cat.likeFood();
} }

在输出的时候使用如下:

void Main()
{
Zoo zoo = new Zoo();
zoo.show(new Dog()); //"我是小狗,我喜欢吃肉"
zoo.show(new Cat()); //"我是小猫,我喜欢吃鱼"
}

这一切工作良好,但好景不长,公司又需要给动物园增加一个猴子,让C去写这个Monkey类,并能输出它喜欢的食物。


class Dog
{ public void likeFood()
{
Console.WriteLine("我是小狗,我喜欢吃肉");
} }
//B写的Cat类,里面有个likeFood方法,如下:
class Cat
{ public void likeFood()
{
Console.WriteLine("我是小猫,我喜欢吃鱼");
} }
//C写的Monkey类,里面有个likeFood方法,如下:
class Monkey
{ public void likeFood()
{
Console.WriteLine("我是猴子,我喜欢吃桃子");
} }

于是你的Zoo类就变成了这样,仅仅多了一个重载方法:

class Zoo
{
public void show(Dog dog)
{
dog.likeFood();
} public void show(Cat cat)
{
cat.likeFood();
} public void show(Monkey money)
{
money.likeFood();
}
} void Main()
{ Zoo zoo = new Zoo();
zoo.show(new Dog()); //"我是小狗,我喜欢吃肉"
zoo.show(new Cat()); //"我是小猫,我喜欢吃鱼"
zoo.show(new Monkey()); //"我是猴子,我喜欢吃桃子" }

虽然只是增加了一个类,但善于思考的你马上就会想到:“如果后面还有更多动物要输出它们喜欢的食物,我的Zoo类都要修改,这对我来说不是一件好事。”
仔细观察Zoo类,发现不变的是show方法,变化的是show方法是参数。因为每个动物都不一样,所以参数也就不一样。所以原来就需要重载多个方法。
如果有一个类,能接收所有动物,那不就解决了?没错,于是你想到了定义一个父类叫Animal,里面有个likeFood方法,让所有动物类去继承Animal。

class Zoo
{ public void show(Animal animal)
{
animal.likeFood();
} } class Animal
{ public void likeFood()
{
Print("我是Animal类");
} }

你告诉原来的A和B两人,让它们写的动物类都去继承Animal,并且里面有个输出动物喜欢食物的方法。

class Dog : Animal
{ public void likeFood()
{
Print("我是小狗,我喜欢吃肉");
} } class Cat : Animal
{ public void likeFood()
{
Print("我是小猫,我喜欢吃鱼");
} } class Monkey : Animal
{ public void likeFood()
{
Print("我是猴子,我喜欢吃桃子");
} }

运行也一切良好,不管你以后还有什么类,只要让需要添加的动物类,继承Animal,并有个likeFood方法,那么你无需修改Zoo,只需要再main方法里传入动物类的实例就能正常工作。
你大赞你聪明绝顶,这样一来,你的Zoo根本不需要改变了。
有一天,公司新来一个人,暂时叫D吧,公司让D写个兔子类,你告诉D,你写的Rabbit类必须继承Animal,并且有一个它喜欢的食物的方法。

class Rabbit : Animal
{ public void favoriteFood()
{
Print("我是兔子,我喜欢吃萝卜");
} } void Main()
{ Zoo zoo = new Zoo();
zoo.show(new Dog()); //"我是小狗,我喜欢吃肉"
zoo.show(new Cat()); //"我是小猫,我喜欢吃鱼"
zoo.show(new Monkey()); //"我是猴子,我喜欢吃桃子"
zoo.show(new Rabbit()); //"我是Animal类" }

Raabit并没有输出预想的结果,你不得不花了点时间去排查原因,最后你发现这不是什么大问题,因为新来的D虽然写了Rabbit类,但是他并不知道你们之前约定的动物喜欢的食物命名为:likeFood()
D写的Rabbit类,里面的方法叫:favoriteFood()
虽然最后D修改他的Rabbit类的方法为likeFood(),但你还是对这个问题做了一番思考,为什么会导致这个问题?
那是因为没有一种约束,使得子类继承父类的时候必须实现父类的方法。有没有一个类,能让它的子类必须实现它定义的方法?有,那就是接口
于是你修改Animal为接口,代码如下:

interface Animal {

    public void likeFood();

}

总结

由上述的例子可以总结接口的几个用处:

  1. 使代码易于维护,不管增加多少动物类,只要实现了animal接口,就都能拿来用
  2. 制定一种标准和规范,减少命名不统一带来的一系列问题
  3. 方便团队开发,设计者不用关心具体方法的实现,实现者可以清楚地知道自己的任务是什么。

这里还有一个问题:在我写代码时,都是先有方法,对象,再有类,然后才去抽象成接口,那么我的每一个类都需要抽象成接口吗?在开发过程中,每写一个方法都要去写它的接口的抽象方法,实现它的serviceTest以及调用它的controllerTest,最后才能真正放到程序中去用,即使是一个小小的功能都要做很多“无用功”,而且需求一旦变更,改起来不是更麻烦?

Java 接口理解的更多相关文章

  1. java接口理解(转载)

    今天和同事好好的讨论了java接口的原理和作用,发现原来自己的对接口的理解仅仅是局限在概念的高度抽象上,觉得好像理解了但是不会变化应用其实和没有理解差不多.以前看一个帖子说学习一个东西不管什么时候都要 ...

  2. 我对面向对象设计的理解——Java接口和Java抽象类

    在没有好好地研习面向对象设计的设计模式之前,我对Java接口和Java抽象类的认识还是很模糊,很不可理解. 刚学Java语言时,就很难理解为什么要有接口这个概念,虽说是可以实现所谓的多继承,可一个只有 ...

  3. Java学习之Java接口回调理解

    Java接口回调 在Java学习中有个比较重要的知识点,就是今天我们要讲的接口回调.接口回调的理解如果解释起来会比较抽象,我一般喜欢用一个或几个经典的例子来帮助加深理解. 举例:老板分派给员工做事,员 ...

  4. java接口的理解

    接口的最主要的作用是达到统一访问,就是在创建对象的时候用接口创建,[接口名] [对象名]=new [实现接口的类],这样你像用哪个类的对象就可以new哪个对象了,不需要改原来的代码,就和你的USB接口 ...

  5. java接口调用——webservice就是一个RPC而已

    很多新手一听到接口就蒙逼,不知道接口是什么!其实接口就是RPC,通过远程访问别的程序提供的方法,然后获得该方法执行的接口,而不需要在本地执行该方法.就是本地方法调用的升级版而已,我明天会上一篇如何通过 ...

  6. Java接口的表现形式

    一.概念理解 Java接口是一些方法特征的集合,并没有方法的具体实现,类似于电源插座,可以充不同类型的电器,但是必须适配特定的接口规范.接口是抽象化的,所以其不能被实例化的(不能有构造函数,创建对象) ...

  7. Java初始化理解与总结 转载

    Java的初始化可以分为两个部分: (a)类的初始化 (b)对象的创建 一.类的初始化 1.1 概念介绍: 一个类(class)要被使用必须经过装载,连接,初始化这样的过程. 在装载阶段,类装载器会把 ...

  8. Effective Java通俗理解(持续更新)

    这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约 ...

  9. Effective Java通俗理解(下)

    Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...

随机推荐

  1. Day8 - B - Non-Secret Cypher CodeForces - 190D

    Berland starts to seize the initiative on the war with Flatland. To drive the enemy from their nativ ...

  2. RDD 可视化 —— RDDOperationScope.withScope

    最近在看各种博客,学习 spark 源代码. 网上对源代码的分析基本都是基于 0.7, 0.8, 1.0 的代码,而现在的发行版已经是 1.5 了.所以有些代码不大对的上.比如函数 RDD.map() ...

  3. 解决使用xampp无法通过ip访问的问题

    本地能通过127.0.0.1或者localhost访问,无法通过外网地址访问: win解决防火墙问题: 1.进入控制面板 -> 系统和安全 -> 2.window防火墙 - 允许其他程序通 ...

  4. DStream-04 Window函数的原理和源码

    DStream 中 window 函数有两种,一种是普通 WindowedDStream,另外一种是针对 window聚合 优化的 ReducedWindowedDStream. Demo objec ...

  5. git上传流程

    转: 1.在github上创建项目 2.使用git clone https://github.com/xxxxxxx/xxxxx.git克隆到本地 3.编辑项目 4.git add . (将改动添加到 ...

  6. CentOS 6.8 32位 安装mysql8

    1.清理掉之前安装过的mysql rpm -qa | grep mysql mysql-libs-5.1.52-1.el6_0.1.x86_64 yum remove mysql-libs-5.1.5 ...

  7. PlayJava SpringMVC与Struts2杂谈

    一 先做一个简单对比: 1. SpringMVC的入口是Servlet,核心是DispatcherServlet,Struts2是Filter,核心是FilterDispatcher 2. Sprin ...

  8. 094-PHP遍历索引数组和关联数组

    <?php $arr=array(63,'abc',45,'hello',3,7,9,'DEF'); //定义一个索引数组 echo '遍历一个索引数组:<br />'; forea ...

  9. docker 安装好后启动异常解决

    一个月前在虚拟机中根据视频教程安装了docker 启动docker后执行 systemctl status docker 出现了异常,具体如下: [root@joinApp2 ~]# systemct ...

  10. SpringMVC:提交日期类型报400错误解决方法

    方法1:可以使用@ControllerAdvice增强Controller @ControllerAdvice public class BaseControllerAdvice { // 初始化绑定 ...