前面介绍了抽象方法及抽象类的用法,看似解决了不确定行为的方法定义,既然叫唤动作允许声明为抽象方法,那么飞翔、游泳也能声明为抽象方法,并且鸡类涵盖的物种不够多,最好把这些行为动作扩展到鸟类这个群体,于是整个鸟类的成员方法都可以如法炮制了。可是这种做法也带来了一些弊端,包括但不限于:
1、能飞的动物不仅仅是鸟类,还有昆虫、蝙蝠等其它动物也能飞,难不成昆虫类、哺乳动物类也要自行声明飞翔方法?这么做显然产生了重复的方法定义。不然的话,要是把飞翔方法挪到更底层的动物类,一大群动物为了不沦为抽象类都得重写飞翔方法,比如鳄鱼、大象等根本不会飞的动物也要装模作样扑腾几下,实在是滑天下之大稽。
2、除了几种常见的鸟类为大众所熟知之外,大部分鸟类其实人们一时半刻叫不出它们的名字,倘若在路上偶遇一只鸟儿,难道因为不认识它就没法描述它的模样了吗?(如果鸟类是个抽象类,外部是不能创建鸟类实例的)
3、就算给整个动物类都添加了叫唤、飞翔、游泳这些抽象方法,并且费尽九牛二虎之力把所有派生而来的子类都实现了这三个抽象方法,也不意味着万事大吉。譬如青蛙擅长跳跃这个动作,哪天程序员突发奇想要给抽象的动物类补充跳跃方法,从而支持青蛙的跳跃行为,随之而来的代价便是让动物类的所有子类都重写跳跃方法,这样也太伤筋动骨了。
综上所述,抽象类解决不了层出不穷的问题,远非什么灵丹妙药,只能用于处理符合条件的特定要求。若想真正有效应对这些刁钻古怪的挑战,还得指望新的抽象技术,在Java编程中这就是接口。接口不从属于类,而是与类平级,类通过关键字class标识,而接口通过关键字interface来标识。由于接口是作为类的辅助角色出现,因此它在结构上与类比较相似,不过也有不少不同之处,举例如下:
1、凡是类都有构造方法,即便是抽象类也支持定义构造方法,但接口不允许定义构造方法,因为接口只用于声明某些行为动作,本身并非一个实体。
2、在Java8以前,接口内部的所有方法都必须是抽象方法,具体的方法内部代码有赖于该接口的实现类来补充。因为有这个强制规定,所以接口内部方法的abstract前缀可加可不加,即使不加abstract,编译器也会默认把该方法当作抽象方法。
3、至于接口内部的属性,则默认为终态属性,即添加了final前缀的成员属性。当然这个final前缀也是可加可不加的,即使不加final,编译器仍会默认把该属性当作终态属性。
按照上述的接口规定,再来编写一个定义了动物行为的接口代码,其中主要包括飞翔方法、游泳方法、奔跑方法等,详细的接口定义代码示例如下:

//定义一个接口。接口主要声明一些特定的行为方法
public interface Behavior { // 声明了一个抽象的飞翔方法。注意,接口内部的方法默认都是抽象方法,所以可以不用添加abstract前缀
public void fly();
//abstract public void fly(); // 这里的abstract可加可不加 // 声明了一个抽象的游泳方法
public void swim(); // 声明了一个抽象的奔跑方法
public void run(); // 接口内部的属性默认都是终态属性,所以可以不用添加final前缀
public String TAG = "动物世界";
//public final String TAG = "动物世界"; // 这里的final可加可不加 // 接口不允许定义构造方法。在Java8以前,接口内部的所有方法都必须是抽象方法
}

接着定义一个鹅类,它不但继承自Bird鸟类,而且实现了新的行为接口Behavior。注意子类继承父类的格式为“extends 父类名”,实现某个接口的格式则为“implements 接口名”,同时该类还要重写接口里的所有抽象方法。于是实现了行为接口的鹅类代码如下所示:

