泛览天下

阅读,看尽天下事

19.面向对象进阶:接口,被实现,多继承,JDK8新增方法(默认,静态,私有),极端情况

2022-08-04 00:07:51


接口,多实现,多继承接口概述,特点接口就是体现规范的,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象体现了现实世界中“如果你是这一类事务,则必须完成某些行为达到某些标准”的思想接口的定义格式:接口用interface关键字来定义pu


接口,多实现,多继承

接口概述,特点

  • 接口就是体现规范的,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象

  • 体现了现实世界中“如果你是这一类事务,则必须完成某些行为达到某些标准”的思想

  • 接口的定义格式:接口用interface关键字来定义

    public interface 接口名{
          //常量
          //抽象方法
    }
    
  • JDK8之前接口中只能是常量和抽象方法,没有其他成分

  • 接口不能被实例化(即接口不能被创建对象)

  • 接口中的成员都是public修饰的,写不写都是,因为规范的目的就是为了公开化

  • 接口中的常量可以省略public static final 关键字,作为标准,接口会自己补上

  • 同上,接口中的抽象方法也可以省略public abstract关键字(但是抽象类中不可以,因为抽象类里方法不一定都是public)

    public interface Person {
      String name = "zs";
      // 等价于public static final String name = "zs";
      
      void eat();
      void drink();
      //等价于public abstract void eat();
    }
    

接口的基本使用:被实现(类可以实现多个接口)

  • 接口是用来被类实现的,实现接口的类被称为实现类,实现类可以理解为所谓的子类

  • 实现类的定义格式(实现关键字implements)

    修饰符 class 实现类名 implements 接口1,接口2,接口3……{
    
    }
    

    由此可知,接口可以被单类实现,也可以被多类实现

    接口可以理解为特殊的父类,实现类可以理解为特殊的子类

    IDEA实现方法快捷键:Alt + Enter

  • 测试代码:

    package com.java.interfaceTest;
    
    public interface SportMan {
        void run();
        void competition();
    }
    
    
    package com.java.interfaceTest;
    
    public interface Law {
        void rule();
    }
    
    
    package com.java.interfaceTest;
    
    public class BallMan implements SportMan, Law {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void rule() {
            System.out.println(getName() + "Follow the rules");
        }
    
        @Override
        public void run() {
            System.out.println(getName() + "run fast");
        }
    
        @Override
        public void competition() {
            System.out.println(getName() + "Competition wite others");
        }
    }
    
    
    package com.java.interfaceTest;
    
    public class Test {
        public static void main(String[] args) {
            BallMan b = new BallMan();
            b.setName("林丹");
            b.run();
            b.rule();
            b.competition();
        }
    }
    
    
  • 由测试代码可知,一个实现类实现接口,必须重写全部接口的全部抽象方法,否则这个类就必须被定义成抽象类

接口与接口的关系:多继承(一个接口可以同时继承多个接口)

  • 类和类的关系:单继承
  • 类和接口的关系:多实现
  • 接口和接口的关系:多继承


  • 通过一个接口继承多个接口,再用一个实现类实现这个接口,达到这个实现类同时实现多个接口的目的

    测试代码:

    package com.java.interfaceExtents;
    
    public interface Person {
        void eat();
        void drink();
    }
    
    
    package com.java.interfaceExtents;
    
    public interface Policeman extends Job,Person{
        void shot();
    }
    
    
    package com.java.interfaceExtents;
    
    public interface Job {
        void order();
    }
    
    
    package com.java.interfaceExtents;
    
    public class Police implements Policeman{
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void order() {
            System.out.println(getName() + "为人民服务");
        }
    
        @Override
        public void eat() {
            System.out.println(getName() + "吃午餐");
        }
    
        @Override
        public void drink() {
            System.out.println(getName() + "喝二锅头");
        }
    
        @Override
        public void shot() {
            System.out.println(getName() + "擅长射击");
        }
    }
    
    
    package com.java.interfaceExtents;
    
    public class Test {
        public static void main(String[] args) {
            Police p  = new Police();
            p.setName("人民警察");
            p.order();
            p.eat();
            p.drink();
            p.shot();
        }
    }
    
  • 在接口和接口之间涉及到了多继承,接口支持多继承的原因是在于,面对像子类处理多个父类中多个方法同名时问题无法处理因此类无法多继承,而接口在面对多个父接口中方法同名时可以 规范合并 ,选择其中任意一个执行就可以达到实现多个同名方法的效果,而这个是类继承是不具备的,也正是如此,类是单继承,接口是多继承

  • 规范合并有前提:即同名方法的返回值相同。如果不同,则不能进行规范合并(此时叫做 规范冲突 ),同时也不能实现接口间的多继承


  • 接口多继承的作用:
    • 规范合并,整合多个接口为同一接口,便于子类实现

