如果将非static
函数复制到具有该方法的每个对象的堆中,那么为什么默认情况下不是Java static
中的所有方法?为什么要以这种方式浪费所有堆内存?
图解说明对我理解这一点更有帮助。
答案 0 :(得分:3)
通常,通过将方法复制到堆上每个对象一次,Java方法不实现。相反,方法通常使用称为虚拟功能表(或“vtable”)的东西来实现。这个想法是每个方法只有一个副本,无论是静态方法还是非静态方法,并且指向这些方法的指针都放在表格中。然后,堆中的每个对象都存储一个指向vtable的指针,指向其对象类型。这意味着任何堆对象的大小都不依赖于对象具有的方法数。实际上,具有100个方法的对象与具有1个方法的对象的大小相同(假设它们具有相同的字段)。每个只存储一个指向vtable的指针,指向其对象类型,其中只有一个副本。
此优化最初用于C ++以支持快速虚拟功能,并且已经在许多其他面向对象的语言中使用。它允许对象很小,但支持动态调度。
换句话说,默认情况下,方法不需要static
,因为它们不会影响堆中对象的大小。对于具有更多函数的对象,创建对象不需要更长时间,或者它使用更多堆空间。
这是一些可能的一些对象布局图(ASCII艺术的道歉!)。假设我们有两个类,A和B.然后在内存中,这些类型的对象可能如下所示:
A vtable for A
+-------------+ +---------------+
| vtable ptr | --+-> | method one |
+-------------+ | +---------------+
| | | | method two |
| fields of A | | +---------------+
| | | | ... |
+-------------+ | +----------------
| | method N |
A | +---------------+
+-------------+ |
| vtable ptr |---+
+-------------+
| |
| fields of A |
| |
+-------------+
B vtable for B
+-------------+ +------------+
| vtable ptr | --> | method one |
+-------------+ +------------+
| | | method two |
| fields of B | +------------+
| | | ... |
+-------------+ +------------+
| method M |
+------------+
注意类型A的两个对象如何共享相同的vtable,以及类型A和B的对象如何仅为其vtable指针使用相同数量的空间,即使它们具有不同数量的方法。
答案 1 :(得分:1)
静态方法无法访问对象的实例成员变量......没有状态,没有OOP。
答案 2 :(得分:1)
您的问题是在程序编程和面向对象编程之间进行比较。 http://en.wikipedia.org/wiki/Procedural_programming#Comparison_with_object-oriented_programming。面向对象与程序的主要动机是使数据结构能够自行执行操作。
关于内存分配的假设,方法不会复制到每个对象,因为Java不支持动态类型修改。例如,如果对象的类型为Foo,那么它将具有由Foo类型声明的所有方法。在不更改类型本身的情况下,无法将新方法添加到Foo的实例。每当在一个对象上调用一个方法时,它就会在后面作为一个过程运行。
无论何时运行:
foo.say( "Hello, world!" );
Java实际上是这样的:
foo
声明的类型。say(String)
的方法。foo
对象的实例状态运行该方法。因为方法保持自己的状态,所以通过将对象实例作为方法参数传递,可以以静态方式实现任何非静态方法。实际上,上面的步骤4很可能是由Java编译器以这种方式实现的。
答案 3 :(得分:1)
不会“为堆上的每个对象复制”非静态方法。每个类加载器只有一个方法(代码)的副本,与static
方法的副本相同。
使方法static
与内存管理无关 - 静态方法需要一些堆栈和堆,就像动态方法一样。
有关static
用途的说明,请参阅Understanding Instance and Class Members Java教程。
答案 4 :(得分:0)
因为如果每个方法都是静态的,那么java就不会是面向对象的。
您需要在不同的对象上调用方法,因为它们具有不同的状态。
至于内存 - 每个静态调用也会进入堆栈。
答案 5 :(得分:0)
该方法的代码不在堆栈中,它位于堆的一部分中(永久生成,由垃圾收集器特殊引用)。在堆栈上只定位了方法的局部变量(以及类似的运行时数据,如返回地址)。
这与静态或非静态方法无关。
此外,该方法的代码将不为该类的每个对象复制 - 只是在执行该方法时,它会获得一个额外的参数,该参数指向调用它的对象。这个参数是静态和非静态方法之间唯一的内存开销。