【问题标题】:Instance variable of a static nested class vs static variable of an outer class静态嵌套类的实例变量与外部类的静态变量
【发布时间】:2021-09-14 01:13:01
【问题描述】:

我在 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
        }
    }
}

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

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

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

【问题讨论】:

  • StaticFoo 是否声明 static 无关紧要,它的实例只会在评估 new StaticFoo(…) 表达式时创建。由于您的代码中没有任何内容,因此将永远创建 StaticFoono 实例,并且在 CASE 2 中永远不会执行 getInner(),而不是一次。

标签: java java-8 static static-classes


【解决方案1】:

它们是不同的。

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

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

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

如果您将StaticFoo.fooInner 更改为static,则与案例1 完全相同。

注意:以上内容仅适用于 Java 8 及更高版本。对于早期的 JVM,在每种情况下分配的 数量 内存仍然与上面的描述相匹配,但是静态变量以及每个 ClassLoader 的单例也存储在不同的内存池中:PermGen 空间而不是主堆。详情请见this answer

【讨论】:

  • 感谢您的回答!阅读您的答案后,我觉得我的说法“由于嵌套类是静态的,因此不会为每个对象创建它”实际上是不正确的。在我的声明中,我将静态类视为类中的静态实例变量,这是错误的(如果我错了,请纠正我)。阅读您的答案后,我推断出,它只是一个在类中定义的类,并且具有一些功能,例如可以从外部类访问私有变量等。对吗?
  • 对。静态内部类只是一个类,其 name 附加到外部类,并共享其 private 命名空间。这个类似于一个静态字段或方法,它只是一个name附加到类的变量或方法。在所有情况下,整个 JVM 中都有一个类/变量/方法,它与封闭类的特定 instance 无关。内部类的static 指的是内部,而不是内部类之外的instances
  • 如果您从内部类中删除static 修饰符,则内部类的每个实例都将与外部类的特定实例相关联,并且可以访问其非静态字段和方法。与外部类的实例A 关联的内部类的实例不能分配/替换给与外部类的实例B 关联的内部类的实例的变量。它的行为与外部类的每个 instance 都有一个单独的内部 完全相同。同样,这完全类似于非静态字段或方法。
  • 另外,请注意:没有“静态实例变量”之类的东西。 “实例”一词意味着与特定类实例相关联的变量,但“静态”一词的含义正好相反——与任何特定实例相关联。正确的术语是“实例字段”(或只是“字段”)和“静态字段”来表示在方法体之外声明的变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-07
  • 1970-01-01
  • 2016-08-30
相关资源
最近更新 更多