JDK8开始接口新增方法

JDK8版本开始后,Java只对接口的成员方法进行了新增

  • 诞生背景:一个成熟的项目上线后,需要对原接口中新增一些新的方法,此时一旦添加新的抽象方法,则会变更实现类中大量代码去实现这些新增方法,造成极大不便,为了在丰富接口的同时又不对子类代码进行更改, JDK8之后允许接口中直接定义带有方法体的方法
  • 新增方法:

    1. 默认方法

      • 类似之前写过的普通实例方法,必须用default修饰

      • 默认会用public修饰(不用写),需要用接口的实现类的对象来调用(事实上接口也没有对象)

        default void run(){
         System.out.println("跑");
        }
        
    2. 静态方法

      • 默认会用public修饰(不用写),必须使用static修饰

      • 特殊的,接口的静态方法必须用本身的接口名来调用

        static void index(){
         System.out.println("页");
        }
        

        例如接口名是Amy,调用格式:Amy.index();

    3. 私有方法

      • 就是私有的实例方法,必须使用private修饰(从JDK1.9才开始有)

      • 只能在本类中被其他的默认方法或者私有方法访问

        private void eat(){
         System.out.println("吃");
        }
        
  • 三种方法测试代码:

    package com.java.interfaceJDK8;
    
    public interface SportMan {
    
        /**
         * 默认方法,使用default修饰,默认使用public修饰
         * 默认方法位于接口中,接口不能创建对象,必须过继给实现类,由实现类的对象调用
         * (其实本质上就是实例方法,但是官方不承认,因为和抽象的概念冲突,加上default后就成了默认方法)
         */
        default void defaultMethod() {
            System.out.println("实现类的对象调用了接口的默认方法");
        }
    
        /**
         * 静态方法,使用static修饰,默认使用public修饰
         * 接口的静态方法,必须使用接口名自己调用
         */
        static void staticMethod() {
            System.out.println("接口名调用了接口的静态方法");
        }
    
        /**
         * JDK1.9新增私有方法,使用private修饰
         * 必须在接口内部被 默认方法 和 其他私有方法 调用才能被访问
         */
        private void privateMethod() {
            System.out.println("接口的静私有方法被接口的默认方法调用");
        }
    
        // 默认方法调用
        default void privateDefaultTest() {
            privateMethod();
            // 其他私有方法调用
            privateMethod2();
        }
    
        // 其他私有方法调用
        private void privateMethod2() {
            System.out.println("私有方法被其他私有方法调用");
        }
    }
    
    
    package com.java.interfaceJDK8;
    
    public class BallMan implements SportMan {
    }
    
    
    package com.java.interfaceJDK8;
    
    public class Test {
        public static void main(String[] args) {
            // 使用实现类的对象调用默认方法
            BallMan b = new BallMan();
            b.defaultMethod();
    
            // 使用接口名调用静态方法
            SportMan.staticMethod();
    
            // 使用默认方法调用私有方法
            b.privateDefaultTest();
        }
    }
    
    

使用接口的注意事项(极端面试官面试考察)

  1. 接口不能创建对象

    因为接口是更加彻底的抽象

  2. 一个类实现多个接口,多个接口中有同样的静态方法不冲突

    因为静态方法调用是用所在的接口名调用,同名时也是自己的接口调用自己的静态方法,因此不冲突

  3. 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的

    即A extends B implements C,此时就近先继承了然后实现了C,如果实现和继承位置互换 ,就变成了先实现再继承,会造成继承与实现之间的冲突,即A implements C extends B是不合法的

  4. 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可

    解释同上述测试代码
    “(其实本质上就是实例方法,但是官方不承认,因为和抽象的概念冲突,加上default后就成了默认方法)”

    类之所以无法多继承,原因之一就是无法对同名方法做出选择

    Java官方为了强制让接口多实现,硬性规定遇到这种情况,干掉所有接口的同名方法,直接重写它们

    于是诞生了新的问题:即程序员非调用同名默认方法中的一个不可,会发现永远无法调用(期待未来更高版本的的JDK)

    这是典型的“耍无赖”,如同默认方法的诞生,从抽象的层面接口无法多实现,所以直接推翻自己,强制规定

  5. 一个 接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承

    往上查看《接口与接口的关系:多继承》中的测试代码,以及规范合并和规范冲突的概念