魔鬼调用"/>
java 继承 多态 this super 结合魔鬼调用
本来就是刷一个题 结果给我整晕了特此记录一下
先说结论: java 多态指的是运行时多态和编译时多态,编译时的多态呢比较好理解就是
根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
运行时多态:也叫作动态绑定,一般是指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象。这个是jvm 有一套规范的 一般我们听到的是 若是字类复写了父类的方法则优先调用字类的方法,否则调用父类的方法, 但是这个调用这要时刻记住(即是this 当前指向誰)否则就是下面这个题目直接把你绕傻
本来的题目是这样的; 问最后打印的是三个啥值
class Test {public static void main(String[] args) {System.out.println(new B().getValue());}static class A {protected int value;public A (int v) {setValue(v);}public void setValue(int value) {this.value= value;}public int getValue() {try {value ++;return value;} finally {this.setValue(value);System.out.println(value);}}}static class B extends A {public B () {super(5);setValue(getValue()- 3);}public void setValue(int value) {super.setValue(2 * value);}}
}
这里贴上我自己的理解 后面写晕了可能有不对的但是最底下有大神的解释可以自己去看,反正我是不想看了
package cn.source.utils;/*** @ClassName: aaa* @Description: TODO* @author: human* @date: 2021-03-10 10:10*/public class aaa {public static void main(String[] args) {//程序的入口// 1 执行这个 new B() 这个动作;System.out.println(new B().getValue());}static class A {protected int value;//2 new B 之前 先执行A的构造方法public A(int v) {// 执行a 自己的setvaluesetValue(v);}// 5 第一次执行 set value 方法//13 第二次执行 setvalue 方法//20 第三次执行 setvalue 方法public void setValue(int value) {// 6 此时的value =2*5// 14 此时的value= 11*2//21 value = 2*8 16this.value = value;}//8 第一次被调用用 此时value 等于10//// 第二次被调用 此时value 等于16// 第三次被调用 此时的值是34public int getValue() {try {//9 10+1 =11value++;// 10 return 的作用 保留当前的这个值11但是必须把finally块儿里边的代码走完 11return value;} finally {//11// 真的狗 this的作用誰调用我,我就是誰 此时this字类B对象,然后。setValue 方法// 这个时候value 等于11 调用字类的方法 逻辑自己去看 这个时候设置的value 34 17this.setValue(value);//15 所以第一次在这个地方打印的就是22// 所以在这个地方打印的就是34// 最后一次调用打印的是17System.out.println(value);}}}static class B extends A {// 实例化b 对象执行改构造方法public B() {// 2// 显示的写了调用父类的构造方法,// 但是父类的构造方法里边是调用的setValue方法;// 此时运行时多态因为字类重写了父类的这个setValue 所以调用的时自己的 setValue 方法// 自己的setValue在内部还是调用的父类的setValue方法 通过super关键字显示的调用 只是参数变了 把这个值给了过去super(5);// 7//这个时候显示的调用了自己的setValue方法,//字类没有重写父类的getValue方法,运行时多态调用的是父类的getValue方法// get value 方法执行完毕之后 return 会保留当前的值 所以此时(11-3)8// 设置完毕之后value的值为16//16 getvalue 返回值为11//17 执行 11-3 =8setValue(getValue() - 3);}// 3 执行自己的 set value 方法//11 第二次执行自己的set value方法// 18 第三次执行自己的setvalue 方法此时的值为8public void setValue(int value) {// 4 在内部还是调用的父类的方法 只是参数变了 此时传给父类的set value 方法的参数是2*5//12 现在的值是 2* 11//19 调用父类的构造方法 此时传入的值为 2*8super.setValue(2 * value);}}
}
思考和解决这个题的主要核心在于对java多态的理解。个人理解时,执行对象实例化过程中遵循多态特性 ==> 调用的方法都是将要实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法。
Step 1: new B()构造一个B类的实例
此时super(5)语句调用显示调用父类A带参的构造函数,该构造函数调用setValue(v),这里有两个注意点一是虽然构造函数是A类的构造函数,但此刻正在初始化的对象是B的一个实例,因此这里调用的实际是B类的setValue方法,于是调用B类中的setValue方法 ==> 而B类中setValue方法显示调用父类的setValue方法,将B实例的value值设置为2 x 5 = 10。
紧接着,B类的构造函数还没执行完成,继续执行setValue(getValue()- 3) // 备注1语句。
先执行getValue方法,B类中没有重写getValue方法,因此调用父类A的getValue方法。这个方法比较复杂,需要分步说清楚:
调用getValue方法之前,B的成员变量value值为10。
value++ 执行后, B的成员变量value值为11,此时开始执行到return语句,将11这个值作为getValue方法的返回值返回出去
但是由于getValue块被try finally块包围,因此finally中的语句无论如何都将被执行,所以步骤2中11这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
这里有很重要的一点:finally语句块中 this.setValue(value)方法调用的是B类的setValue方法。为什么?因为此刻正在初始化的是B类的一个对象(运行时多态),就像最开始第一步提到的一样(而且这里用了使用了this关键词显式指明了调用当前对象的方法)。因此,此处会再次调用B类的setValue方法,同上,super.关键词显式调用A的setValue方法,将B的value值设置成为了2 * 11 = 22。
因此第一个打印项为22。
finally语句执行完毕 会把刚刚暂存起来的11 返回出去,也就是说这么经历了这么一长串的处理,getValue方法最终的返回值是11。
回到前面标注了 //备注1 的代码语句,其最终结果为setValue(11-3)=>setValue(8)
而大家肯定也知道,这里执行的setValue方法,将会是B的setValue方法。 之后B的value值再次变成了2*8 = 16;
Step2: new B().getValue()
B类中没有独有的getValue方法,此处调用A的getValue方法。同Step 1,
调用getValue方法之前,B的成员变量value值为16。
value++ 执行后, B的成员变量value值为17,此时执行到return语句,会将17这个值作为getValue方法的返回值返回出去
但是由于getValue块被try finally块包围而finally中的语句无论如何都一定会被执行,所以步骤2中17这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
finally语句块中继续和上面说的一样: this.setValue(value)方法调用的是B类的setValue()方法将B的value值设置成为了2 * 17 = 34。
因此第二个打印项为34。
finally语句执行完毕 会把刚刚暂存起来的17返回出去。
因此new B().getValue()最终的返回值是17.
Step3: main函数中的System.out.println
将刚刚返回的值打印出来,也就是第三个打印项:17
最终结果为 22 34 17。
更多推荐
java 继承 多态 this super 结合魔鬼调用
发布评论