Lesson13_抽象类和接口

Lesson13_抽象类和接口

回顾

多态的必要条件

  • 继承 or 实现
  • 方法重写
  • 父类型的引用持有子类型的对象

上下转型

  • 向上转型
    • 安全:不会报错
    • 丢失:丢失子类新增的成员
  • 向下转型
    • 风险:instanceof协助判断,避免ClassCaseException boolean res = 引用变量 instanceof 类型;
    • 恢复:恢复子类特有的成员

动态绑定和静态绑定

  • 静态绑定:程序运行在之前就可以确定调用的是哪个方法。private\static\final修饰的方法和构造方法
  • 动态绑定:对象创建后才能确定调用的是哪个类重写的方法

抽象类

抽象方法

  • 使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉类必须要给抽象方法提供具体的实现。
  • 特点:
    • 只有方法头没有方法体的方法称之为抽象方法;
    • 抽象方法用abstract来修饰;
    • 抽象方法代表一种不确定的操作或者行为;
    • 抽象方法不能被调用。

抽象类

  • 包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须要给抽象方法提供具体的实现。通过抽象类,我们就可以做到严格控制子类的设计,使子类之间更加通用。

  • 抽象类就是当描述一个类的时候,如果不能确定其功能方法(函数)如何定义,那么该类就可以定义为抽象类,不能确定的功能函数要描述为抽象方法(函数)。

  • 抽象类和抽象方法的基本用法

    //抽象类
    abstract class Animal {
        abstract public void shout();  //抽象方法
    }
    class Dog extends Animal { 
        //子类必须实现父类的抽象方法,否则编译错误
        public void shout() {
            System.out.println("汪汪汪!");
        }
        public void seeDoor(){
            System.out.println("看门中....");
        }
    }
    //测试抽象类
    public class TestAbstractClass {
        public static void main(String[] args) {
            Dog a = new Dog();
            a.shout();
            a.seeDoor();
        }
    }
    
  • 抽象类和抽象方法的使用要点

    • 有抽象方法的类只能定义成抽象类。
    • 抽象类用abstract关键字修饰。
    • 用abstract修饰的类是抽象类,它不可以创建对象,即不能用new来实例化才抽象类。
    • 抽象类只能用来被继承。
    • 抽象方法必须被子类实现或继续抽象。
    • 抽象类中可以有具体的方法,可以没有抽象方法。
  • 抽象方法 ==> 抽象类

  • 意义?

    • 如果不想让别人创建该类的对象,就把它抽象
      • 有些类没有创建对象的必要,为了避免用户创建这些类的对象,可以将其抽象
      • 抽象类中可以定义要“做什么”,至于"怎么做"可以交给不同的子类去实现。父类本身没有实现的必要性
    • 抽象类不能实例化对象。构造方法只是用来在子类中调用,来初始化父类中的属性。
      • 子类所有的构造方法,第一条语句都会默认调用父类的无参构造方法;
      • 如果父类没有无参构造方法,子类所有构造方法都会报错。
      • 除非在子类所有构造方法中都手动调用父类中的有参构造方法。

模板方法

定义一套流程 ,至于如何实现其中的步骤,交给子类去完成。

// 工作者类
public abstract class Worker {
    protected abstract void prepare();// 准备工作
    protected abstract void startWork();// 开始工作
    protected abstract void finish();// 结束工作,收尾

    // 模板方法:定义了一套流程(顺序),让子类去实现其中的步骤
    public void work(){
        prepare(); // 步骤一
        startWork(); // 步骤二
        finish(); // 步骤三
    }
}
  • 模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。
  • 模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。
  • 简单的说,一个模板方法用一些抽象的操作定义一个算法,而子类将重新定义这些操作以提供具体的行为。
  • 它的意图是定义了在一个操作中的算法框架,把一些步骤推迟到子类去实现。模板方法模式让子类不需要改变算法结构而重新定义特定的算法步骤。
  • 为了防止子类随意覆盖父类的模板,一般模板可以设计我final的。
  • 模板方法中的方法可以分为两大类:模板方法和基本方法。
    • 模板方法:
      • 一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
      • 一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。
    • 基本方法:
      • 基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。
      • 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
      • 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
      • 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。
        1. 第一类钩子方法可以与一些具体步骤“挂钩”,以实现在不同条件下执行模板方法中的不同步骤,这类钩子方法的返回类型通常是boolean类型的,这类方法名一般为isXXX(),用于对某个条件进行判断,如果条件满足则执行某一步骤,否则将不执行
        2. 有一类钩子方法就是实现体为空的具体方法,子类可以根据需要覆盖或者继承这些钩子方法,与抽象方法相比,这类钩子方法的好处在于子类如果没有覆盖父类中定义的钩子方法,编译可以正常通过,但是如果没有覆盖父类中声明的抽象方法,编译将报错。

接口

