【问题标题】:Why isn't every method is a static method?为什么不是每个方法都是静态方法?
【发布时间】:2011-10-11 19:06:18
【问题描述】:

如果非static 函数被复制到具有该方法的每个对象的堆中,那么为什么Java static 中的所有方法都不是默认的?为什么要这样浪费所有的堆内存?

图解说明会更有助于我理解这一点。

【问题讨论】:

  • 就方法而言,static 关键字表示该方法处于类级别,与任何特定实例无关,与内存分配、堆栈或堆内存无关。跨度>
  • 这个问题引出了另一个问题——你读过任何关于 OOP 技术的文章吗?
  • @Hovercraft Full of Eels- 我认为这个问题在被问到时措辞不佳。问题不是“为什么有非静态方法”,而是“似乎非静态方法比静态方法使用更多空间;为什么这是默认值?”
  • @Andrew Thompson - 阿门!

标签: java methods static


【解决方案1】:

您的问题是对过程编程和面向对象编程进行比较。 http://en.wikipedia.org/wiki/Procedural_programming#Comparison_with_object-oriented_programming。 OO 与过程化的主要动机是拥有能够自己执行操作的数据结构。

关于内存分配的假设,方法不会复制到每个对象,因为Java不支持动态类型修改。例如,如果一个对象是 Foo 类型,那么它将具有 Foo 类型声明的所有方法。如果不更改类型本身,就不能将新方法添加到 Foo 的实例中。每当在对象上调用方法时,它都会在幕后作为过程运行。

无论何时运行:

foo.say( "Hello, world!" );

Java 实际上做了这样的事情:

  1. 创建字符串“Hello, world!”
  2. 查找为foo 声明的类型。
  3. 在该类型中查找带有签名say(String) 的方法。
  4. 使用foo 对象的实例状态运行该方法。

由于方法保持自己的状态,任何非静态方法都可以通过将对象实例作为方法参数传入以静态方式实现。事实上,上面的第 4 步很可能是 Java 编译器以这种方式实现的。

【讨论】:

    【解决方案2】:

    通常,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 指针使用相同数量的空间,即使它们有不同数量的方法。

    【讨论】:

      【解决方案3】:

      方法的代码不在栈上,它在堆的一部分(永久代,垃圾收集器的特殊情况)。在堆栈上,只有方法的局部变量(以及类似的运行时数据,如返回地址)位于。

      这与它们是静态方法还是非静态方法无关。

      此外,方法的代码将不会为类的每个对象复制 - 只是在执行方法时,它会获得一个额外的参数,该参数指向调用它的对象.这个单一参数是静态和非静态方法之间唯一的内存开销。

      【讨论】:

        【解决方案4】:

        因为如果每个方法都是静态的,java 就不会是面向对象的。

        你需要在不同的对象上调用一个方法,因为它们有不同的状态。

        至于内存 - 每个静态调用也会进入堆栈。

        【讨论】:

        • 谢谢大家的帮助,非常感谢
        【解决方案5】:

        静态方法无法访问对象的实例成员变量...没有状态,没有 OOP。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-08-18
          • 2014-01-03
          • 2011-12-11
          • 1970-01-01
          • 2020-05-04
          • 2018-10-27
          • 2010-09-28
          相关资源
          最近更新 更多