前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java——接口的基本总结(基本定义、使用接口定义标准、工厂设计模式、代理设计模式、抽象类与接口的区别)

Java——接口的基本总结(基本定义、使用接口定义标准、工厂设计模式、代理设计模式、抽象类与接口的区别)

作者头像
Winter_world
发布2020-09-25 10:20:28
2.1K0
发布2020-09-25 10:20:28
举报

接口与抽象类相比,使用率是最高的,所有的设计基本是围绕接口进行的,这部分内容很重要,要彻底学明白需要很长时间,与接口相关 的两个重要设计模式:工厂设计模式、代理设计模式,是需要死记硬背的。

1、接口的基本概念

接口是一种特殊类,但是接口中的组成比类的简单,主要由抽象方法和全局常量组成。而接口使用interface关键字来定义。

【举例】:定义一个接口

代码语言:javascript
复制
interface A{ //定义了一个接口
    public static final String MSG= "hello";
    public abstract void print();
}

接口是不能直接实例化对象的,当一个接口定义完成后,按如下步骤进行接口的使用:

  • 1)接口一定要定义子类,子类利用implements关键字来实现接口,一个子类可以实现多个接口;

           --秒杀抽象类的单继承局限;

  • 2)接口的子类必须覆写接口中的全部抽象方法;
  • 3)接口的对象利用子类对象的向上转型进行实例化操作。

【举例】:使用接口

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        X x = new X();//实例化子类对象
        A a = x;   //子类为父接口实例化
        B b = x;
        a.print();
        b.fun();
    }
}

interface A{ //定义了一个接口
    public static final String MSG= "hello";
    public abstract void print();
}
interface B{
    public abstract void fun();
}
class X implements A,B{//同时实现A、B两个父接口

    @Override
    public void print() { //覆写接口A中的抽象方法
        System.out.println("你好,接口A");
    }

    @Override
    public void fun() {//覆写接口B中的抽象方法
        System.out.println(MSG);
    }
}

但是,现在有这样一种操作,也能输出hello,B和A没有什么关系,却可以转换,因为X是子类。

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        A a = new X();   //X子类为父接A口实例化
        B b = (B)a;
        b.fun();
    }
}

【注意】:关于接口的组成描述

接口里面在定义的时候就已经明确的给出了开发要求:抽象方法和全局常量,所以,以下两种接口的定义本质上是一样的。

完整定义

简化定义

interface A{ //定义了一个接口 public static final String MSG= "hello"; public abstract void print(); }

interface A{ //定义了一个接口 String MSG= "hello"; void print(); }

如果定义接口方法时没有使用public,本质上也不是default权限,默认就是public。为了防止开发者概念混淆,所以后续开发建议在定义接口时都写上public,可以不写abstract。

代码语言:javascript
复制
interface A{ //定义了一个接口
     String MSG= "hello";
     public void print();
}

现在程序中出现有类、抽象类、接口,三者之间的联系需要注意:

一个普通类若要实现接口,又要继承抽象类,一定要显extends继承抽象类,再实现接口,形式如下:

class 子类 extends 抽象类 implements 接口1,接口2,...{}

【举例】:观察子类的多继承

代码语言:javascript
复制
interface A{ //定义了一个接口
    public static final String MSG= "hello";
    public abstract void print();
}
abstract class B{
    public abstract void fun();
}

class X extends B implements A{
    @Override
    public void print() {
    }
    @Override
    public void fun() {
    }
}

另外,除了以上的结构外,抽象类还可以直接实现接口:

【举例】:抽象类实现接口

代码语言:javascript
复制
interface A{ //定义了一个接口
    public static final String MSG= "hello";
    public abstract void print();
}
abstract class B implements A{ //此时抽象类有两个抽象方法
    public abstract void fun();
}

class X extends B{
    @Override
    public void print() {
    }
    @Override
    public void fun() {
    }
}

抽象类可以实现接口,但是反过来,接口是不能继承抽象类的,一个接口却可以使用extends关键字继承多个父接口。

【举例】:接口多继承

代码语言:javascript
复制
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{
    @Override
    public void printA() {
    }
    @Override
    public void printB() {
    }
    @Override
    public void printC() {
    }
}

虽然接口本身只能有抽象方法 和全局常量,但是内部的结构是不受限制 的,也就是 一个接口的内部可以继续定义内部类,内部抽象类,或内部接口。如果一个内部接口上使用了static定义,这个内部接口就属于外部接口。

【举例】:使用static定义内部接口

代码语言:javascript
复制
interface A{ //定义了一个接口
    static interface B{
        public void print();
    }
}
class X implements A.B{//注意此处使用的是.........
    @Override
    public void print() {
    }
}

对于接口的使用,有如下几点总结:

  • 接口避免了单继承局限,一个子类可以实现多个接口;
  • 接口中 的权限统一为public,方法都是抽象方法,大多数情况下接口中都不会定义全局常量;
  • 所有的内部类结构都不受定义语法的限制,static定义的内部接口就是外部接口。

实际开发中,接口的三个使用原则:

  • 制定操作的标准;
  • 表示一种能力;
  • 将服务器端的远程方法视图提供给客户端。

2、接口的应用——定义标准

现实生活中,对于接口的名字很常见,如USB接口、HDMI接口、DVI接口等。以USB设备为主,描述一下接口的实际作用:

【举例】:首先要定义的就是接口

代码语言:javascript
复制
interface USB{
    public void start();
    public void stop();
}

【举例】:电脑上提供有支持USB的操作插入点

代码语言:javascript
复制
class Computer{
    public void plugin(USB usb){
        usb.start();
        usb.stop();
    }
}

不管有多少个设备,电脑plugin()方法只有接收USB的接口实例,操作步骤就是固定的。

【举例】:定义USB的子类

代码语言:javascript
复制
class Flash implements USB{
    @Override
    public void start() {
        System.out.println("开始使用U盘进行操作");
    }

