类继承(多态+动态绑定)
韩顺平 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)->e
复制代码
多态
编译类型为父类,运行类型为子类- 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()
- }
- @endumlenduml
复制代码
测试- 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 想要访问子类的属性/独有方法, 向下转型
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |