菜鸡的Java笔记 第二十四 - java 接口的基本定义

风流意气都作罢 提交于 2020-01-20 03:15:26

1.接口的基本定义以及使用形式
        2.与接口有关的设计模式的初步认识
        3.接口与抽象类的区别
        
        接口与抽象类相比,接口的使用几率是最高的,所有的设计几乎都是围绕着接口进行的
        但是要想把接口彻底弄明白,需要很长一段时间
        
        接口是一种特殊的类,但是在接口里面的组成与类不同,比类的组成部分简单,主要由抽象方法和全局常量所组成
        而接口使用 interface 关键字来定义
        范例:定义一个接口

interface A{ // 定义了一个接口
    public static final String MSG = "Hi";
    public abstract void ptint();
}
public class Interface{
    public static void main(String args[]){
        // 无法实例化,但是可以调用
        System.out.println(A.MSG);
    }
}

           
        在以后不管学习到系统类库的使用还是自己写代码的时候,接口里面的主要组成(98%)都是抽象方法

            当一个接口定义完成之后,需要遵循如下的步骤进行接口的使用
                接口一定要定义子类,子类利用 implements 关键字来实现(实现这个词可以理解为继承)接口,一个子类可以同时实现多个接口
                    秒杀抽象类的单继承局限,一个抽象类只能够被一个子类所继承
                接口的子类(如果不是抽象类)那么必须覆写接口中的全部抽象方法
                接口的对象利用子类对象的向上转型进行实例化操作
        
        范例:使用接口

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

interface B{
    public abstract void fun();
}
class X implements A,B{ // 此时的X子类同时实现了B和B两个父接口
    public void ptint(){
        System.out.println("*******");
    }
    public void fun(){
        System.out.println(MSG);
    }
}
public class Interface{
    public static void main(String args[]){
        X x = new X();// 实例化子类对象
        A a = x; // 子类为父接口实例化
        B b = x; // 子类为父接口实例化
        a.ptint();
        b.fun();
    }
}
/*
结果
********
Hi
*/

           
            但是这个时候会有这样一种比较神奇的操作

        细节:在进行转型的处理之中,真正有用处的不是这个父类(或是父接口),而是在于 new 的子类上
        既然 X 属于两个父接口的共同子类,那么一旦是使用了这个子类实例化的接口对象就表示可以进行父接口的强制转换

        范例:神奇的操作

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

interface B{
    public abstract void fun();
}
class X implements A,B{ // 此时的X子类同时实现了B和B两个父接口
    public void ptint(){
        System.out.println("*******");
    }
    public void fun(){
        System.out.println(MSG);
    }
}
public class Interface{
    public static void main(String args[]){
        A a = new X(); // X子类为父接口A 实例化
        B b = (B)a;// 理解不了的操作
        b.fun();
    }
}
//结果:

           
            B和A鸟关系都没有但是可以转换,因为X是子类
            
        注意:关于接口的组成描述
            接口里面在定义的时候就已经明确的给出了开发要求:抽象方法和全局常量,所以一下两种接口的定义从本质上讲是完全一样的
                完整定义:                                        简化定义:
                interface A{// 定义了一个接口                    interface A{// 定义了一个接口
                    public static final String MSG = "Hi";            String MSG = "Hi";
                    public abstract void ptint();                    void ptint();
                }                                                }
            如果在定义接口方法的时候没有使用 public ,那么本质上也不是 default 权限,而默认就是 public

interface A{// 定义了一个接口
    String MSG = "Hi";
    void ptint();
}
class X implements A{ // 此时的X子类同时实现了B和B两个父接口
    void ptint();
}
public class Interface{
    public static void main(String args[]){
        
    }
}
//结果:(出错:X中的 ptint()无法实现A中的 ptint(),正在尝试分配更低的访问权限:以前为 public)
                


            很多时候为了防止一些开发者概念不清晰,所以以后建议在定义接口的时候永远都写上 public ,但是一般都不会去写 abstract
            现在程序之中出现有类,抽象类,接口,这几者之间的联系就需要注意好了
            一个普通类如果要实现接口又要求继承抽象类,则一定采用 extends 继承抽象类,再 implements 实现接口
            格式:
                 class 子类 extends 抽象类 implements 接口1,接口2...{}
        
            范例:观察子类的多继承

interface A{// 定义了一个接口
    String MSG = "Hi";
    public void ptint();
}
abstract class B{
    public abstract void fun();
}
class X extends B implements A{ // 此时的X子类同时实现了B和B两个父接口
    public void ptint(){}
    public void fun(){}
}
public class Interface{
    public static void main(String args[]){
        
    }
}
            
            另外除了以上的结构之外,一个抽象类还可以直接实现接口
        范例:抽象类实现接口
interface A{// 定义了一个接口
    String MSG = "Hi";
    public void ptint();
}
abstract class B implements A{//这个时候抽象类有两个抽象方法
    public abstract void fun();
}
class X extends B { // 此时的X子类同时实现了B和B两个父接口
    public void ptint(){}
    public void fun(){}
}
public class Interface{
    public static void main(String args[]){
        
    }
}

           
            现在一定要知道,抽象类可以实现接口,但是反过来,接口可不能够继承抽象类,但是一个接口却可以使用 extends 关键字继承多个父接口
        范例:接口多继承

interface A{// 定义了一个接口
    public void ptintA();
}
interface B{
    public void ptinB();
}
interface C extends A,B{// C 是A与B的子接口
    public void ptintB();
}
class X implements C{
    public void ptintA(){}
    public void ptintB(){}
    public void ptinC(){}
}
public class Interface{
    public static void main(String args[]){
        
    }
}

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