//定义一个实现了接口Behavior的鹅类。注意鹅类需要实现Behavior接口的所有抽象方法
public class Goose extends Bird implements Behavior { public Goose(String name, int sexType) {
super(name, sexType);
} // 实现了接口的fly方法
@Override
public void fly() {
System.out.println("鹅飞不高,也飞不远。");
} // 实现了接口的swim方法
@Override
public void swim() {
System.out.println("鹅,鹅,鹅,曲项向天歌。白毛浮绿水,红掌拨清波。");
} // 实现了接口的run方法
@Override
public void run() {
System.out.println("槛外萧声轻荡漾,沙间鹅步满蹒跚。");
}
}

对于外部来说,这个鹅类跟一般的类没啥区别,鹅类所实现的接口方法,在外部看来都是鹅类的成员方法,原来怎么调用现在依然怎么调用。下面是外部使用鹅类的代码例子:

	// 演示简单接口的实现类用法
private static void testSimple() {
Goose goose = new Goose("家鹅", 0);
goose.fly(); // 实现了接口的fly方法
goose.swim(); // 实现了接口的swim方法
goose.run(); // 实现了接口的run方法
}

接口与类相比还有一个重大区别,在Java体系之中,每个类最多只能继承一个父类,不能同时继承多个类,也就是不允许多重继承。而接口不存在这方面的限制,某个类可以只实现一个接口,也可以同时实现两个接口、三个接口等等,待实现的接口名称之间以逗号分隔。例如除了飞翔、游泳、奔跑这三种动作之外,有些动物还擅长跳跃,比如青蛙、袋鼠等等,倘若在现有的Behavior接口中增加跳跃方法jump,那么包括Goose在内所有实现了Behavior的类都要重写jump方法,显然改造量巨大。现在借助接口的多重实现特性,完全可以另外定义新的行为接口Behavior2,在新接口中声明跳跃方法,那么只有实现Behavior2接口的类才需要重写jump方法。按此思路单独定义的新接口Behavior2代码见下:

//定义另一个行为接口
public interface Behavior2 { // 声明了一个抽象的跳跃方法
public void jump();
}

然后编写Frog蛙类的定义代码,这个蛙类同时实现了接口Behavior和Behavior2,这样它要重写Behavior的三个方法,以及Behavior2的跳跃方法。下面是一个蛙类代码的简单例子:

//定义一个实现了接口Behavior和Behavior2的蛙类。类只能继承一个,但接口可以实现多个
public class Frog implements Behavior, Behavior2 { // 实现了Behavior2接口的jump方法
@Override
public void jump() {
System.out.println("青蛙跳跃的技能叫做“蛙跳”");
} // 实现了Behavior接口的fly方法
@Override
public void fly() {
} // 实现了Behavior接口的swim方法
@Override
public void swim() {
System.out.println("青蛙游泳的技能叫做“蛙泳”");
} // 实现了Behavior接口的run方法
@Override
public void run() {
}
}

由于新增的jump方法属于新接口Behavior2,不属于原接口Behavior,因此实现了Behavior接口的鹅类代码无需任何修改,只有实现Behavior2的蛙类代码才需额外处理。当然这个特殊处理也仅限于蛙类的定义,对于外部而言,蛙类Frog仍是一个普通的类,外部调用它并没有什么两样,具体的调用代码示例如下:

	// 演示某个类同时实现了多个接口
