“在初学Java这类面向对象语言时,其中一条规则就是私有(private)成员变量不能被继承,只能由父类提供相应的方法,子类通过调用该方法访问父类的private变量。但是通过HSDB这款JDK自带的运行时状态查看工具,却发现了private修饰的成员变量也是可以被子类继承的。
”
测试代码验证
public class Test03Class {
public static void main(String[] args) throws IOException {
Son son = new Son();
son.info();
}
}
class Father {
private int money1 = 1;
private int money2 = 2;
private int money3 = 3;
public void info() {
System.out.println(money3);
}
}
class Son extends Father {
private int money3 = 4;
private int money4 = 5;
}
测试代码很简单,父类Father中定义了money1、money2、money3,3个int类型的成员变量,并提供了info方法让子类或其他类访问money3这个变量。子类Son中同样定义了money3、money4,2个int成员变量。mian方法创建一个son对象并调用了子类继承下来的info( )方法,大家可以先猜测son调用info方法后,控制台输出什么内容。
接下来在main方法的末行打下断点让程序处于运行状态后打开HSDB。HSDB工具被放在本地jdk安装目录中的lib包中,进入lib目录下后执行以下启动命名
java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

在输入栏中键入需要监控的Java进程ID号,我们可以通过jps命令获得。

输入16468后就进入了主界面

可以看到此时运行的JVM程序有5个线程,Finalizer垃圾回收线程、Reference Handler虚引用内存回收线程还有main主线程等等。
接着点击Tools->Object Histogram 功能按键查看全部对象并找到son对象

双击所选条目并点击Inspect后就能看到son对象在堆中的内存布局

Java对象在JVM运行时会被抽象成一个C++的Oop对象,Oop结构中前两个是_mark和 _metadate_Klass,也就是我们熟悉的Java对象头。接着就是在开篇测试代码中Father类和Son类中定义的5个成员变量。
可以发现子类中开辟了对应的内存空间存储了父类的私有成员变量money1、money2。并且Father中定义的money3=3同样也存在Oop中,没有被覆盖掉子类的的money3=4所覆盖掉。
子类为什么需要继承父类全部的成员变量
解答这个问题,我们可以先回答上文提出的"son调用info方法后,控制台输出什么内容"这个问题。答案是3,也就是son对象并没有输出自己的money3=4这个结果,而是输出了父类的money3=3结果。子类需要继承父类全部的成员变量的原因也就在于此。Java在方法调用时,需要查看该方法入口是所属哪一个类的,比如info( )方法,由于Son没有重写Father的info( )方法,因此采用了Father类中info的方法入口。而输出3的结果告诉我们,方法调用时如果访问了成员变量,则访问的是该方法所属类的成员变量。
如果Son重写了info( )方法,那么输出结果就是4了
class Father {
private int money1 = 1;
private int money2 = 2;
private int money3 = 3;
public void info() {
System.out.println(money3);
}
}
class Son extends Father {
private int money3 = 4;
private int money4 = 5;
// main方法中son.info()测试输出的结果就是4
public void info() {
System.out.println(money3);
}
}
有了上述方法调用访问成员变量的规则后我们再解答子类为什么需要继承父类全部的成员变量。假设如果子类不继承父类的全部成员变量,而Son类中没有重写info( ) 方法那怎么样才能让控制台输出3呢?
我们知道给普通成员变量(非static、final修饰)显示赋值发生在对象创建时,也就是new Father( )时会给成员变量显示赋值。而假设此时Son没有继承money3,则必须先new一个Father对象显示给money3=3赋值后,再想办法让son对象获取该father对象然后获取money3=3,最后控制台输出。
这个过程看起是不是有点傻?为了访问一个成员变量还有new出一个对象,一个Java对象开启指针压缩最少都需要16byte内存空间,而一个int只需4byte,怎么算都是直接父类成员变量放在放在子类中划算,JVM也是这么做的,也就有了HSDB中可以看到父类全部的成员变量都被子类继承了。
总结
通过HSDB工具验证了Java字段的继承关系:父类的成员变量无论被访问权限(private、protected)是什么都会被子类继承,即使子类出现变量名一模一样的变量也不会覆盖继承下来的父类成员变量。但是,虽然子类继承了父类全部的成员变量,但是对于私有的成员变量是没有权限直接访问的,只是在子类对象内存中占据着,等待调用父类方法时才能访问该内存空间。




