阿里云【名师课堂】Java面向对象开发68 ~ 70、73:接口的定义和使用
@
通过《阿里云【名师课堂】Java面向对象开发65 ~ 67:抽象类的定义和使用》的学习我们知道,抽象类虽然可以对子类的实现进行了约束,但是抽象类有一个缺点:单继承局限。
如果要在约束子类的实现要求的同时避免单继承局限,就要用到这节课所学的接口。在一个操作既可以使用抽象类又可以使用接口时,接口优先。
68:接口基本概念
1、定义
接口是一组常量和抽象方法的集合,在Java中国你接口可以使用interface
关键字来定义。
- 作为对比:抽象类:抽象方法、全局常量、普通常量、构造方法、普通方法。
范例:定义一个接口
interface IMessage { // 接口标识头加个I,便于区分
public static final String MSG = "这是一个全局常量MSG" ; // static全局,final常量
public abstract void print() ; // 一个抽象方法,没有方法体
}
如果子类想要使用接口,就必须用到关键字implements
来实现接口。
同时一个子类可以实现多个接口,也就是说可以利用接口来实现多继承的概念。
- 对于接口的子类(如果不是抽象子类)则必须覆写接口中的全部抽象方法。
- 随后可以利用子类对象的向上转型,通过实例化子类来得到接口的实例化对象。
范例:接口实现多继承
interface IMessage { // 接口标识头加个I,便于区分
public static final String MSG = "这是一个全局常量MSG" ; // static全局,final常量
public abstract void print() ; // 一个抽象方法,没有方法体
}
interface INews {
public abstract String get() ;
}
// 一个子类可以同时实现多个接口
class MessageImp implements IMessage,INews {
public void print() {
System.out.println(IMessage.MSG) ;
}
public String get() {
return IMessage.MSG ; // 访问常量建议加上类名称
}
}
public class TestDemo {
public static void main(String args[]) {
IMessage msg = new MessageImp() ; // 子类为父接口实例化
msg.print() ; // 调用被子类覆写过的print()方法
}
}
既然子类继承了两个接口,那么当然也可以:
public class TestDemo {
public static void main(String args[]) {
INews news = new MessageImp() ; // 子类为父接口实例化
System.out.println(news.get()) ; // 调用被子类覆写过的get()方法
}
}
2、观察接口间的转换
回顾:《阿里云【名师课堂】Java面向对象开发64:多态性》中的对象多态性概念。
······
public class TestDemo {
public static void main(String args[]) {
INews n = new MessageImp() ; // 子类为父接口实例化
System.out.println("【INews】" + n.get()) ; // 调用被子类覆写的INews中的get()方法
// 重点在关键字new,n被实例化为MessageImp的对象
// 并且MessageImp是IMessage的子类
IMessage msg = (IMessage)n ; // 转型为IMessage的对象
msg.print() ; // 调用被覆写的IMessage中的方法
}
}
分析:
当一个子类继承了多个接口之后,并且接口对象通过子类进行实例化,那么这个对象在多个父接口之间是允许互相转换的。
69:接口使用限制
1、权限限制
首先需要说明:接口中只允许存在public
权限。也就是说,不管是属性还是方法,其权限都是public
。
- 所以类在覆写接口方法时不仅要去掉
abstract
修饰,给出方法体,而且方法的访问权限一定要用public
来修饰(否则就降低了权限)。 - 接口中方法只有抽象方法,则
abstract
可以省略,因为没有abstract
它也是抽象方法。
2、实现顺序
当一个子类既需要实现接口又需要继承抽象类的时候,请先使用extends
继承抽象类,而后再使用implements
实现接口。
范例:让子类继承抽象类和实现接口
interface IMessage { // 接口标识头加个I,便于区分
public String get() ; // 一个抽象方法,没有方法体,在接口中省略了abstract
}
abstract class AbstractMessage { // 抽象类标识头加个Abstract,便于辨识
public abstract void print() ; // 一个抽象方法,没有方法体
}
class MessageImpl extends AbstractMessage implements IMessage { // 接口的子类标识末尾加上Impl
public String get() {
return "覆写接口的方法" ;
}
public void print() { // 去abstract、加方法体
System.out.println("覆写抽象类方法") ;
}
}
public class TestDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
System.out.println(msg.get()) ;
// MessageImpl是抽象类和接口的共同子类,可以转型
AbstractMessage amsg = (AbstractMessage)msg ;
amsg.print() ;
}
}
3、抽象类与接口间的实现
3.1、三重继承
一个抽象类可以使用implements
实现多个接口,但是接口不能够去继承抽象类。
interface IMessage {······}
abstract class AbstractMessage implements IMessage { // 抽象类实现接口
public abstract void print() ; // 本身就是抽象类,无需再覆写接口中的抽象方法
}
class MessageImpl extends AbstractMessage { // 通过继承实现接口的抽象类,子类也实现了接口
public String get() {
return "覆写接口的方法" ;
}
public void print() { // 去abstract、加方法体
System.out.println("覆写抽象类方法") ;
}
}
public class TestDemo {······}
可以看到与上一个程序达到完全相同的效果。
实际上,这段结构被称为三重继承。
3.2、假实现
范例:观察“假实现”
interface IMessage {
public String get() ;
public void print() ;
}
abstract class AbstractMessage implements IMessage {
public void print() { // 重载IMessage中的print抽象方法
System.out.println("假实现,抽象类覆写接口的print方法") ;
}
}
/*
class MessageImpl extends AbstractMessage implements IMessage {}
这时如果按这句写,起到的作用是强调MessageImpl是IMEssafe的子类
但是这只是一个重复标记,功能与直接写 class MessageImpl extends AbstractMessage {}完全一致
*/
class MessageImpl extends AbstractMessage {
public String get() {
return "子类覆写接口的get方法" ;
}
}
public class TestDemo {······}
可以看到,print方法被抽象类实现了,get方法被子类实现了。
下面进行正常三重继承
与假实现
流程分析:
这种加重结构描述的方法用于比较多的抽象类与方法的实现时,方便使用者捋清关系、看懂程序。
4、接口的继承
虽然接口不可以继承抽象类,但是一个接口可以使用extends
关键字来继承多个父接口。
interface A {
public void printA() ;
}
interface B {
public void printB() ;
}
interface C extends A,B {
public void printC() ;
}
class Impl implements C {
public void printA() {}
public void printB() {}
public void printC() {}
}
public class TestDemo {
public static void main(String args[]) {
}
}
5、接口定义内部结构
接口可以定义一系列的内部结构,包括:内部普通类、内部抽象类、内部接口。其中,使用static定义的内部就扣就相当于外部接口。
- 作为对比:抽象类也分内部抽象类和外部抽象类,内部抽象类中可以使用static进行定义,描述为外部抽象类。
interface A {
static interface B { // 使用了static定义,描述一个外部接口
public void printB() ;
}
}
class X implements A.B { // 实现内部接口
public void printB() {}; // 覆写接口中的抽象方法
}
public class TestDemo {
public static void main(String args[]) {
}
}
与抽象类相同,这种内部接口的方法仍然不是编程时的首选。
70:接口的应用:定义标准
对于接口在实际的开发之中有三大核心应用环境:
- 定义操作标准;
- 表示能力;
- 在分布式开发之中暴露远程服务方法。
现在描述一个概念:要求电脑上可以使用任何的USB设备。
1、定义USB标准(by接口)
interface USB { // 忘了写IUSB,doesn't matter
public void setUp() ; // 定义:需要先安装驱动
public void operation() ; // 定义:正常工作
}
2、定义电脑类
class Computer { // 电脑只负责提供接口,不关心是哪些设备插入
public void plugin(USB usb) { // 只能插入USB设备
usb.setUp () ; // 所有设备都要安装驱动
usb.operation () ; // 所有设备都要正常工作
}
}
3、定义USB子类
class USBFlashDisk implements USB {
public void setUp() {
System.out.println("成功安装U盘驱动!") ;
}
public void operation() {
System.out.println("进行数据传输!") ;
}
}
class Printer implements USB {
public void setUp() {
System.out.println("成功安装打印机驱动!") ;
}
public void operation() {
System.out.println("进行文档打印!") ;
}
}
4、定义测试类
public class TestDemo {
public static void main(String args[]) {
Computer pc = new Computer() ;
/* USBFlashDisk udisk = new USBFlashDisk() ;
Printer pri = new Printer() ;
pc.plugin(udisk) ;
pc.plugin(pri) ; */
pc.plugin(new USBFlashDisk()) ;
pc.plugin(new Printer()) ;
}
}
把上述代码结合,成果满足要求:
5、总结
通过这段程序,我们可以发现使用接口和对象多态性的概念结合之后,对于参数的统一更加明确了。
而且可以发现,接口是在类之上的设计抽象。
73:抽象类与接口的区别
1、区别与联系
区别 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract class 类名称 {} |
interface 接口名称 {} |
结构组成 | 抽象方法、普通方法、全局变量、全局常量、属性、构造方法 | 抽象方法、全局常量 |
权限 | 各种权限(理论上) | 只有public访问权限 |
子类定义 | extends 继承抽象类 |
implements 实现接口 |
关系 | 一个抽象类可以实现若干个接口 | 一个接口不能继承抽象类,但是可以使用extends 继承多个父接口 |
子类限制 | 一个子类只能继承一个抽象类(单继承) | 一个子类可以实现多个接口(多继承) |
联系:抽象类和接口都可以有抽象方法,都不能被实例化,都是引用数据类型。 |
2、选择
接口与抽象类的选择:
- 当抽象类和接口都可以使用时,优先选择接口,因为其更为简洁、方便;
- 如果某个问题需要使用继承才可以解决,比如:子类除了需要实现父类的抽象方法之外,还需要从父类继承一些变量或一些重要的普通方法,就要用抽象类。
- 如果某个问题不需要继承,只是需要若干个类给出某些重要的抽象类方法的实现细节,就可以使用接口。
3、结构体的设计关系
我们已经学习的结构体:类、对象、抽象类、接口之间的关系可以用一张图描述: