출처: http://stackoverflow.com/questions/10047802/public-static-final-or-private-static-final-with-getter


In Java, it's taught that variables should be kept private to enable better encapsulation, but what about static constants? This:

public static final int FOO = 5;

Would be equivalent in result to this:

private static final int FOO = 5;
...
public static getFoo() { return FOO; }

But which is better practice?


There's one reason to not use a constant directly in your code.

Assume FOO may change later on (but still stay constant), say to public static final int FOO = 10;. Shouldn't break anything as long as nobody's stupid enough to hardcode the value directly right?

No. The Java compiler will inline constants such as Foo above into the calling code, i.e.someFunc(FooClass.FOO); becomes someFunc(5);. Now if you recompile your library but not the calling code you can end up in surprising situations. That's avoided if you use a function - the JIT will still optimize it just fine, so no real performance hit there.


요약: 만약 어떤 코드가 caller와 callee로 되어 있을 때 callee의 상수가 public static final로 되어있을 경우, 그냥 그대로 사용할 경우 문제가 발생할 수 있다.

예를 들어 public static final int Foo = 5라고 정의가 되어있을 때 만약 someFunc(FooClass.Foo)라고 caller에서 사용한다면, JIT에서는 SomeFunc(5)로 치환해서 컴파일을 하게 된다. 그렇게 때문에 나중에 callee의 코드를 public static final int Foo = 10으로 고치게 되버리면 caller를 재컴파일하지 않으면 문제가 발생하게 된다. 이런 상황을 방지하려면, getter를 사용해야 한다.

non-static이나 non-final인 경우 이런 문제가 발생하지 않는다


아래는 추가 자료

출처: http://stackoverflow.com/questions/5173372/java-static-final-values-replaced-in-code-when-compiling


==fileA.java==
class A
{  
   
public static final int SIZE = 100;
}  

Then in another file i use this value

==fileB.java==  
import A;
class b
{
     
Object[] temp = new Object[A.SIZE];
}

When this gets compiled does SIZE get replaced with the value 100, so that if i were to down the road replace the FileA.jar but not FileB.jar would the object array get the new value or would it have been hardcoded to 100 because thats the value when it was originally built?


Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   bipush  42
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #12; //Field temp:[Ljava/lang/Object;
   
13:  return

}

(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   ldc     #12; //int 86753098
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #13; //Field temp:[Ljava/lang/Object;
   
13:  return

When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".

In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).