我理解通常给出的抽象解释是为了让你正确使用这些东西,但是JVM是如何做到的。
答案 0 :(得分:17)
当一个对象被实例化时,实际上只有非静态数据被“创建”,并且引用了创建它的对象类型。
没有任何方法被复制过。
创建它的类的“引用”实际上是一个指针分派表。每个可用于该类的方法都有一个指针。指针始终指向方法的“正确”(通常是对象树中最低/最具体的)实现。
这样,如果您对另一个方法进行了顶级调用,但是另一个方法已被覆盖,则会调用重写的方法,因为这是表中指针指向的位置。由于这种机制,调用重写方法不应该花费更多时间而不是顶级方法。
指针表+成员变量是类的“实例”。
变量问题与完全不同的机制“名称空间”有关。变量根本不是“Subclassed”(它们不会进入调度表),但公共或受保护的变量可以被局部变量隐藏。这完全由编译器在编译时完成,与运行时对象实例无关。编译器确定您真正想要的对象,并将对它的引用填入您的代码中。
范围规则通常倾向于“最近”变量。任何远离同名的东西都会被忽略(阴影),而不是更接近的定义。
如果您感兴趣,可以更加具体地了解内存分配:所有“OBJECTS”都分配在“Heap”上(实际上比真正的堆更高效,更漂亮,但同样的概念。)变量总是指针--Java永远不会复制对象,你总是复制指向该对象的指针。方法参数和局部变量的变量指针分配在堆栈上完成,但即使在堆栈上创建变量(指针),它们指向的对象仍然永远不会在堆栈上分配。
我很想写一个例子,但这已经太久了。如果你想让我输出一些扩展关系的类,以及他们的方法和数据如何影响生成的代码,我可以......只是问。
答案 1 :(得分:2)
我想你会发现这是一个全面的例子:
http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
答案 2 :(得分:0)
从堆中分配内存以保存对象及其超类的所有实例变量和特定于实现的数据。特定于实现的数据包括指向类和方法数据的指针。
对象的实例变量初始化为默认值。
调用派生程度最大的类的构造函数。构造函数做的第一件事是调用构造函数的大写。这个过程一直持续到调用java.lang.Object的构造函数为止,因为java.lang.Object是java中所有对象的基类。
< / LI>在执行构造函数体之前,将执行所有实例变量初始值设定项和初始化块。然后执行构造函数的主体。因此,基类的构造函数首先完成,最派生类的构造函数最后完成。