JIANGzihao0222

导航

 

接口与抽象类(深入多态)

什么是抽象类?

用abstract关键字声明抽象类,抽象类不能用new 关键字进行实例化。在设计继承结构时,必须决定清楚什么类是抽象类,什么类是具体类。编译器不会让你初始化一个抽象类。抽象类,除了被继承以外,是没有其它任何用途的。抽象类中,必须包含有抽象方法,还可以包含非抽象方法。

什么是抽象方法?

即用 abstract关键字声明的方法,抽象方法没有方法实体,即没有具体的实现过程。拥有抽象方法的类,必须声明为抽象类。抽象类中的抽象方法,用于规定一组子类共同的协议。

  1.  
    abstract class Animal {
  2.  
    // 抽象方法,没有方法体
  3.  
    public abstract void eat();
  4.  
    }

在继承过程中,具体类必须实现抽象父类的所有抽象方法

抽象方法没有具体的方法体,它只是为了标记出多态而存在。在覆写抽象父类的抽象方法时,方法名、参数列表必须相同,返回值类型必须兼容。Java很在乎你是否实现了抽象类的抽象方法。

  1.  
    public class Canine extends Animal {
  2.  
    // 覆写抽象类的抽象方法
  3.  
    public void eat() {
  4.  
    System.out.println("Canine,会吃食物!!");
  5.  
    }
  6.  
    // 非继承的方法
  7.  
    public void roam() {
  8.  
     
  9.  
    }
  10.  
    }

多态的使用

在Java中,所有类都是从Object这个类继承而来的,Object是所有类的源头,它是所有类的父类。Object有很有用的方法,如 equals(), getClass(), hashCode(), toString()等。

  1. Object类,是抽象类吗? 答:不是,它没有抽象方法。
  2. 是否可以覆写Object中的方法? 答:Object类中带有 final关键字的方法,不能被覆写。
  3. Object类有什么用? 答:用途一,它作为多态可以让方法应付多种类型的机制,以及提供Java在执行期对任何对象都需要的方法实现。另一个用途,它提供了一部分用于线程的方法。
  4. 既然多态类型这么有用,为什么不把所有的参数类型、返回值类型都设定为Object? 答:因为Java是强类型语言,编译器会检查你调用的是否是该对象确实可以响应的方法。即,你只能从确实有该方法的类中去调用。
  1.  
    Object dog = new Dog();
  2.  
    dog.toString(); // 这可以通过编译,因为toString()是Object类中自有的方法。
  3.  
    dog.eat(); // 这将无法通过编译,因为dog是Object类型,它调用的eat()方法在Object类中没有。

在使用多态时,要注意对象多种类型之间的差异。如下代码:

  1.  
    Dog dog1 = new Dog();
  2.  
    Animal dog2 = new Dog();
  3.  
    Object dog3 = new Dog();
  4.  
     
  5.  
    注意这三个dog对象的区别: dog1 拥有 Dog / Animal / Object中所有的方法。dog2 拥有 Animal / Object 中的方法,不能调用 Dog 类特有的方法。 dog3 只拥有Object 中的方法,不能调用 Animal / Dog类中的方法。这就是在使用多态过程中,需要特别注意的问题。
  6.  
     
  7.  
    那么该如何把 Object 类型的 dog转化成真正的 Dog 类型呢?
  8.  
    if (dog2 instanceof Dog) {
  9.  
    Dog dog4 = (Dog)dog2;
  10.  
    }
  11.  
    if (dog3 instanceof Dog) {
  12.  
    Dog dog5 = (Dog)dog3;
  13.  
    }
  14.  
    // 此时,dog4 / dog5 就是真正的 Dog类型了。

什么是接口?

接口,是一种100%纯抽象的类。接口中的所有方法,都是未实现的抽象方法。

为什么需要接口?

接口存在的意义,就是为了解决Java多重继承带来的致命方块问题。为什么接口可以解决致命方块的问题呢?因为在接口中,所有方法都是抽象的,如此一来,子类在实现接口时就必须实现这些抽象方法,因此Java虚拟机在执行期间就不会搞不清楚要用哪一个继承版本了。

  1.  
    // interface关键字,用于定义接口
  2.  
    public interface Pet {
  3.  
    public abstract void beFriendly();
  4.  
    public abstract void play();
  5.  
    }
  1.  
    // 继承抽象父类 Animal类, 实现 Pet接口
  2.  
    public class Dog extends Animal implements Pet {
  3.  
    // 实现接口中的抽象方法
  4.  
    public void beFriendly() {
  5.  
    System.out.println("实现 Pet接口中的 beFriendly()方法");
  6.  
    }
  7.  
    // 实现接口中的抽象方法
  8.  
    public void play() {
  9.  
    System.out.println("实现 Pet接口中的 play()方法");
  10.  
    }
  11.  
    // 覆写抽象父类中的抽象方法
  12.  
    public void eat() {
  13.  
    System.out.println("覆写抽象父类中的eat()抽象方法");
  14.  
    }
  15.  
    }
  1.  
    同一个类,可以实现多个接口!
  2.  
    public class Dog extends Animal implements Pet, Saveable, Paintable { ... }

如何判断应该是设计 类、子类、抽象类、还是接口呢?

  1. 如果新的类无法对其它的类通过 IS-A 测试时,就设计成不继承任何类的类。
  2. 只有在需要某个类的特殊化版本时,以覆写或增加新的方法来继承现有的类,得到子类。
  3. 当你需要定义一群子类的模板,又不想让程序员初始化该模板时,就设计出抽象类。
  4. 如果希望类可以扮演多态的角色,就设计出完全抽象的接口。

super关键字代表什么?

super代表父类,在子类中使用 super关键字指代父类,通过super还可以调用父类的方法。

  1.  
    // 抽象父类
  2.  
    abstract class Animal {
  3.  
    void run () {}
  4.  
    }
  5.  
    // 继承父类
  6.  
    class Dog extends Animal {
  7.  
    void run () {
  8.  
    super.run(); // 这里,调用并执行父类的 run() 方法
  9.  
    // do other things
  10.  
    }
  11.  
    }
  12.  
    Dog d = new Dog();
  13.  
    d.run(); // 这调用的是子类Dog对象的 run()方法。
posted on 2022-10-28 20:46  实名吓我一跳  阅读(31)  评论(0)    收藏  举报