首页 文章

理解堆上的静态和非静态方法表示之间的区别

提问于
浏览
3

我已经阅读了一些关于这个主题的帖子:

但我对它们所说明的概念感到困惑:

静态方法(实际上是所有方法)以及静态变量都存储在堆的PermGen部分中,因为它们是反射数据的一部分(类相关数据,而不是实例相关) .

因此,无论是否 static ,方法仅存储在类中的堆上的一个副本 . 现在只有一个副本在类中,我解释为所有方法都属于该类,那么为什么Java只能使用实例化的实例调用非静态方法?

为什么我们有非静态方法属于实例的概念,而不是类?

1 回答

  • 6

    这个故事的另一面需要提及 . 有课时,请说 Bar

    public class Bar {
       public static void doSomething() { ... }
    
       public void doSomethingElse() { ... }
    }
    

    在堆上, doSomethingElse 的签名不是 doSomethingElse() ,而是 doSomethingElse(Bar this) . 与没有参数的 doSomething 相反(因此你不能从静态方法调用 this - 没有 this 来调用) .

    当你有这样的电话时:

    Bar bar = new Bar();
    bar.doSomethingElse();
    

    它只是一个语法糖:

    doSomethingElse(bar); // I neglected here for simplification but the name of the method in the compiled code also includes the name of the class.
    

    定义扩展类 Foo 时:

    public class Foo extends Bar {
        @Override
        public void doSomethingElse() { ... }
    }
    

    另一种方法是创建 doSomethingElse(Foo this) . 接下来就是一个虚拟表(如果你不熟悉这个术语,请阅读它) - 每个类都有一个虚拟表,它将方法签名映射到具体代码 . 在运行时调用方法时,根据实例的动态类型,在类(而不是实例)虚拟表中搜索正确的实现 .

    所以完整的例子就是(当然这只是一个简化):

    Java syntax (syntactic sugar):

    Bar b = new Foo();
    b.doSomethingElse();
    

    What really happens (simplified):

    // Step 1: Get the correct overriden method for Foo class from the virtual table
    // Step 2: Invoke the method with the instance "b" as the first parameter (the "this" parameter)
    Foo.getMethodFromVirtualTable("doSomethingElse").invoke(b);
    

    这当然只是一种简化,但就像它发生的那样 .

    事实上,当你想到它时,所有方法在内存中都是静态的(这就是它们驻留在 PermGen 中的原因) . 编译器为每个类使用静态虚拟表,以便调用正确的方法 . 这允许多态性 .

相关问题