接口与抽象类相比,接口的使用频率是最高的,所有的设计都是围绕着接口进行的。
接口的基本概念
接口是一种特殊的类,但是在接口里面的组成与类不同,比类的组成部分简单,主要有抽象方法和全局常量所组成。而接口使用interface关键字来定义。
范例:定义一个接口
interface A{//定义一个接口
public static final String MSG = "Hello World.";
public abstract void print();
}
当一个接口定义完成之后,需要遵循如下的步骤进行接口的使用:
- 接口一定要定义子类,子类利用implement关键字来实现接口,一个子类可以同时实现多个接口;
- 接口的子类(如果不是抽象类) 必须覆写接口中的全部抽象方法;
- 接口的对象利用子类对象的向上转型进行实例化操作。
范例:使用接口
interface A{//定义一个接口
public static final String MSG = "Hello World.";
public abstract void print();
}
interface B{
public abstract void fun();
}
class X implements A,B{//此时的X子类同时实现了A和B两个父接口
public void print(){
System.out.println("你好,嗨皮吗?");
}
public void fun(){
System.out.println(MSG);
}
}
public class Hello{
public static void main(String args[]){
X x = new X();//实例化子类对象
A a=x;//子类为父接口实例化
B b=x;//子类为父接口实例化
a.print();
b.fun();
}
}
范例:神奇操作
public class Hello{
public static void main(String args[]){
A a = new X();//X子类为父接口A实例化
B b = (B) a;
b.fun();
}
}
B和A没有关系,可以转换的原因是因为X是两者的子类。
注意:关于接口的组成描述
接口里面在定义的时候就已经明确的给出了开发要求:抽象方法和全局常量。所以以下两种接口的定义从本质上讲是完全一样的。
如果在定义接口方法的时候没有使用public,那么本质上也不是default权限,而默认就是public。
一般情况下,建议定义接口时写上public,但是可以不写abstract。
程序中出现类、抽象类、接口,这几者之间的关系需要注意。
一个普通类如果要实现接口又要继承抽象类,则一定采用先extends继承抽象类,再implements实现接口。格式:
class 子类 extends 抽象类 implements 接口1,接口2,…{}
范例:观察子类多继承问题。
interface A{//定义一个接口
public static final String MSG = "Hello World.";
public void print();
}
abstract class B{
public abstract void fun();
}
class X extends B implements A{//此时的X子类同时实现了A和B两个父接口
public void print(){
System.out.println("你好,嗨皮吗?");
}
public void fun(){
System.out.println(MSG);
}
}
另外除了以上的结构之外,一个抽象类还可以直接实现接口。
范例:抽象类实现接口
interface A{//定义一个接口
public static final String MSG = "Hello World.";
public void print();
}
abstract class B implements A{//这个时候抽象类有两个抽象方法
public abstract void fun();
}
class X extends B{//此时的X子类同时实现了A和B两个父接口
public void print(){
System.out.println("你好,嗨皮吗?");
}
public void fun(){
System.out.println(MSG);
}
}
抽象类可以实现接口,接口不能够继承抽象类,但一个接口可以使用extends关键字继承多个父接口。
范例:接口多继承
interface A{//定义一个接口
public void printA();
}
interface B{
public void printB();
}
interface C extends A,B{//C是A,B的子接口
public void printC();
}
class X implements C{
public void printA(){}
public void printB(){}
public void printC(){}
}
虽然接口本身只能够有抽象方法和全局常量,但是内部的结构是不到限制的,也就是说一个接口内部可以继续定义内部类、内部抽象类、内部接口。如果一个内部接口上使用了static定义,那么这个内部接口就属于外部接口。
范例:使用static定义内部接口
interface A{
static interface B{
public void print();
}
}
class X implements A.B{
public void print(){}
}
总之对于接口的使用可以发现有如下几点:
- 接口避免了单继承局限,一个子类可以实现多个接口;
- 接口中的权限统一为public,方法都是抽象方法,90%的情况下接口中很少定义全局常量;
- 所有的内部类结构都不受到定义语法的限制,static定义的内部接口就是一个外部接口。
接口的实际应用——标准
范例:首先要定义出的就是接口
interface USB{
public void start();
public void stop();
}
不管什么样的USB设备只要一连接到电脑上,那么就需要默认执行固定的操作。
范例:电脑上提供有支持USB的插入点
class Computer{
public void plugin(USB usb){
usb.start();
usb.stop();
}
}
不管有多少设备,电脑的plugin()方法里面只要接收的是USB接口实例,那么操作的步骤就是固定的。
范例:定义USB子类
class Flash implements USB{
public void start(){
System.out.println("开始使用U盘进行操作");
}
public void stop(){
System.out.println("U盘停止工作");
}
}
范例:定义键盘
class Keyboard implements USB{
public void start(){
System.out.println("开始使用键盘输入信息");
}
public void stop(){
System.out.println("键盘停止工作");
}
}
现在的子类是按照严格的操作标准使用着。
范例:程序调用处
public class Hello{
public static void main(String args[]){
Computer c = new Computer();
c.plugin(new Flash());//传递U盘对象
c.plugin(new Keyboard());//传递键盘对象
}
}
此时有了接口标准,即便有几千万个子类,也是可以在一个接口上使用的,所以接口是定义标准。即,接口可以连接两个不同的层。
接口的应用——工厂设计模式(Factory,背)
为什么有工厂模式?
范例:观察代码
interface Fruit{//水果
public void eat();//吃
}
class Apple implements Fruit{
public void eat(){
System.out.println("吃苹果。");
}
}
public class Hello{
public static void main(String args[]){
Fruit f = new Apple();
f.eat();
}
}
没有语法错误,但是有设计缺陷。如果现在fruit增加了一个子类,而且主类想使用这个子类。
interface Fruit{//水果
public void eat();//吃
}
class Apple implements Fruit{
public void eat(){
System.out.println("吃苹果。");
}
}
class Cherry implements Fruit{
public void eat(){
System.out.println("吃樱桃。");
}
}
public class Hello{
public static void main(String args[]){
Fruit f = new Apple();
f.eat();
}
}
此时发现,如果要扩充程序却影响了客户端的执行,这样的设计非常的不合理,解决该问题可参考Java可移植性的实现原理。
- 不可移植性:程序 → 操作系统;
- 可移植性:程序 → JVM → 操作系统。
范例:可以在客户端与接口之间引入一个中间层。
面试题:请设计一个Factory程序
interface Fruit{//水果
public void eat();//吃
}
class Apple implements Fruit{
public void eat(){
System.out.println("吃苹果。");
}
}
class Cherry implements Fruit{
public void eat(){
System.out.println("吃樱桃。");
}
}
class Factory{
public static Fruit getInstance(String className){//直接取得接口实例
if("apple".equals(className)){
return new Apple();
}else if("cherry".equals(className)){
return new Cherry();
}else{
return null;
}
}
}
public class Hello{
public static void main(String args[]){
Fruit f = Factory.getInstance(args[0]);
if(f!=null){
f.eat();
}
}
}
接口的应用——代理设计模式(Proxy,背)
代理结构是一种在接口上的 应用,一个接口有一个核心的操作功能,只依靠核心的操作主题是无法完成所需要功能的,那么需要一个代理主题。代理主题完成所有的与核心主题有关的概念。
范例:还原代码
interface Subject{//核心操作主题
public void get();//核心操作
}
class RealSubject implements Subject{
public void get(){
System.out.println("陶大爷取回了被强行霸占的1.5亿。");
}
}
class ProxySubject implements Subject{
private Subject subject;//代理的真是主题
public ProxySubject(Subject subject){
this.subject = subject;
}
public void prepare(){
System.out.println("【追逃前的准备】");
}
public void get(){
this.prepare();
this.subject.get();//真实主题的讨债
this.destory();
}
public void destory(){
System.out.println("【追逃后的收尾】");
}
}
public class Hello{
public static void main(String args[]){
Subject sub = new ProxySubject(new RealSubject());
sub.get();
}
}
抽象类与接口的区别(面试题)
抽象类、类、对象和接口之间的关系
通过以上的几点比较可以发现,抽象类与接口实际上都可以限制子类必须要覆写的方法要求,但是由于抽象类本身存在有单继承局限,所以在日后开发过程之中,如果发现抽象类与接口都可以同时使用的时候,优先考虑接口,而抽象类在实际应用中,往往是作为接口与普通类之间的过渡使用的。
总结
- 接口利用interface关键字定义,接口中定义方法的情况居多;
- 接口利用对象的向上转型实现对象的实例化操作,调用的方法是每个子类所覆写的方法;
- 接口应用:标准(连接不同的两种类)、工厂设计模式、代理设计模式。