最近在进行知识回顾的时候发现很多知识只是了解了如何去使用,根据what、why、how去分析发现why(为什么)和how(怎么是)层次理解薄弱。
java的特点之一继承,大家都知道什么是继承和为什么要用继承,但是java中怎么实现的继承呢?
子类对象中会保存一个实例对象的引用super,该引用指向其父类。 在实际的方法调用时,java会先在当前类的对象中寻找名称相同的方法,如果没有,就到super引用的父类对象中去寻找该方法,所以,若在子类中存在和父类方法的签名和返回值类型完全相同的方法(重写)的话,java就会直接调用该对象的方法而不用去父类去寻找调用方法了。而且在子类对象中,可以直接通过super来调用父类对象中的方法 。
结合以上文字描述和图片能清晰的了解继承实现原理。
接下来通过代码。
public class Test {
public static void main(String[] args) {
B b = new B(0);
int y = b.getY();
}
}
class A{
public static int x=2;
private int y=2;
protected int z;
A(){
x=x+66;
showX();
}
A(int n){
x=n+66;
showX();
}
public void showX(){
System.out.println("A.x="+x);
}
public int getY(){
return y;
}
}
class B extends A{
B(int x){
this.x=x+66;
showX();
}
public void showX(){
System.out.println("B.x"+x);
}
public int getY(){
System.out.println("B.Y="+(super.getY()+x));
return super.getY()+x;
}
}
以上代码输出的结果为:
B.x68
B.x66
B.Y=68
是不是和预想的不太一样,请看以下解释:
1:B b = new B(0);实例化一个B,首先调用的是父类A的无参构造函数,此时x的值为68,之后调用A无参构造函数中的showX()方法,这里是最可能出现异议的地方,它调用的是B类的showX(),具体原因大家可以了解,本人觉得根据上图所示,虽然showX()在A类的无参构造函数中,但实际调用时还是先走B类的方法,B类没有改方法才会找父类的方法。
2:调用B类带参数的构造函数,此时this.x为68将A类的x属性继承,但是经过赋值运算,x变为66,注意A类的x和B类的x均为66,由于B类没有重写x的值,寻找过程就为find B 类的x,有就使用B类的x,没有往父类上找x,找到了此时this.x指向父类的x,修改的也是父类的x值。
3:虽然y在A类上是私有的,但B类还是能通过super.getY()方法获取y的值。
以上均为三行输出值的具体分析。
接下来还有一个代码例子:
abstract class C{
int i=9;
public void printI(){
System.out.println("i="+i);
}
}
public class D extends C{
int i=2;
public static void main(String[] args){
D d = new D();
d.printI();
}
}
输出结果为:i=9
好像和上面讲的不太一样,不应该为i=2?
分析如下:由于D类中没有printI方法,实际调用的为父类C的printI方法,C类中的printI方法只会调用C类中的成员变量或方法中的局部变量所以输出的结果为:i=9。
jvm的执行过程
(1)子类D 的构造方法被调用,实例化一个D对象,D对象的成员被初始化
(2)jvm隐含的调用父类的构造方法,实例化一个C对象,C对象的成员被初始化。
(3)由于C对象的printI()未被屏蔽,所以调用的C对象的printI()函数。
那么,在这里A的成员函数当然是访问自己的成员变量了。
代码也可以这么写:
package com.wupeng;
class C{
int i=9;
public void printI(){
System.out.println("i="+i);
}
}
public class D extends C{
int i=2;
public void printI(){
super.printI();
}
public static void main(String[] args){
D d = new D();
d.printI();
}
}