private static void testMultiple() {
Frog frog = new Frog();
frog.swim(); // 实现了Behavior接口的swim方法
frog.jump(); // 实现了Behavior2接口的run方法
}

  

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(五十八)简单接口及其实现的更多相关文章

  1. Java开发笔记(十八)上下求索的while循环

    循环是流程控制的又一重要结构,“白天-黑夜-白天-黑夜”属于时间上的循环,古人“年复一年.日复一日”的“日出而作.日落而息”便是每天周而复始的生活.计算机程序处理循环结构时,给定一段每次都要执行的代码 ...

  2. Java开发笔记(九十八)利用Callable启动线程

    前面介绍了如何利用Runnable接口构建线程任务,该方式确实方便了线程代码的复用与共享,然而Runnable不像公共方法那样有返回值,也就无法将线程代码的处理结果传给外部,造成外部既不知晓该线程是否 ...

  3. Java学习笔记二十八:Java中的接口

    Java中的接口 一:Java的接口: 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承 ...

  4. Java开发学习(二十八)----拦截器(Interceptor)详细解析

    一.拦截器概念 讲解拦截器的概念之前,我们先看一张图: (1)浏览器发送一个请求会先到Tomcat的web服务器 (2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源 (3)如 ...

  5. Java开发笔记(十五)短路逻辑运算的优势

    前面提到逻辑运算只能操作布尔变量,这其实是不严谨的,因为经过Java编程实现,会发现“&”.“|”.“^”这几个逻辑符号竟然可以对数字进行运算.譬如下面的代码就直接对数字分别开展了“与”.“或 ...

  6. Java学习笔记(十八)——Java DTO

    [前面的话] 在和技术人员的交流中,各种专业术语会出现,每次都是默默的记录下出现的术语,然后再去网上查看是什么意思.最近做项目,需要使用到DTO,然后学习一下吧. 这篇文章是关于Java DTO的,选 ...

  7. 【Java学习笔记之十八】Javadoc注释的用法

    Javadoc注释的用法 Java 文档 // 注释一行/* ...... */ 注释若干行/** ...... */ 注释若干行,并写入 javadoc 文档 通常这种注释的多行写法如下: /*** ...

  8. 安卓开发笔记(十八):实现button按钮事件的三种方法

    Android开发中有三种主要的方式用于设置View的点击事件,1.创建内部类:2.主类中实现OnClickListener接口:3.使用匿名内部类.这三种方式都用到了OnClickListener接 ...

  9. Java开发笔记(十六)非此即彼的条件分支

    前面花了大量篇幅介绍布尔类型及相应的关系运算和逻辑运算,那可不仅仅是为了求真值或假值,更是为了通过布尔值控制流程的走向.在现实生活中,常常需要在岔路口抉择走去何方,往南还是往北,向东还是向西?在Jav ...

  10. Java开发笔记(十九)规律变化的for循环

    前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...

随机推荐

  1. Spring 复习第一天

    一.Sping的优点 1.方便解耦,简化开发 ----Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理. 2.AOP编程的支持 ----Spring提供面向切面编程, ...

  2. 【react】---17新增的生命周期

    一.废除的生命周期 官网文档指出使用这些生命周期的代码会在未来版本的react中更容易产生bug,尤其是对于异步渲染的版本 由于未来采用异步渲染机制,所以即将在17版本中去掉的生命周期钩子函数 com ...

  3. 动态添加echarts

    本次只贴js和jsp代码 jsp只需添加一个div即可, <div class="fLayout-right-box"> <hy:layoutArea width ...

  4. js 中 的 BOM对象

    BOM对象(浏览器对象模型 Browser Object Model) 01.页面的前进和后退 02.移动,调整和关闭浏览器窗口 03.创建新的浏览器窗口 01.window对象 ***** 核心对象 ...

  5. webpack 4.0 中 clean-webpack-plugin 的使用

    其实 clean-webpack-plugin 很容易知道它的作用,就是来清除文件的. 一般这个插件是配合 webpack -p 这条命令来使用,就是说在为生产环境编译文件的时候,先把 build或d ...

  6. Python爬取房产数据,在地图上展现!

    小伙伴,我又来了,这次我们写的是用python爬虫爬取乌鲁木齐的房产数据并展示在地图上,地图工具我用的是 BDP个人版-免费在线数据分析软件,数据可视化软件 ,这个可以导入csv或者excel数据. ...

  7. [Swift]LeetCode179. 最大数 | Largest Number

    Given a list of non negative integers, arrange them such that they form the largest number. Example ...

  8. [Swift]LeetCode372. 超级次方 | Super Pow

    Your task is to calculate ab mod 1337 where a is a positive integer and bis an extremely large posit ...

  9. [Swift]LeetCode756. 金字塔转换矩阵 | Pyramid Transition Matrix

    We are stacking blocks to form a pyramid. Each block has a color which is a one letter string, like ...

  10. [Swift]LeetCode942. 增减字符串匹配 | DI String Match

    Given a string S that only contains "I" (increase) or "D" (decrease), let N = S. ...