    @Override
    public void stop() {
        System.out.println("U盘停止操作");
    }
}
class Keyboard implements USB{
    @Override
    public void start() {
        System.out.println("开始使用键盘进行操作");
    }

    @Override
    public void stop() {
        System.out.println("键盘停止操作");
    }
}

【举例】:程序调用

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Computer c = new Computer();
        c.plugin(new Flash()); //传递U盘对象
        c.plugin(new Keyboard());//传递键盘对象
    }
}

所以,如果有了接口标准,即便有千万个子类,也是在一个接口上使用的,所以说接口可以定义标准,说的再高级一点:

接口可以连接两个不同的层。

3、接口的应用——工厂设计模式(Factory)

这部分内容很重要,以下设计的工厂类程序基本结构必须要记住。工厂模式用于对象的创建,使得客户从具体的产品对象中被解耦。首先编写一段简单的代码,观察下为什么会有所谓的工厂设计模式?

【举例】:观察程序定义

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fruit f = new Apple();
        f.eat();
    }
}

interface Fruit{
    public void eat();
    
}
class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}

以上代码本身看上去没什么语法问题,但是有一个设计上的缺失,若现在Fruit增加了一个子类,且主类想使用这个子类,该怎么办?

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fruit f = new Cherry();
        f.eat();
    }
}

interface Fruit{
    public void eat();

}
class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}
class Cherry implements Fruit{
    @Override
    public void eat() {
        System.out.println("吃樱桃");
    }
}

以上,我们发现,若要扩充我们的程序,却影响了客户端的执行,若要解决这个问题,可参照Java可移植性的实现原理:

  • 不可移植性:程序-》操作系统;
  • 可移植性:程序-》JVM-》操作系统;

【举例】在客户端与接口之间引入一个中间层

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fruit f = Factory.getInstance("apple");//这里可以改成外部输入的
        if(f!=null){
            f.eat();
        }
    }
}

interface Fruit{
    public void eat();

}
class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}
class Cherry implements Fruit{
    @Override
    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;
        }
    }
}

以上代码形式,如果现在想增加一个新的子类,不需要修改客户端,直接修改工厂类Factory类即可。

4、接口的应用——代理设计模式(Proxy)

这部分内容很重要,以下设计的程序基本结构必须要记住。

代理设计模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。换种说法,代理结构指的是在接口上的一种应用,一个接口有一个核心的操作主题,但是只依靠核心的操作主题是无法完成所需要的功能的,那么需要有一个代理主题,代理主题完成所有的与核心主题有关的概念。

【举例】:代码实现(一个客户通过讨债公司讨债的故事)

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Subject sub = new ProxySubject(new RealSubject());
        sub.get();
    }
}

interface Subject{ //核心操作主题
    public void get();//核心操作
}

class RealSubject implements Subject{
    @Override
    public void get() {
        System.out.println("终于取回了被强占的Money");
    }
}

class ProxySubject implements Subject { //代理的真实主题
    private Subject subject; //代理的真实主题
    public ProxySubject(Subject subject){
        this.subject = subject;
    }

    public void prepare(){
        System.out.println("讨债前的准备工作");
    }
    @Override
    public void get() {
        this.prepare();
        this.subject.get();//真实主题的讨债
        this.destroy();
    }

    public void destroy(){
        System.out.println("讨债后的收尾工作");
    }
}

5、抽象类与接口的区别(常见面试题)

目前我们学习了抽象类、类、对象、接口,这些概念从开发上来讲有什么关系?

所有类的抽象使用的就是接口,接口避免了单继承的局限;

【面试题】:抽象类与接口的区别?

序号

区别

抽象类

接口

1

定义关键字

abstract

interface

2

组成

属性、常量、抽象方法、构造方法、普通方法

全局常量、抽象方法

3

权限

可以使用各种权限

只能是public

4

子类实现

extends关键继承一个抽象类

implements关键字实现多个接口

5

关系

抽象类可以实现多个接口

接口不能继承抽象类,但是却可以利用extends关键字实现接口的多继承

6

对象实例化

依靠子类对象的向上转型实现抽象类或接口对象的实例化

7

设计模式

模板设计模式

工厂设计模式、代理设计模式

8

操作局限

具有单继承局限

没有单继承局限

由以上比较,抽象类与接口实际上都可以限制子类必须要覆写的要求,但是由于抽象类本身存在单继承局限,所以日后开发中,若发现抽象类与接口都可以使用时,优先考虑接口,而抽象类通常用来作为接口与普通类之间的过渡类使用。

6、总结

1)接口利用interface关键字定义,接口中定义方法的情况居多;

2)接口利用对象向上转型实现接口对象的实例化操作,调用的方法是每个子类所覆写的方法;

3)接口的应用:标准(连接不同的两种类)、工厂设计模式、代理设计模式。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、接口的基本概念
    • 2、接口的应用——定义标准
    • 3、接口的应用——工厂设计模式(Factory)
    • 4、接口的应用——代理设计模式(Proxy)
    • 5、抽象类与接口的区别(常见面试题)
    • 6、总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档