Java子类真的不能继承父类的Private成员吗?
从我们学习面向对象程序编程开始,就被告知,子类继承父类,只能继承父类的public,proteced 成员,对于private成员,是不能被继承的。 那么事实真的是如此吗?
我们来验证一下,这里我们使用了Ehcache的SizeOf工具,可以得知对象的大小
import net.sf.ehcache.pool.sizeof.AgentSizeOf;
import net.sf.ehcache.pool.sizeof.SizeOf;
public class ClassA{
}
public class ClassB extends ClassA{
public static void main(String[] args) {
SizeOf size = new AgentSizeOf();
ClassA a = new ClassA();
ClassB b=new ClassB();
System.out.println("ClassA size = " + size.sizeOf(a));
System.out.println("ClassB size = " + size.SizeOf(b));
}
}
这段程序运行的结果,classA是16字节,ClassB是16字节。 为什么ClassA和ClassB明明是空对象,却占用了16字节空间呢?
答案很简单,在64位平台上,如果开了指针压缩,则会占用12字节,否则是16字节。 而这个大小其实是对象头的大小。 对于类实例对象来说,
对象头包含两部分,分别有一个_mark和_metadata。
为什么会有对象头呢,对象头其实是继承于JVM内部的OopDesc对象, JVM内部采用了oop-klass这种二分模型,包含继承于oopDesc的对象头,用于描述对象信息,而klassKlass区域,则储存着实例数据。
Oop对象头 (_mark,_metadata) |
---|
实例区数据1 |
实例区数据2 |
实例区数据3 |
.......... |
_mark存储了JVM内部对象的哈希值,锁状态等信息。
_metadata则是一个指针,指向klassOopDesc类型实例。
所以我们签名写的ClassA和ClassB 明明是空对象,却占用16字节空间。 在开启了指针压缩之后,其实占用大小为12字节,但是JVM进行了内存对齐,会填补4个空白字节。所以大小也就成了16字节。
上面的例子只是描述了Java对象在JVM内部的存储模式。 那么Private成员到底会不会被继承呢? 下面再写一个例子
public class ClassA{
private double value1=10;
private double value2=12;
}
public class ClassB extends ClassA{
public static void main(String[] args) {
SizeOf size = new AgentSizeOf();
ClassA a = new ClassA();
ClassB b=new ClassB();
System.out.println("ClassA size = " + size.sizeOf(a));
System.out.println("ClassB size = " + size.SizeOf(b));
}
}
这次打印的结果变成了,两个size都是32,说明无论是ClassA还是ClassB都是有这两个double类型的字段的。 这说明子类是可以继承父类的所有成员的,包括私有方法,构造方法,静态方法,或者是被final修饰的方法等等。 但是由于被private修饰,导致不能在子类访问这个成员。 private的限定其实也非常明确,就是这个类本身的范围,子类并不属于这个父类的范围,所以不能访问。 而java也完全可以通过反射来访问父类的私有成员 下面是例子。
public class ClassA{
private double value1=10;
private double value2=12;
}
public class ClassB extends ClassA{
public static void main(String[] args) {
Class b=ClassB.class;
Class a=b.getSuperclass();
try {
Field[] fields= a.getDeclaredFields();
AccessibleObject.setAccessible(fields,true);
for(Field field :fields){
field.setAccessible(true);
System.out.println("ClassA value = " + field.get(new ClassB()).toString());
System.out.println("ClassB value = " + field.get(new ClassA()).toString());
}
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
最后的结果是: ClassA value = 10.0 ClassB value = 10.0 ClassA value = 12.0 ClassB value = 12.0
总结
Java在继承中,会继承所有的父类成员,即使这个成员被private修饰。在内存中,是可以通过大小明确验证成员的存在的。而想要访问父类的隐藏成员,可以通过反射这一机制实现。
- 0
- 0
-
分享