interface A{
    static interface B{
        public void ptint();
    }
}
class X implements A.B{
    public void ptint(){}
}
public class Interface{
    public static void main(String args[]){
        
    }
}

           
        总之对于接口的使用可以发现有如下几点:
            接口避免了单继承的局限,一个子类可以实现多个接口
            接口中的权限统一为 public ,方法都是抽象方法,90%的情况下接口中很少定义全局常量
            所有的内部类结构都不受到定义语法的限制,static 定义的内部接口就是一个外部接口
        实际开发中接口的三个使用原则:
            定制操作标准
            表示一种能力
            将服务器端的远程方法视图提供给客户端



*/

/*     接口的实际应用-- 标准
        现实生活中对于接口这个名词应该不陌生例如:USB,PCI,VGA,HDMI,DVI等接口
        以USB为主,描述一下接口的实际作用
        范例:首先要定义出的就是接口

interface USB{
    public void start();
    public void stop();
}
public class Interface{
    public static void main(String args[]){
        
    }
}

           
            不管什么样的USB设备只要一连接到电脑上,那么就需要默认执行固定的操作
        范例:电脑上提供有支持USB的操作标准插入点

interface USB{
    public void start();
    public void stop();
}
class Computer{  // 电脑
    public void plugin(USB usb){
        usb.start();
        usb.stop();
    }
}
public class Interface{
    public static void main(String args[]){
        
    }
}

           
            不管有多少设备,电脑的 plugin() 方法里面只要接收的是USB接口实例,那么操作的步骤就是固定的
        范例:定义USB 的子类

interface USB{
    public void start();
    public void stop();
}
class Computer{  // 电脑
    public void plugin(USB usb){
        usb.start();
        usb.stop();
    }
}
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 Interface{
    public static void main(String args[]){
        
    }
}

           
            现在的子类是按照严格的操作标准使用着
        范例:程序调用处

interface USB{
    public void start();
    public void stop();
}
class Computer{  // 电脑
    public void plugin(USB usb){
        usb.start();
        usb.stop();
    }
}
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 Interface{
    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 Interface{
    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 Interface{
    public static void main(String args[]){
        Fruit f = new Cherry();
        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 getlnstance(String className){ // 直接取得接口实例
        if("apple".equals(className)){
            return new Apple();
        }else if("cherry".equals(className)){
            return new Cherry();
        }else{
            return null;
        }
    }
}
public class Interface{
    public static void main(String args[]){ // 为了方便模拟化调用
        Fruit f = Factory.getlnstance(args[0]);
        if(f != null){
            f.eat();
        }
    }
}

           
            执行苹果操作: java Interface apple
            执行樱桃操作: java Interface cherry
            如果现在要想增加新的子类,那么不需要修改客户端,直接修改工厂类即可

代理设计模式的核心结构:真实主题是负责核心操作,而这个核心操作如果要想正常完成功能,就必须有代理主题来负责辅助功能实现

*/

/*    接口的应用--代理设计模式(Proxy,背)
        所谓的代理结构指的是在接口上的一种应用,一个接口一个核心的操作主题
        但是在整个操作的过程之中,如果只依靠核心的操作主题是无法完成所需要功能的,那么需要有一个代理的主题
        代理主题完成所以的与核心主题有关的概念
        范例:

interface Subject{// 核心操作主题
    public void get();//核心操作
}
class RealSubject implements Subject{
    public void get(){
        System.out.println("大爷取回了被强行霸占的钱");
    }
}
class ProxySubject implements Subject{
    private Subject subject;// 代理的真实主题
    public ProxySubject(Subject subject){
        this.subject = subject;
    }
    public void prepar(){
        System.out.println("追讨前的准备.........");
    }
    public void get(){
        this.prepar();
        this.subject.get();// 真实主题的讨债
        this.destroy();
    }
    public void destroy(){
        System.out.println("追讨完后的首尾:掩埋......");
    }
}

public class Interface{
    public static void main(String args[]){ // 为了方便模拟化调用
        Subject sub = new ProxySubject(new RealSubject());
        sub.get();
    }
}


*/

/*    抽象类与接口的区别(面试题)
        到现在为止已经学习过了这么几个概念:抽象类,类,对象,接口,这些概念从开发上来讲什么关系呢?
        所有类的抽象使用的就是接口,而且接口避免了单继承局限
        面试题:请解释抽象类与接口的区别

区别 抽象类  接口
定义关键字 abstract class  interface
组成 属性,常量,抽象方法,构造方法,普通方法 抽象方法与全局常量
权限  可以使用各种权限 只能够 public
子类实现 利用 extends 关键字可以继承一个抽象类 利用 implements 关键字可以实现多个接口
 关系 抽象类可以实现多个接口 接口不能继承抽象类,接口却可以利用 extends 关键字实现接口的多继承
对象实例化 依靠子类对象的向上转型实现抽象类或接口 对象的实例化操作
设计模式 模版设计模式 工厂设计模式,代理设计模式
操作局限 单继承局限 没有单继承局限



        通过以上的几点比较可以发现,抽象类与接口实际上都可以限制子类必须要覆写的方法要求,但是由于抽象类本身存在有单继承局限
        所以在日后开发过程之中,如果发现抽象类与接口都可以同时使用的时候,优先考虑接口,而抽象类在实际的应用中往往是作为接口与普通类之间的过度累使用


    总结
        1.接口利用 interface 关键字定义,接口中定义方法的情况居多
        2.接口利用对象向上转型实现接口对象的实例化操作,调用的方法是每个子类所覆写的方法
        3.接口应用:标准(连接不同的两种类),工厂设计模式,代理设计模式

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!