当Java中的类A继承自类B时,堆中会发生什么?

9

在Java中,假设我们有两个类AB,其中B继承了A,而A具有三个私有字段和一个带有三个参数的构造函数:

public class A {
private int a ;
private int b ;
private int c ;

 public A(int a, int b, int c) {
    this.a = a;
    this.b = b;
    this.c = c;
 }
}

这里是类B

public class B extends A {        

  public B() {
    super(1,2,3);
 }
}

我们考虑以下测试类。
public class TestA {

        public static void main(String[] args) {
            A a = new A(1,2,3);
            B b = new B();        
        }
    }

问题是,当创建带有私有字段的类A并将其继承到类B时,堆中发生的有序过程是什么?当创建这两个类的实例时,堆中会发生什么?内存分配如何发生,类在计算机内存中如何交互?
我们也知道子类无法继承其超类的私有字段,那么当调用构造函数B()时会发生什么?

B类将无法编译。 - BetaRide
1
@BetaRide 我知道 B 类不会编译。我不是在问它是否能编译。我想知道堆内存层面上这些类之间的关系。 - pentanol
1
尽管子类不会“继承”超类的私有字段,但这些字段仍然存在(并占用空间)于对象中,并且当B的构造函数调用超类构造函数(无论是隐式还是显式)时,这些字段将在某个时刻被初始化。B中的方法可以在同一实例上调用超类方法,并且必须能够访问超类中的私有字段。“不继承私有字段”只是意味着这些字段在B中的代码中不可见。但它们仍然存在。 - ajb
@ajb 你的意思是B中的代码看不到它,但可以通过A中的代码访问它。我理解得对吗? - pentanol
1
很多关于这个的细节都是高度实现特定的。JLS指定了必须发生的“什么”,但没有说明“如何”。你从sharp edge那里接受的答案包含几个明显错误的地方,应该被取消接受。例如,JLS没有提到类应该加载到哪里,在Open/OracleJDK 7及以下版本(全球最流行的JVM之一)中,类被加载到堆中,尽管在一个名为permgen的私有区域中,用户代码无法直接触及它。 - kittylyst
显示剩余4条评论
1个回答

5

类对象与其他对象一样被加载到堆中。这个对象只是代表类。

Oracle官方“了解内存指南”

以及古老的Java规范,你可以阅读整个文档以了解类加载器的工作原理,你不会找到任何“类在堆中加载”的内容。此外,您可以在互联网上进行一些初步搜索以获得进一步的澄清。

Class B将会完美编译。

现在按照顺序回答你的问题:

创建私有字段的A类并让B类继承它的堆中有什么有序过程发生?

无法确定它们的顺序,因为这取决于JVM如何管理它们,私有成员不会被继承,但存在于父类(超类)的实例化对象中。

换句话说,子类的实例将具有父类的私有字段的副本。但是,它们不会对子类可见,因此访问它们的唯一方法是通过父类的方法。

创建这两个类的实例时,在堆中会发生什么?

通常,序列将类似于在创建AB的实例后:

  1. 引用A.class对象(在创建A类实例之后)
  2. 引用B.class对象(在创建B类实例之后)
  3. 对象实例变量块
  4. A实例变量块(只有a、b、c)
  5. B实例变量块(在这种情况下没有)

不过,每个JVM的实现都可以自由选择如何分配它们。

我们还知道子类不能继承其超类的私有字段,那么当调用构造函数B()时会发生什么?

当你调用 B() 时:

B b = new B();

它将调用super(1,2,3)

那么之后会发生什么?传递给super();的值将传递给A(int a, int b, int c),然后分配给A的实例变量,但这并不意味着那些私有字段现在可供B使用。你只是传递了值给超类构造函数而已。

--编辑--

如果您想更好地理解堆和栈,请参见此问题

--编辑-2--

如果您花些时间研究这个维基百科,它包括JVM的所有内容,包括堆和其他内存结构中的过程

--最终编辑-3--

关于OP评论中关于Super类的私有成员

看看这个 这个问题的答案将澄清您对继承成员和不可访问私有成员的困惑,因为子类实例是父类的实例,它的实例具有父类的所有字段!私有成员对该子类不可见!!这就是您所引用的JLS规范!它们占据对象内部的空间。它们对子类不可见,但它们在该实例内部存在。


我已经在询问JVM的过程是什么。JVM进程不是随意的,它是有组织的。 - pentanol
JVM 的处理顺序并不重要,因为它可以因 JVM 的不同实现而有所不同。JVM 的典型过程在我的第二个回答中 :) - Sharp Edge
非常感谢 Sharp 的有益回答。但问题已经涉及到 JVM 进程的过程。 - pentanol
1
Sharp edge先生,这里有一些混淆。您说私有字段由子类继承,并且只能由超类的构造函数访问。但是根据JAVA API,它们根本没有被继承。您能否向我展示一个表明私有字段由子类继承的来源? - pentanol
1
@pentanol,我从未说过子类继承了超类的私有成员变量!!请再次查看我的回答,我说的是子类的对象包含其超类的私有字段。子类本身并不知道其超类的私有字段。我知道这听起来很混乱,但是子类对象具有超类成员(私有或公共),但是私有成员不可见。就是这样。 - Sharp Edge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接