声明概念

  • 接口就是比抽象类还抽象的抽象类,可以更加规范的对子类进行约束。全面专业的实现了:规范和具体实现的分离。

  • 抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。

  • 从接口的实现者角度看,接口定义了可以向外部提供的服务。

  • 从接口的调用者角度看,接口定义了实现者能提供那些服务。

  • 接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。大家在工作以后,做系统时往往就是使用“面向接口”的思想来设计系统。

  • 接口和实现类不是父子关系,是实现规则的关系。like-a

  • 是对功能的封装,一组没有血缘关系的类之间相似功能的封装。可以实现多个接口,避免单继承带来的局限。

  • 用interface关键字声明,①表示一种能力,或者②用于制定一套规范,让实现类遵循

    • 属性全是常量 public static final修饰
    • 方法全是抽象 public abstract 修饰
    • *JDK8开始,可以包含带有方法体的方法。
  • 声明

    [访问修饰符]  interface 接口名   [extends  父接口1,父接口2]  {
    常量定义;  
    方法定义;
    }
    
  • 定义:

    • 访问修饰符:只能是public或默认。
    • 接口名:和类名采用相同命名机制。
    • extends:接口可以多继承。
    • 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
    • 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
  • 要点:

    • 子类通过implements来实现接口中的规范。
    • 接口不能创建实例,但是可用于声明引用变量类型。
    • 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
    • JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
    • JDK1.8后,接口中包含普通的静态方法。
  • Java类和类之间是单继承(一个类只能有一个父类)

  • 类和接口之间是多实现(一个类可以实现多个接口)

  • 接口和接口之间是多继承(一个接口可以继承多个接口)

    public class TestInterface {
        public static void main(String[] args) {
            Volant volant = new Angel();
            volant.fly();
            System.out.println(Volant.FLY_HIGHT);
             
            Honest honest = new GoodMan();
            honest.helpOther();
        }
    }
    /**飞行接口*/
    interface Volant { 
        int FLY_HIGHT = 100;  // 总是:public static final类型的;
        void fly();   //总是:public abstract void fly();
    }
    /**善良接口*/
    interface Honest { 
        void helpOther();
    }
    /**Angle类实现飞行接口和善良接口*/
    class Angel implements Volant, Honest{
        public void fly() {
            System.out.println("我是天使,飞起来啦!");
        }
        public void helpOther() {
            System.out.println("扶老奶奶过马路!");
        }
    }
    class GoodMan implements Honest {
       public void helpOther() {
            System.out.println("扶老奶奶过马路!");
        }  
    }
    class BirdMan implements Volant {
        public void fly() {
            System.out.println("我是鸟人,正在飞!");
        }
    }
    
  • 接口的继承

    interface A {
        void testa();
    }
    interface B {
        void testb();
    }
    /**接口可以多继承:接口C继承接口A和B*/
    interface C extends A, B {
        void testc();
    }
    public class Test implements C {
        public void testc() {
        }
        public void testa() {
        }
        public void testb() {
        }
    }
    

面向接口编程

  • 面向接口编程是面向对象编程的一部分。

​ 为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更多的体现在具体实现上。我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现规范的高质量的项目。

​ 接口就是规范,就是项目中最稳定的东东! 面向接口编程可以让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。

​ 通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和和可维护性。

​ 面向接口编程的概念比接口本身的概念要大得多。设计阶段相对比较困难,在你没有写实现时就要想好接口,接口一变就乱套了,所以设计要比实现难!

接口本质探讨

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人;如果你是坏人,则必须欺负好人。

  • 接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。

  • 面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

  • 使用接口可以更好的支持多态?

  • 父类的引用只能够持有其子类的对象,其它没有血缘关系的类是无法兼容的。

  • 接口,任何类都可以实现,没有单继承的限制。跨越了血缘的限制。任何接口的实现类都可以兼容

总结

  • 接口:接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。
    • 优点: 一个类可以实现多个接口,接口可以让这个类不仅具有主类型的行为,而且具有其他的次要行为,比如所HashMap接口的主要类型是Map,而Cloneable接口是它具有一个次要类型,这个类型说明它可以安全的克隆。
    • 缺点:如果向一个Java接口中加入一个新的方法,所有实现这个接口的类都要编写具体的实现。
  • 抽象类:抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
    • 优点:具体类可以从抽象类中自动得到方法的默认实现。
    • 缺点:一个类只能继承一个父类,所以抽象类作为类型定义工具的效能大打折扣。

抽象类和接口的本质差异

  • 代码大全一书中说到,软件开发的本质上就是:对复杂度的控制管理。
  • 开发过程中,我认为最大的复杂度来自“变化”,项目需求的变化,项目架构的变化,功能具体实现的变化等等。
  • 而抽象类和接口正是我们工作中常用的用以解决具体实现变化的一种方式,它为我们提供了:“将接口和实现分离的方法”。这样,我们编码过程就可以面向接口而不是面向实现编程,这样当具体实现发生变化时,上游系统将基本不用做改动。
  • 抽象类和接口本质上的目的都是一样的:“提供更加高度的抽象”。但是,接口相对于抽象类,它的抽象程度更进一步。

