前言
在我们之前的文章里有说过,Java的类是单继承的,也就是说,一个类只能有一个“亲爹”。但是Java的类到底能不能实现多继承呢?间接实现多继承行不行?
其实这是可以的!
我们可以利用Java里的interface接口,来让Java的类间接地实现多继承,相当于是给一个类安排了“干爹”,而且还可以有多个“干爹”。
那么interface接口到底是咋回事?该怎么用?怎么实现多继承?本文将为您详细讲解
全文大约 【5000】字, 不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考
一. 接口简介
1. 简介
Java中的接口(interface)类似于是一种特殊的抽象类,它也是众多抽象方法的集合。 接口的定义方式、组成部分都与抽象类相似,却比普通的抽象类更为抽象和纯粹。不过我们只能说接口是类似于抽象类,却并非真的是类。接口与类是两种不同的概念,类描述的是对象的属性和方法,接口则主要是包含了类要实现的方法。
因为接口不是真的类,所以无法被实例化,但是却可以被实现。通常我们是使用interface关键字 来定义接口,使用implements关键字让类来实现一个或多个接口,从而间接成为该接口的一个子类,达到多继承的目的。一般情况下,当一个类实现了接口后,就要实现该接口中所有的方法,否则该类就必须声明为抽象类。
另外,在 Java 中,接口类型可以用来声明一个变量,该变量可以关联到实现了该接口的对象上,达到多态的目的。
2. 分类
一般情况下,我们所谓的Java接口,都是指上面的概念。但实际上,Java的接口其实可以有两种理解角度。
2.1 狭义接口
从狭义的角度来说,接口就是我们上面所说的接口,它代表着一种能力和约定。因为Java是单继承的,当父类中的方法无法满足子类的需求时,可以通过实现接口来扩充子类的能力。接口支持多实现,这就可以为类扩充多种能力。所以接口代表了某种能力,而接口中的方法则是对能力的具体要求。
2.2 广义接口
从广义的角度来说,接口是一种标准和规范。 为了更好地约束大家的行为,完成一致的目标,我们可以制定统一的要求和标准规范,这其实也是一种接口。
就好比在日常生活中,我们经常需要给各种电子设备充电或传输数据,这就需要充电线和充电插口。早期的时候,每个厂家生产的设备都有一个自己的插口,有的是圆的,有的是方的......你要是出个门,身上可能都得带一大堆的各种充电器充电线,否则大家彼此之间都不通用。这就严重降低了客户购买电子产品的欲望,给客户增加了无尽的麻烦。
怎么办?于是电子设备行业就组建了标准委员会,大家别单打独斗了,以后所有厂家生产的电子设备,都统一使用USB插口。这样即使不同的设备,彼此之间也可以做到互联互通,这就极大地减少了麻烦,增加了客户购买电子设备的欲望。这里的USB就是一种统一的标准和规范,这就是接口!生活中这样的例子举不胜数,我们在软件开发时同样也需要有这样的标准和规范。
3. 特性
根据上面所述的接口概念,壹哥给大家提取一下接口的特性:
● 接口中的每个方法都是隐式抽象的,这些方法默认会被隐式地指定为public abstract(只能是public abstract,在JDK 9之前,使用其他修饰符会报错);
● 接口中可以有变量,默认都会成为常量,因为接口中的变量会被隐式地指定为public static final变量(在JDK 9版本之前,只能是 public,用private修饰会报编译错误);
● JDK 8之前,接口中的方法默认是没有实现的,只能由实现该接口的类来实现接口中的方法。从JDK 8开始,接口中可以有默认的default实现方法;
● JDK 9开始,接口中允许将方法定义为private,使得某些复用的代码不会把方法暴露出去。
4. 接口与类的对比
很多初学者可能会搞不清接口和类的区别,所以面试时有面试官也会经常考察这一块,那么接下来我们再把接口与类的异同归纳总结一下。
4.1 接口与类的相似点
● 接口与类都可以定义多个方法;
● 接口与类的文件后缀都是.java,字节码文件都是.class;
● 接口与类的命名规范相同,都遵循驼峰法则,首字母都要大写。
4.2 接口与类的区别
● 接口没有构造方法;
● 接口不能实例化对象;
● 一般情况下,接口中所有的方法都是公开的抽象方法,不能有默认实现;
● JDK 8开始接口中可以有默认实现,JDK 9开始接口的方法可以有private方法;
● 接口中的变量默认是public static final变量,不能包含其他的成员变量;
● 接口与类的关系不是继承,而是实现;
● 接口支持间接地进行多继承。
4.3 接口与抽象类区别
● 抽象类可以有抽象和非抽象的方法,非抽象方法可以有具体的实现,接口中的方法一般都没有实现。
● 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
● 接口中不能含有构造方法、静态代码块及静态方法,而抽象类中可以有构造方法、静态代码块和静态方法;
● 一个类只能继承一个抽象类,但一个类却可以实现多个接口。
其实有些同学会搞不清楚,既然抽象类与接口很相似,那到底什么时候该用接口,什么时候该用抽象类呢?
我们要知道,抽象方法本质上是一种定义接口的规范,它规定了高层类的接口,保证了所有子类都有相同的接口实现,从而让多态就发挥出威力。所以,如果一个抽象类没有任何字段,且所有的方法都是抽象方法,我们就可以把该抽象类改写为接口了!
5. 优点
根据接口的概念和特性可知,接口具有如下优点:
● 接口可以使得程序的耦合性降低;
● 接口能够更自然地使用多态;
● 接口的设计与实现完全分离;
● 利用接口可以更容易搭建出程序框架。
二. 接口的定义与实现
1. 接口定义
1.1 基本语法
我们使用interface定义接口时,基本的语法格式如下:
访问修饰符 interface 接口名称 [extends] [接口名1, 接口名2,...] {
// 声明变量
// 抽象方法
// 默认实现
}
复制代码
我们要注意,因为接口默认就是隐式抽象的,所以声明接口时不必使用abstract关键字。而接口中每个方法和变量默认也都是隐式抽象且公开的,声明时同样不需要abstract和public关键字。另外一个接口还可以利用extends关键字来继承另外的几个接口。但是我们一定要注意,接口继承时不是单继承的,即一个接口可以继承多个其他的接口!!!当继承多个接口时,接口之间用逗号分割。 如下图所示:
1.2 实现案例
public interface Flyable {
//接口中的方法,默认都是公开的抽象方法,不用加public和abstract关键字,也不用有方法体
void fly();
//JDK 8开始,接口中允许有默认的方法实现,该方法可以带public,也可以不带,但是不能是private修饰的
public default void flyInDream() {
System.out.println("在梦中,我可以飞");
};
//JDK 9开始,接口中允许有private修饰的私有方法,但是该方法必须有实现的方法体
private void flyWithMe() {
System.out.println("跟我一起飞");
};
}
复制代码
由上述代码可以验证出,接口中的方法,默认都是公开的抽象方法,不用加public和abstract关键字,也不用有方法体。
从JDK 8开始,接口中允许有默认的方法实现,该方法可以带public修饰符,也可以不带,但是不能是private修饰的。
从JDK 9开始,接口中允许有private修饰的私有方法,但是该方法必须有实现的方法体。当然,一般情况下,接口中的默认实现方法和私有方法都不是必须的,公开的抽象方法才是必须的。
2. 接口实现
一个接口定义出来之后,我们就可以来实现该接口了,此时要用implements关键字。
/**
* 人类可以继承动物类,同时还可以通过实现若干个接口来扩展自己的能力。
* 实现接口必须接口里的抽象方法。
*/
public class Person implements Flyable{
@Override
public void fly() {
System.out.println("人可以坐飞机飞行");
}
}
复制代码
一个类可以同时实现多个接口,当实现多个接口时,接口名称之间要用逗号分割。
3. 接口继承
在Java中,接口与接口之间是可以继承的,也就是说,一个接口可以继承另一个接口。和类的继承方式相似,接口的继承也是使用extends关键字,并且子接口可以继承父接口里的方法。但是与类的继承不同的是,接口可以多继承。
3.1 定义Skill接口
/**
* 技能接口
*/
public interface Skill {
//通用、常规技能
void commonSkill();
}
复制代码
3.2 继承接口
接口是允许多继承的,即一个接口可以同时继承另外的N个接口,接口名之间用逗号分割。
public interface Swingable extends Skill{
//游泳
void swing();
}
复制代码
3.3 实现多个接口
假如我们定义了一个A接口,A接口中有2个方法;然后又定义了一个B接口,B接口中有3个方法;接着让A接口继承B接口,此时,当C类实现A接口的时候,需要实现5个方法,因为C类需要把继承树中的所有抽象方法都实现了。我们来看下面的案例:
/**
* 一个类可以实现多个类
*/
public class Student implements Flyable,Swingable{
//该方法来自于Skill接口,Swingable接口继承了Skill接口
@Override
public void commonSkill() {
System.out.println("学会了常规技能操作");
}
//该方法来自于Swingable接口
@Override
public void swing() {
System.out.println("学会了游泳技能操作");
}
//该方法来自于Flyable接口
@Override
public void fly() {
System.out.println("学会了飞行技能操作");
}
}
复制代码
但是我们要注意,当我们要重写接口中声明的方法时,需要注意以下规则:
● 一个类在实现接口里的方法时,不能抛出强制性的异常,只能在接口或者继承接口的抽象类中抛出该强制性异常;
● 一个类在方法重写时要保持一致的方法名,且两者之间应该保持相同或者相兼容的返回值类型;
● 如果一个抽象类实现接口,该类没必要实现此接口里的方法。
三. 标记接口
1. 概念
除了上面这种常规的普通接口之外,在Java中其实还有一种比较“有个性”的接口,这就是“标记接口”。所谓的标记接口,就是指没有任何方法和属性的接口。该接口主要是用于表明所有实现了该接口的类都属于一个特定的类型,可以供其他代码来测试允许执行一些操作。凡是实现了标记接口的类,就相当于大家的身上都盖了个戳子,打了个标记,表示大家都是“一类人”,都是“一路货色”。
Java中最常见的标记接口就是Serializable序列化接口,如下图所示:
Serializable接口可以使得所有实现了该接口的类,拥有被序列化的能力:
Serializability of a class is enabled by the class implementing the java.io.Serializable interface.
2. 作用
标记接口的存在,主要是有两大作用:
● 创建公共的父接口:假如我们的项目很复杂,有多个甚至几十个类或接口其实都是同一类型,我们就可以使用一个标记接口来作为一组接口的父接口,就是给大家找个共同的“爹”。
● 通过多态给类赋予数据类型:虽然实现标记接口的类不需要实现任何接口方法,但我们可以将该类利用多态性变成一个接口类型。
四. 接口新特性
1. 新特性
之前就跟大家说过,Java是一个不断更新迭代的语言,现在每隔半年就会对JDK进行一次大的版本迭代,而每次迭代后都会出现一些新特性。当然更新时,并不是把所有的技术都更新,只是对原有的不那么高效或安全的技术进行升级。比如在不同的JDK版本中,对接口就有不同的升级:
● JDK 8以后,接口里可以有静态方法和方法体;
● JDK 8以后,接口中允许包含带有具体实现的方法,该方法称为default"默认方法"。默认方法使用 default 关键字修饰。
● JDK 9以后,接口中允许将方法定义为private,使得某些复用的代码不会把方法暴露出去。
2. 案例代码
public interface Flyable {
//接口中的方法,默认都是公开的抽象方法,不用加public和abstract关键字,也不用有方法体
void fly();
//JDK 8开始,接口中允许有默认的方法实现,该方法可以带public,也可以不带,但是不能是private修饰的
public default void flyInDream() {
System.out.println("在梦中,我可以飞");
};
//JDK 9开始,接口中允许有private修饰的私有方法,但是该方法必须有实现的方法体
private void flyWithMe() {
System.out.println("跟我一起飞");
};
}
复制代码
大家要注意,对于默认的default方法,它存在的目的是,当我们需要给接口新增一个方法时,所有的子类都需要跟着修改实现。但如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去重写新增的方法即可,因为实现类可以不必重写default方法。
四. 结语
至此就把接口的基本使用与特性给大家讲解完毕了,最后我们再来总结一下本文的重点内容吧:
标签:Java,JDK,实现,接口,继承,详解,interface,方法,public From: https://blog.51cto.com/u_15739596/6180630● 接口分为狭义和广义的接口;
● 接口也是一种数据类型,适用于向上转型和向下转型;
● 接口中所有的方法都是抽象方法,接口中不能定义实例字段;
● 要明确接口与类、抽象类的区别;
● 一个类只能继承一个类,但能实现多个接口;
● 一个接口能继承另外多个接口;
● 一般情况下,接口中的每个方法都是隐式抽象的;
● 接口中可以有变量,默认都会成为常量;
● JDK 8之前,接口中的方法是没有默认实现的;
● JDK 9开始,接口中允许将方法定义为private;