阿里云【名师课堂】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、结构体的设计关系

我们已经学习的结构体:类、对象、抽象类、接口之间的关系可以用一张图描述:
在这里插入图片描述

posted @ 2020-06-19 11:18  溺水的情书  阅读(335)  评论(0编辑  收藏  举报