接口(interface)和抽象类(abstract class)是支持抽象类定义的两种机制。

  • 区别:
    • 语法:

      • 相同:
        1. 接口和抽象类都能定义方法和属性。
        2. 接口和抽象类都是看作是一种特殊的类。大部分的时候,定义的方法要子类来实现
        3. 抽象类和接口都可以不含有抽象方法。接口没有方法就可以作为一个标志。比如可序列化的接口Serializable,没有方法的接口称为空接口。没有抽象方法的抽象类,不知道有什么作用,总之是可以通过编译的。
        4. 抽象类和接口都不能创建对象。
        5. 抽象类和接口都能利用多态性原理来使用抽象类引用指向子类对象。
        6. 继承和实现接口或抽象类的子类必须实现接口或抽象类的所有的方法,抽象类若有没有实现的方法就继续作为抽象类,要加abstract修饰。若接口的子类没有实现的方法,也要变为抽象类
      • 不同:
        1. 接口能够多实现,而抽象类只能单独被继承,其本质就是,一个类能继承多个接口,而只能继承一个抽象类。
        2. 方法上,抽象类的方法可以用abstract 和public或者protect修饰。而接口默认为public abttact 修饰。
        3. 抽象类的方法可以有需要子类实现的抽象方法,也可以有具体的方法。而接口在老版本的jdk中,只能有抽象方法,但是Java8版本的接口中,接口可以带有默认方法。
        4. 属性上,抽象类可以用各种各样的修饰符修饰。而接口的属性是默认的public static final
        5. 抽象类中可以含有静态代码块和静态方法,而接口不能含有静态方法和静态代码块。
        6. 抽象类可以含有构造方法,接口不能含有构造方法。
        7. 设计层面上,抽象类表示的是子类“是不是”属于某一类的子类,接口则表示“有没有”特性“能不能”做这种事。如飞机和鸟都能飞,但是他们在设计上实现一个Fly接口,实现fly()方法。远比两个类继承飞行物抽象类好得多。因为,飞机和鸟有太多的属性不一样。
        8. 设计层面上,另外一点,抽象类可以是一个模板,因为可以自己带集体方法,所以要加一个实现类都能有的方法,直接在抽象类中写出并实现就好,接口在以前的版本则不行。新版本Java8才有默认方法。
        9. 既然说到Java 8 那么就来说明,Java8中的接口中的默认方法是可以被多重继承的。而抽象类不行。
        10. 另外,接口只能继承接口。而抽象类可以继承普通的类,也能继承接口和抽象类。
    • 设计理念:

      • 抽象类是“自下而上”的设计,它在完成ABC三个下层的类后,发现需要抽象一个上层的抽象类D,所以说它是 自下而上的设计。抽象类是很有用的重构工具,它将继承层次结构上移,这种上移操作一般是后发的。抽象类是对一类事物的抽象,类ABC和抽象类D一般具备“is-a”的关系。它通过继承操作实现它的终极目标:复用。所以,抽象类是能拥有普通属性和普通方法的,它除了不能实例化和必须有抽象方法外,和普通类没太大区别。
      • 接口则不一样,它是“自上而下”的设计理念,我们工作的设计过程中,一般都是先设计好接口,先不考虑具体的实现。所以,如果说抽象类是对事物的抽象,接口就是对行为的抽象,实现了接口的类符合“like-a”的关系,接口建立了类和类之前的契约和协议,这也是它的终极目标:行为契约。接口将接口和具体实现两者解耦的更加彻底,使得面向接口编程更加的容易和纯粹。
    • 用途:

      • 接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用
  • 简而言之,抽象类是一种功能不全的类,接口只是一个抽象方法声明和静态不能被修改的数据的集合,两者都不能被实例化。从某种意义上说,接口是一种特殊的抽象类。

问题:实现两个接口中有同名方法

  • A、B接口中有同返回值同方法签名的抽象方法时,实现类实现一个方法即可,因为在两个接口中一模一样的方法,它既实现了A中的方法也实现了B中的方法。
  • A、B接口中有不同返回值同方法签名的抽象方法时,实现类无法同时实现A、B接口。对于这个问题可以通过内部类来解决。
  • A、B接口中有同返回值同方法签名不同异常抛出时,实现无法同时实现A、B接口,实现类实现的方法上抛出的异常只能是两个接口中同名方法同时抛出的异常,也就是交集。有不同的异常抛出时则无法同时实现。

其他

  • 四种引用数据类型:数组、class、interface、null
  • 理解了多态的概念(理论支持)、接口(代码实现)之后,可以任性去看设计模式。如果会UML就更好

简单工厂

策略模式

练习题

  • 小明喂宠物、喂孩子

  • 张三开关电器、开关门窗

你可能感兴趣的:(笔记,教案,javase,java)