我的同事刚问我一个非常有趣的问题,我不能给他一个答案。
我们假设我们有以下课程:
public class Person {
String name;
public Person(String name) {
this.name = name;
}
public void print() {
System.out.println("xxx");
}
}
现在,我们正在创建对象:
Person p1 = new Person("a");
Person p2 = new Person("b");
Person p3 = new Person("c");
Person p4 = new Person("d");
Person p5 = new Person("e");
Person p6 = new Person("f");
Person p7 = new Person("g");
Person p8 = new Person("h");
问题是:
我们是否保留每个单个对象中可用方法的信息?如果我们创建一个新对象p9
,JVM是否会创建只包含字段信息的对象,还是会向该对象添加有关方法的信息?
另一个问题:
如果我调用p1.print()
会怎样? p1
是否必须要求Person
类提供此方法,或者它是否已保存在p1
对象中?
答案 0 :(得分:5)
对于所有实例,方法的代码不会重复,这是完全没有必要的。代码位于内存中的特殊区域,并由所有实例共享。另一方面,实例变量所需的内存自然归每个实例所有。
关于如何调用方法,对象实际上并不需要在每次调用方法时都要求类,它有一个指向方法代码的指针,并且可以立即调用它。
有关JVM内部工作方式的更多信息,请参阅:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html
答案 1 :(得分:2)
通常,代码只表示一次,无论有多少个对象。对象“拥有”方法的OO概念只是一种抽象。因此,从本质上讲,无论你的方法是实例还是静态,在幕后它仍然“属于”类。
对于最终方法,调用使用静态绑定(已经预先确定将根据表达式的类型调用哪个方法)。
对于虚拟方法,会发生更有趣的事情。根据实现,对象包含正确方法的地址(不再是方法本身),或者运行时反映该对象以确定其实际类并在层次结构中找到适当的方法
答案 2 :(得分:2)
我不知道Oracle JVM是如何做到的,但是在一般的面向对象编程系统中,一个对象有一个隐藏的实例变量,指向它的类。所有实例都指向同一个类对象,指向实例方法的指针是类的一部分。
答案 3 :(得分:1)
通过不将对象和方法绑定在一起,JVM做得非常好。要清楚地了解它的功能,您需要了解它的底层架构。
JVM有一个类加载器系统,它由以下资源组成:
当我们创建一个对象时,类被加载(延迟加载)并获得所有必需的资源。在方法区域中,所有方法驻留在一起,对象驻留在堆中,并在所有线程之间共享。 JVM将执行程序所需的所有内存分配/组织到多个运行时内存区域。