我阅读了有关最终实例变量的其他问答,然后我知道为该类的每个实例在堆中创建了 非静态最终实例变量 但是在 java-herbert schildt的完整参考书中,据说:
声明为final的变量不会按实例占用内存。因此,final变量本质上是一个常量
哪个是正确的?
答案 0 :(得分:3)
第一个语句是正确的……尽管限于实例的final
字段。
第二个语句不正确。
一个final
变量将在运行时某处占用内存。但是,该内存的位置取决于您声明为final
的变量的类型。
如果变量是局部变量或方法的形式参数变量,则存储单元将位于堆栈上。
如果变量是非静态字段,则存储单元将是对象的一部分,并且将在堆中。
如果变量是静态字段,则存储单元的位置将取决于实现。 (它可能在堆中,也可能在其他地方。)
应注意,某些static final
字段是(但不是全部!)字段是编译常量,编译器有效地内联了它们的值。但是即使发生这种情况,在运行时仍然会有一个实际的字段占用存储单元,并且该存储单元的内容可以通过反射方式访问。
(实际上,您甚至可以反射性地修改final
字段。执行此操作时,JVM规范未指定该行为,在某些情况下可能会令人惊讶。)
最终变量是否按实例占用内存?
不,因为:
final
。final
,并且它们“不会在每个实例上占用内存” 如果
final
变量在其声明本身中初始化并且仅向其分配内存一次,则编译器可以做出此假设,因为显然创建的每个实例将对该最终变量具有相同的值。编译器足够聪明来执行这种优化吗?
考虑这个
public class Test
public static test(final int val) {
System.out.println(val);
}
}
现在val
是final
,但是它的值取决于您如何调用test
方法。这在({javac
)编译时是未知的,并且通常JIT编译器也不知道。确实,假设test
是用不同的参数调用的,则无法直接优化val
存储单元。
除static final
变量(符合JLS定义)外,所有情况均适用于Test.test
变量。
从理论上讲,JIT编译器可能会看到val
仅使用相同的常量值进行了调用,并将该值内联到本机代码中,因此不需要存储单元。但是,这种优化对性能的影响很小,并且不太可能应用于实际代码中。因此,我怀疑实现它是否值得。
(test
最有可能由于将val
的主体内联到其呼叫站点而被优化了。这是更普遍的优化,肯定是值得的。这样可以允许窥孔优化器来推断内联代码中不需要class FooHolder {
friend class BigClass;
private:
int foo_A;
int foo_B;
//...
}
class BarHolder {
friend class BigClass;
private:
bool bar_A;
bool bar_B;
//...
}
class BigClass {
public:
int getFoo_A() { return m_FooHolder.foo_A; };
int getFoo_B() { return m_FooHolder.foo_B; };
//...
bool getBar_A() { return m_BarHolder.bar_A; };
bool getBar_B() { return m_BarHolder.bar_B; };
//...
private:
FooHolder m_FooHolder;
BarHolder m_BarHolder;
}
。