3.9、接口(重点)
3.9.1 、接口的基本概念
接口属于一种特殊的类,如果一个类定义的时候全部由抽象方法和全局常量所组成的话,那么这种类就称为接口,但是接口是使用interface关键字进行定义的。
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } |
那么在接口之中,也同样存在了抽象方法,很明显,接口对象无法直接进行对象的实例化操作,那么接口的使用原则如下:
· 每一个接口必须定义子类,子类使用implements关键字实现接口;
· 接口的子类(如果不是抽象类)则必须覆写接口之中所定义的全部抽象方法;
· 利用接口的子类,采用对象的向上转型方式,进行接口对象的实例化操作。
下面给出子类实现接口的语法格式:
class 子类 [extends 父类] [implemetns 接口1,接口2,...] {} |
通过格式可以发现,每一个子类可以同时实现多个接口,但是只能继承一个父类。
范例:让子类实现接口
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } class X implements A,B { // 同时实现了两个接口 public void print() { // 方法覆写 System.out.println("Hello World .") ; } public void get() { System.out.println(INFO) ; } } public class Test { public static void main(String args[]) { A a = new X() ; B b = new X() ; a.print() ; b.get() ; } } |
那么如果一个类现在即要实现接口又要继承抽象类的话,则应该采用先继承后实现的方式完成。
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } abstract class C { public abstract void fun() ; } class X extends C implements A,B { // 同时实现了两个接口 public void print() { // 方法覆写 System.out.println("Hello World .") ; } public void get() { System.out.println(INFO) ; } public void fun() { System.out.println("世界,你好!") ; } } public class Test { public static void main(String args[]) { A a = new X() ; B b = new X() ; C c = new X() ; a.print() ; b.get() ; c.fun() ; } } |
但是需要说明的是:接口之中的全部组成就是抽象方法和全局常量,那么在开发之中以下的两种定义接口的最终效果是完全一样的:
完整定义: |
简化定义: |
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } |
interface A { // 定义接口 public String INFO = "Hello World ." ; public void print() ; } |
接口之中的访问权限只有一种:public,即:定义接口方法的时候就算没有写上public,那么最终也是public。
在Java之中每一个抽象类都可以实现多个接口,但是反过来讲,一个接口却不能继承抽象类,可是Java之中,一个接口却可以同时继承多个接口,以实现接口的多继承操作。
interface A { public void printA() ; } interface B { public void printB() ; } interface C extends A,B { // 一个接口继承了多个接口 public void printC() ; } class X implements C { public void printA() {} public void printB() {} public void printC() {} } |
而在开发之中,内部类是永远不会受到概念限制的,在一个类中可以定义内部类,在一个抽象类之中也可以定义抽象内部类,在一个接口里面也可以定义内部抽象类或内部接口,但是从实际的开发来讲,用户自己去定义内部抽象类或内部接口的时候是比较少见的(Android开发中见过内部接口),而且在定义内部接口的时候如果使用了static,表示是一个外部接口。
interface A { public void printA() ; static interface B { // 外部接口 public void printB() ; } } class X implements A.B { public void printB() { System.out.println("Hello World .") ; } } public class Test { public static void main(String args[]) { A.B temp = new X() ; temp.printB() ; } } |
以上对于接口的概念并不是很难理解,但是需要强调的是,在实际的开发之中,接口有三大主要功能:
·制订操作标准;
·表示一种能力;
·将服务器端的远程方法视图暴露给客户端。
3.9.2 、使用接口定义标准
在日常的生活之中,接口这一名词经常听到的,例如:USB接口、打印接口、充电接口等等。
现在假设每一个USB设备只有两个功能:安装驱动程序、工作。
范例:定义出一个USB的标准
interface USB { // 操作标准 public void install() ; public void work() ; } |
范例:在电脑上应用此接口
class Computer { public void plugin(USB usb) { usb.install() ; usb.work() ; } } |
范例:定义USB设备
class Phone implements USB { public void install() { System.out.println("安装手机驱动程序。") ; } public void work() { System.out.println("手机与电脑进行工作。") ; } } |
范例:定义USB设备
class Print implements USB { public void install() { System.out.println("安装打印机驱动程序。") ; } public void work() { System.out.println("进行文件打印。") ; } } |
范例:连接
interface USB { // 操作标准 public void install() ; public void work() ; } class Computer { public void plugin(USB usb) { usb.install() ; usb.work() ; } } class Phone implements USB { public void install() { System.out.println("安装手机驱动程序。") ; } public void work() { System.out.println("手机与电脑进行工作。") ; } } class Print implements USB { public void install() { System.out.println("安装打印机驱动程序。") ; } public void work() { System.out.println("进行文件打印。") ; } } public class Test { public static void main(String args[]) { Computer c = new Computer() ; c.plugin(new Phone()) ; // USB usb = new Phone() ; c.plugin(new Print()) ; } } |
按照这种方式开发下去的话,不管有多少个USB接口的子类,都可以在电脑上使用。
3.9.3 、接口的实际作用 —— 工厂设计模式(Factory)
下面首先来观察如下的程序代码。
interface Fruit { public void eat() ; } class Apple implements Fruit { public void eat() { System.out.println("吃苹果。") ; } } class Orange implements Fruit { public void eat() { System.out.println("吃橘子。") ; } } public class Test { public static void main(String args[]) { Fruit f = new Apple() ; f.eat() ; } } |
本程序非常简单就是通过接口的子类为接口对象实例化,但是本操作存在什么样的问题呢?
之前一直在强调,主方法或者是主类是一个客户端,客户端的操作应该越简单越好。但是现在的程序之中,有一个最大的问题:客户端之中,一个接口和一个固定的子类绑在一起了。
在本程序之中,最大的问题在于耦合上,发现在主方法之中,一个接口和一个子类紧密耦合在一起,这种方式比较直接,可以简单的理解为由:A è B,但是这种紧密的方式不方便于维护,所以后来使用了A è C è B,中间经历了一个过渡,这样一来B去改变,C去改变,但是A不需要改变,就好比JAVA的JVM一样:程序 è JVM è 操作系统。
范例:修改代码
interface Fruit { public void eat() ; } class Apple implements Fruit { public void eat() { System.out.println("吃苹果。") ; } } class Orange implements Fruit { public void eat() { System.out.println("吃橘子。") ; } } class Factory { public static Fruit getInstance(String className) { if ("apple".equals(className)) { return new Apple() ; } if ("orange".equals(className)) { return new Orange () ; } return null ; } } public class Test { public static void main(String args[]) { Fruit f = Factory.getInstance(args[0]) ; f.eat() ; } } |
这个时候发现客户端不再和一个具体的子类耦合在一起了,就算以后增加了新的子类,那么只需要修改Factory类即可实现。
3.9.4 、接口的实际作用 —— 代理设计模式(Proxy)
张金宇的不幸人生。。。555555。
interface Subject { // 操作主题 public void get() ; // 要银子 } class RealSubject implements Subject { // 真正的要银子 public void get() { System.out.println("真实业务主题") ; } } class ProxySubject implements Subject { private Subject sub = null ; public ProxySubject(Subject sub) { this.sub = sub ; } public void prepare() { System.out.println("准备操作。") ; } public void destroy() { System.out.println("收尾操作。") ; } public void get() { this.prepare() ; this.sub.get() ; this.destroy() ; } } public class Test { public static void main(String args[]) { Subject sub = new ProxySubject(new RealSubject()) ; sub.get() ; } } |
通过以上的分析就可以得出结论:代理负责完成与真实业务有关的所有辅助性操作。
3.9.5 、抽象类和接口的区别(面试题)
通过如上的分析,感觉抽象类和接口在使用上似乎区别不大,那么下面就通过一个表格给出这两者的区别。
(面试题:请解释抽象类和接口的区别?)
No. |
区别 |
抽象类 |
接口 |
1 |
定义关键字 |
abstract class |
interface |
2 |
组成 |
常量、变量、抽象方法、普通方法、构造方法 |
全局常量、抽象方法 |
3 |
权限 |
可以使用各种权限 |
只能是public |
4 |
关系 |
一个抽象类可以实现多个接口 |
接口不能够继承抽象类,却可以继承多接口 |
5 |
使用 |
子类使用extends继承抽象类 |
子类使用implements实现接口 |
抽象类和接口的对象都是利用对象多态性的向上转型,进行接口或抽象类的实例化操作 |
|||
6 |
设计模式 |
模板设计模式 |
工厂设计模式、代理设计模式 |
7 |
局限 |
一个子类只能够继承一个抽象类 |
一个子类可以实现多个接口 |
通过上面的分析可以得出结论:在开发之中,抽象类和接口实际上都是可以使用的,并且使用那一个都没有明确的限制,可是抽象类有一个最大的缺点 —— 一个子类只能够继承一个抽象类,存在单继承的局限,所以当遇到抽象类和接口都可以使用的情况下,优先考虑接口,避免单继承局限。
到此时已经学习过了:对象、类、抽象类、接口、继承、实现等等,这些都属于什么样的关系呢?
接口就是在类的基础之上的进一步具体的抽象。
4、总结
1、 继承性用于扩充类的功能;
2、 方法的覆写与对象多态性的联系;
3、 final关键字的使用;
4、 单例设计模式;
5、 对象的多态性,转型问题;
6、 抽象类和接口的概念;
7、 今天的三个设计模式(背下结构):单例、工厂、代理。
5、预习任务
匿名内部类、Object类、异常的捕获及处理、包及访问控制权限。
6、作业
No. |
表达式 |
描述 |
1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
No. |
方法名称 |
类型 |
描述 |
1 |
|
|
|
2 |
|
|
|
3 |
|
|
|
4 |
|
|
|
5 |
|
|
|
6 |
|
|
|
7 |
|
|
|
8 |
|
|
|
9 |
|
|