静态嵌套类的实例变量与外部类的静态变量

par*_*ver 6 java static static-classes java-8

我在 java 中使用静态嵌套类用于特定用例。下面显示了相同的最小示例:

public class Foo {
    static int fooInner = getInner(); // CASE 1 

    private static class StaticFoo {
        int fooInner = getInner(); // CASE 2

        public int useFooInner(){
            System.out.println(fooInner);
            //do something
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是案例 1 中的内存分配与案例 2 中的内存分配有何不同?或者是一样的?如果我也将 case 2 变量设为静态会怎样。内存使用会有所不同吗?

注意:请不要提及会发生阴影。虽然我已经将两个变量都放在那里,但它是一个“OR”案例,这就是“CASE”的原因。

PS:感觉内存使用量应该是一样的。由于嵌套类是静态的,因此不会为每个对象创建它,因此实例变量fooInner (案例 2)也将只创建一次。因此, getInner() 函数将只运行一次。但这只是抽象层面+直觉。更具描述性的答案将不胜感激!

use*_*601 3

他们是不同的。

从内存分配的角度来看,静态内部类与顶级类没有什么不同。您将被编译为一个在运行时本质上独立于其父类的StaticFoo类 ( )。Foo$StaticFoo.class在编译时,会对私有成员进行访问检查。

因此,在情况 1 中,类中有一个静态字段。它将被分配为Foo.class堆上对象的字段。每个 ClassLoader 只会有一个实例来加载 Foo 类,这通常意味着整个 JVM 只有一个共享实例。

在情况 2 中,类中有一个实例字段Foo$StaticFoo。在堆上,将为创建的每个 StaticFoo 实例分配空间(并分配一个值)。创建的每个 StaticFoo 将访问该字段自己的实例,并且由于它不是final,因此每个实例的值都可以独立更改。

如果改为StaticFoo.fooInnerstatic则与情况 1 完全相同。

注意:以上仅适用于 Java 8 及更高版本。对于早期的 JVM,每种情况下分配的内存仍然与上面的描述相符,但静态变量以及每个 ClassLoader 的单例也存储在不同的内存池中:PermGen Space 而不是主堆。有关更多详细信息,请参阅此答案。

  • 另外,请注意:不存在“静态实例变量”之类的东西。“实例”一词意味着与类的特定实例关联的变量,但“静态”一词的含义恰恰相反 - 不与任何特定实例关联。正确的术语是“实例字段”(或简称“字段”)和“静态字段”,表示在方法体外部声明的变量。 (2认同)