Skip to content

封装继承与多态

3.7 封装与继承

封装


将一个事物的相关信息聚集在一个逻辑单元内部的机制就是封装。

形式:方法、属性私有化(类的封装)、内部类等。

优点:提高复用性和安全性,使代码结构更加紧密。

继承


对于两个类 A 和 B 来说,它们之间的关系是:B 由 A 派生而来(对于 B 类来说,它拥有 A 类的所有属性和方法),则可以说 B 类继承自 A 类。

3.7.1 形式

使用extends关键字实现继承。
Java 仅支持单继承(一个子类只能有一个父类)。

3.7.2 单继承与多继承

  • 单继承
    子类只能有一个父类的继承形式。

    优点

    • 可以提高代码的复用性

    • 可以避免方法调用的混乱,提高了方法调用的安全性

  • 多继承
    子类可以拥有多个父类的继承形式。

    优点

    • 代码的复用性上要优于单继承
  • 举例

    • iPhone 类仅继承自智能设备类。(单继承)

      单继承

    • Apple Watch 类同时继承自时钟类只能设备类。(多继承)

      多继承

3.7.3 直接继承与间接继承

  • 直接继承
    B 类直接派生于 A 类,称直接继承。

  • 间接继承 B 类直接派生于 A类,C 类直接派生于 B 类。则称 C 类间接继承于 A 类。
    间接继承

特别地

子类继承自父类时,父类中这些内容子类不可见:

  • private

  • 代码块

  • 构造方法

3.8 多态

  • 当众多对象接收到同一个消息后,可以产生不同的响应效果,这种现象称为多态。

  • 多态性依托于继承性。

  • 多态主要针对的是对象具有的行为,而不是属性。

  • 可以提高代码的灵活性,配合反射实现解耦。

3.8.1 编译时多态

方法重载 (Method Overload)
行为多态
发生在同一个类中,方法名一致而参数列表不同。和修饰符、返回值类型、异常无关。

3.8.2 运行时多态

  • 向上造型

    • 对象多态

    • 父类引用指向子类对象。
      代码高亮标出。

      Input

      java
      class Super {
        // ...
      }
      
      class Sub extends Super {
        // ...
      }
      
      class App {
        public static void main(String args[]) {
          Super super = new Sub();  // 向上造型
      
          Sub sub = (Sub) new Super();  // 向下造型
        }
      }

      注意

      向下造型是不允许的,上面的例子会通过编译器检测,但运行后会抛出异常:
      Output

      java.lang.ClassCastException: class Super cannot be cast to class Sub
    • 使用向上造型时,编译期只检查两个类之间的关系不检查具体用哪个类创建对象

    • 对象方法看父类,方法内容看子类。

  • 方法覆盖 (Method Override)

    • 又称方法重写,是行为多态

    • 发生在父子类中,双方拥有方法签名完全一致的非静态方法。

    • 调用方法的时候,调用的是重写后的方法。

    注意

    1. 子类重写的方法权限修饰符需大于等于父类

    2. 关于返回值类型

      • 父类方法返回值类型是引用数据类型,则子类重写的方法的返回值类型是父类方法返回值类型的子类或其本身。

      • 父类方法返回值类型是基本数据类型,则子类重写的方法返回值类型必须和父类方法相同。

    3. 子类抛出的异常需为父类异常的子类。

    Input

    java
    class A {
      /**
      * 方法 mA
      * @return void
      */
      public void mA() {
        // ...
      }
    }
    
    class B extends A {
      /**
      * 方法 mB
      * @return void
      */
      public void mB() {
        // ...
      }
    }
    
    /**
      * 方法 m
      * @return B
      */
    class C {
      public B m() {
        // ...
      }
    }
    
    class D extends C {
      /**
      * 方法 m
      * @return A
      */
      public A m() {
        // ...
      }
    }
    
    ...
    
    public static void main(String[] args) {
      // 用C类声明,用D类创建对象
      // 则对象c拥有返回值类型为B的方法m()
      C c = new D();
    
      // 用B类声明一个变量b,来接收方法的返回值
      // 方法调用的是D类中的m()
      // 由于D类中m()的返回值类型是A,故最终是用子类接收父类对象
      B b = c.m();
    
      // 对象b无法调用B类中的mB()
      // b.mB();
    }