天空闲话 发表于 2022-8-9 14:47:34

类继承(多态+动态绑定)

类继承(多态+动态绑定)

韩顺平 Java P307-P318
继承

以Object类为基类(Base) 向下继承 使用关键字 extends
class Father {}
class Child extends Father {}子类继承父类所有可公开的属性、方法,规则属性、方法的修饰关键字
访问级别关键字同类同包子类不同包公开public√√√√受保护protected√√√×默认√√××私有private√×××属性、类的查询原则

在继承的逻辑下,访问属性、方法时,遵循查询原则
从当前类查找同名的属性、方法,如果找到,则访问、使用
md 流程图 flowchat 案例
http://flowchart.js.org/
st=>start: Start
e=>end
init=>operation: 在当前(运行)类查找方法/属性
findInRun=>condition: 当前类中找到了?
findInFather=>condition: 当父类中找到了?
isReadable=>condition: 可访问?
isObject=>condition: 查找到Object?
useIt=>operation: 访问/使用
notUseIt=>operation: 不可访问/使用 抛异常
findGrand=>operation: 继续向上级父类查找
st->init->findInRun
findInRun(yes)->useIt->e
findInRun(no)->findInFather
findInFather(yes, bottom)->isReadable
findInFather(no,right)->findGrand(right)->isObject
isObject(no,top)->findInFather
isObject(yes,bottom)->notUseIt
isReadable(yes,left)->useIt
isReadable(no,bottom)->notUseIt(left)->ehttps://images.cnblogs.com/cnblogs_com/blogs/722367/galleries/2179874/o_220624120223_Snipaste_2022-06-24_20-01-14.png
多态

编译类型为父类,运行类型为子类
Father father = new Child child();
// = 为分隔符
// 左侧为编译类型
//                   右侧为运行类型向上转型


[*]本质: 父类的引用指向子类的对象
[*]语法 父类类型 引用名 = new 子类类型()
[*]特点:

[*]编译类型看左边,运行类型看右边
[*]可以调用父类中的所有成员(遵循访问权限)
[*]不能调用子类中的特有成员
[*]最终运行效果看子类的具体实现(理解为有重写的使用重写,没有则向上查找)

向下转型


[*]语法: 子类类型 引用名 = (子类类型) 父类引用
[*]只能转换引用,对象不发生改变
[*]父类的引用必须指向要转化的对象

[*]也即,向上转型中,左右类型互相调换,不可替换为继承树中间的类
[*]例如 Father obj = new Child()
[*]那么只能转为 Child obj_trans = (Child) obj

[*]向下转型后,可以调用子类类型中的所有成员
多态案例

类定义
class Animal {
    public void cry() {
      System.out.println("Animal cry() 动物在叫....");
    }
}

class Cat extends Animal {
    public void cry() {
      System.out.println("Cat cry() 小猫喵喵叫...");
    }
    public void eatMouse() {
      System.out.println("Cat eatMouse() 小猫吃老鼠...");
    }
}

class Dog extends Animal {}测试用例
    public static void main(String[] args) {

      /*animal 编译类型就是 Animal , 运行类型 Dog */
      Animal animal = new Dog();
      /*animal 运行类型是 Dog
      遵循查询原则, 从Dog向上逐级查找 cry() 方法
      Dog 中没有 cry() 则向上 找到 Animal.cry() */
      animal.cry(); // Animal cry() 动物在叫....

      animal = new Cat(); //animal 编译类型 Animal,运行类型就是 Cat
      animal.cry(); // Cat cry() 小猫喵喵叫...

      /* 在编译阶段(写代码时),能调用哪些成员,是由编译类型来决定的 */
      animal.eatMouse(); // 报错,无法编译

      /* 如果希望在多态中使用子类的方法,则向下转型 */
      ((Cat)animal).eatMouse;// Cat eatMouse() 小猫吃老鼠...
    } 动态绑定

在使用多态时,将使用动态绑定原则,该原则与上文的查询原则不冲突

[*]当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
[*]当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
总结:
默认使用向上转型 父类类型 引用名 = new 子类类型()

[*]对于方法

[*]遵循向上转型规则,不可使用子类的特有方法
[*]当使用父类子类都有的方法时,可以使用,按子类的重写去执行
[*]当子类无此方法时,沿继承树向上查找

[*]对于属性

[*]遵循向上转型规则,不可使用子类的特有属性(重名属性也不使用,但内存中存在)
[*]方法所在的类使用所在类的属性值,与编译类型无关

案例
class A {
    public int i = 10;
    //动态绑定机制:
    public int sum() {
      return getI() + 10;//20(子类有getI 按重写处理) + 10
    }
    public int sum1() {
      return i + 10;//10(获取的是当前类的i = 10) + 10
    }
    public int sum2() {
      return -1;
    }
    public int getI() {
      return i;
    }
}
class B extends A {
    public int i = 20;
    public int sum2() {
      return i + 1;
    }
    public int getI() {
      return i; // 获取的是当前类的 i = 20
    }
}umlPlant 类图画法 https://plantuml.com/zh/class-diagram
@startuml
Title "绑定案例"
class A {
    + int i = 10
    + int sum()
    + int sum1()
    + int sum2()
    + int getT()
}

class B extends A {
    + int i = 20
    + int sum2()
    + int getT()
}

@endumlendumlhttps://images.cnblogs.com/cnblogs_com/blogs/722367/galleries/2179874/o_220624120243_Snipaste_2022-06-24_20-00-15.png
测试
    public static void main(String[] args) {
      A obj = new B();
      // 方法
      int res1 = obj.sum();// 30
      int res2 = obj.sum1(); // 20
      int res3 = obj.sum2(); // 21 不是特有方法, 按重写处理
      // 属性
      int res4 = obj.i;      // 10 多态, 不访问子类的独有
      int res5 = ((B)obj).i; // 20 想要访问子类的属性/独有方法, 向下转型

    }
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 类继承(多态+动态绑定)