Strange Java behaviour with static and final qualifiers [duplicate] - java

This question already has answers here:
Final fields initialization order
(2 answers)
Closed 6 years ago.
In our team we found some strange behaviour where we used both static and final qualifiers. This is our test class:
public class Test {
public static final Test me = new Test();
public static final Integer I = 4;
public static final String S = "abc";
public Test() {
System.out.println(I);
System.out.println(S);
}
public static Test getInstance() { return me; }
public static void main(String[] args) {
Test.getInstance();
}
}
When we run the main method, we get a result of:
null
abc
I would understand if it wrote null values both times, since the code of static class members is executed from top to bottom.
Can anyone explain why this behaviour is happening?

These are the steps taken when you run your program:
Before main can be run, the Test class must be initialized by running static initializers in order of appearance.
To initialize the me field, start executing new Test().
Print the value of I. Since the field type is Integer, what seems like a compile-time constant 4 becomes a computed value (Integer.valueOf(4)). The initializer of this field has not yet run, printing the initial value null.
Print the value of S. Since it is initialized with a compile-time constant, this value is baked into the referencing site, printing abc.
new Test() completes, now the initializer for I executes.
Lesson: if you rely on eagerly initialized static singletons, place the singleton declaration as the last static field declaration, or resort to a static initializer block that occurs after all other static declarations. That will make the class appear fully initialized to the singleton's construction code.

S is a compile-time constant, following the rules of JLS 15.28. So any occurrence of S in the code is replaced with the value which is known at compile-time.
If you change the type of I to int, you'll see the same for that, too.

You have strange behavior due to the Integer data type. Regarding JLS 12.4.2 static fields are initialized in the order you write it, BUT compile-time constants are initialized first.
If you do not use the wrapper type Integer but the int type, you get the behavior you want.

Your Test compiles into:
public class Test {
public static final Test me;
public static final Integer I;
public static final String S = "abc";
static {
me = new Test();
I = Integer.valueOf(4);
}
public Test() {
System.out.println(I);
System.out.println("abc");
}
public static Test getInstance() { return me; }
public static void main(String[] args) {
Test.getInstance();
}
}
As you can see, the constructor for Test gets called before I is initialized. This is why it prints "null" for I. If you were to swap the declaration order for me and I, you would get the expected result because I would be initialized before the constructor is invoked. You can also change the type for I from Integer to int.
Because 4 needs to get autoboxed (i.e., wrapped in an Integer object), it is not a compile-time constant and is part of the static initializer block. However, if the type were int, the number 4 would be a compile-time constant, so it would not need to be explicitly initialized. Because "abc" is a compile-time constant, the value of S is printed as expected.
If you would replace,
public static final String S = "abc";
with,
public static final String S = new String("abc");
Then you would notice the output of S is "null" as well. Why does that happen? For the same reason why I also outputs "null". Fields like these that have literal, constant values (that do not need autoboxing, like String) are attributed with the "ConstantValue" attribute when compiled, which means that their value can be resolved simply by looking into the class' constant pool, without needing to run any code.

Related

final variable initialized first

I was reading the question here : Java : in what order are static final fields initialized?
According to the answer
"except that final class variables and fields of interfaces whose
values are compile-time constants are initialized first ..."
I think this is not correct because the following will fail :
static {
String y = x;
}
public static final String x = "test";
In the static block, x is not recognized. Can anyone please comment if that answer is correct ?
The order of initialization doesn't change the fact that the JLS doesn't let you refer to variables before they're declared in various cases. This is described in JLS§8.3.3:
Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (§6.3). Specifically, it is a compile-time error if all of the following are true:
The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
That's why your code gets this compilation erorr:
error: illegal forward reference
The statement that static fields that are constant variables are initialized first is indeed defined in JLS§12.4.2:
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.1).
...
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
As you can see, constant variables are initialized in step 6, but others in step 9.
This demonstrates the behavior:
public class Example {
static String y;
static {
y = foo();
}
static String foo() {
return x.toUpperCase();
}
public static final String x = "test";
public static void main(String[] args) throws Exception {
System.out.println(x);
System.out.println(y);
}
}
That compiles, and outputs:
test
TEST
In contast, if you change the x line so it's not constant anymore:
public static final String x = Math.random() < 0.5 ? "test" : "ing";
It compiles, but then fails because x is null as of y = foo();.
For the avoidance of doubt: I don't recommend using methods to initialize fields like that. :-)
As this answer states:
... they are initialized in the order in which they appear in the source.
You are absolutely right, your example fails, because you are trying to use a variable that is declared after the usage. Since static block are executed in order of the source code, you are absolutely correct and should suggest and edit for that answer, because this statement is invalid:
except that final class variables ... are initialized first.

illegal reference to static field from initializer

I am new to enums in java and I am confused as to why this code compiles fine
enum Scale5 {
GOOD(), BETTER(), BEST();
static Scale5 s=GOOD;
}
But this code fails:
enum Scale5 {
GOOD(), BETTER(), BEST();
Scale5 s=GOOD;
}
And I get the error : illegal reference to static field from initializer.
I dont understand the reason.I am relatively inexperienced in enums so please dump it down for me.Thanks a lot!
The question asked here Cannot refer to the static enum field within an initializer? is the exact opposite of what I have asked.In my case declaring s as static compiles the code just fine.
From Java Language Specification:
It is a compile-time error to reference a static field of an enum type
that is not a constant variable (§4.12.4) from constructors, instance
initializer blocks, or instance variable initializer expressions of
that type.
Think of an enum like this:
public final class Scale5
{
public static final Scale5 GOOD = new Scale5();
public static final Scale5 BETTER = new Scale5();
public static final Scale5 BEST = new Scale5();
static Scale5 s = GOOD;//works because GOOD is initialized first;
Scale5 ss = GOOD;//doesn't work because in order to initialize GOOD,
//ss must be assigned an object that is not yet initialized;
}
It is very simple. The first case works because the compiler defines all the enum constants, then initializes static variables. Hence, GOOD at that point already exists and all is fine.
On the second case, the variable exists for all the enum constants, hence, when you are creating GOOD (or BETTER or BEST), GOOD must already be defined and bound. This is of course, illegal.
Consider following example:
class X {
public static final X GOOD = new X();
X y = GOOD;
static X z = GOOD;
public static void main(String[] args) {
System.out.println(GOOD);
System.out.println(GOOD.y);
System.out.println(X.z);
}
}
Output:
X#1db9742
null
X#1db9742
As you see value of GOOD.y is null. That is because at the moment of initializing value of
public static final X GOOD = new X();
when you invoked new X() you ware using current value of GOOD which is null until new X() will be finished. While in case of normal classes this behaviour is allowed, it was forbidden for enums probably because it was too often misunderstood.
In case of static fields they are executed problem dissipaters because they are executed in order of declaration, so
static X z = GOOD;
will be executed when we are sure that GOOD was initialized with proper value.

Are there circumstances under which Java does not initialize static fields immediately?

In a bigger project I am experiencing a strange behavior (at least for my understanding) with static field initialization. As I understand it, all static fields should be initialized at program start, meaning that when starting to work with a non-static field, there should not be any non-initialized static fields (more precisely, all static assignments "field = ..." should have been performed).
The following code is NOT a MWE, because it DOES what I expect it to, but it is essentially exactly what I'm doing in the bigger context. I have not been able to create a smaller example which results in the same problem.
When running this code:
import java.util.HashSet;
public class FB {
private static final HashSet<String> collection = new HashSet<>();
public static final String foo = bar("item");
public static String bar(String newItem) {
collection.add(newItem);
System.out.println("Yes, I've been invoked, and I currently store this: " + collection);
return newItem;
}
public static void main(String[] args) {
}
}
the output is (as Java initializes first the static field 'collection' and then foo by invoking bar(.)):
Yes, I've been invoked, and I currently store this: [item]
So far, so good. In the real project, I am doing exactly this (although foo and bar(.) are in different classes), but bar(.) is NOT invoked before I actually use the value of foo. (At least this happens in one case out of five - which are all created in the same way as shown above. The other four work fine.) Are there any circumstances which would cause Java behaving like this?
I already looked at these discussions, but they do not seem to capture my problem:
When are static variables are initialized?
Why static fields are not initialized in time?
Java Static Field Initialization
I realize that, when swapping the positions of foo and collection, the method invokation cannot work because collection would not be initialized (or rather, initialized to null?) when foo gets initialized. (To be honest, I am not sure in which order static fields are initialized when located in different classes, so this might be a source of problems.) But this would result in a
Exception in thread "main" java.lang.ExceptionInInitializerError
and not in just not invoking bar(.).
If required, I can provide more details about the real project, but so far I don't know what else might be of interest. (Sorry for the fuzzy description, but this is all I have so far.)
Static variables are instantiated by the JVM classloader and are shared by every instance of the class.
public class StaticVars {
static int i = 3;
public static void main( String[] args ) {
System.out.println( "Called at Runtime: " + getStaticVar() );
System.out.println( "Called via it's static member: " + i );
}
static int getStaticVar() {
return i;
}
static {
int i = 1;
System.out.println( "JVM ClassLoaded: " + i );
}
static {
int i = 2;
System.out.println( "Second JVM ClassLoaded: " + i);
}
}
Further, as dognose and NPE referenced, if you attempt to reference a static variable before it is initialized you will get an Illegal Forward Reference Error as static fields are initialized in sequential order. The following restrictions apply to static fields static methods are not checked in the same manner.
8.3.2.3. Restrictions on the use of Fields during Initialization
The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:
The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
The usage is not on the left hand side of an assignment.
The usage is via a simple name.
C is the innermost class or interface enclosing the usage.
More Info :: Illegal forward reference error for static final fields
when starting to work with a non-static field, there should not be any non-initialized static fields (more precisely, all static assignments field = ... should have been performed).
Generally, that's the case. However, it is possible to construct an example where it isn't. The following code prints out
static_obj=null
instance_obj=4
meaning that static_obj has not been initialized by the time instance_obj has been:
class Ideone
{
static {
new Ideone();
}
public static final Object static_obj = new Integer(42);
public final Object instance_obj = new Integer(4);
public Ideone() {
System.out.printf("static_obj=%s\n", static_obj);
System.out.printf("instance_obj=%s\n", instance_obj);
}
public static void main(String[] args) {
}
}
If you compile the class above, you can see that bar is called in the static initialization block.
static <clinit>()V
L0
LINENUMBER 4 L0
NEW java/util/HashSet
DUP
INVOKESPECIAL java/util/HashSet.<init> ()V
PUTSTATIC FB.collection : Ljava/util/HashSet;
L1
LINENUMBER 5 L1
LDC "item"
INVOKESTATIC FB.bar (Ljava/lang/String;)Ljava/lang/String;
PUTSTATIC FB.foo : Ljava/lang/String;
RETURN
MAXSTACK = 2
MAXLOCALS = 0
The only way to see an incomplete initialised class in FB is to be in the static block already.
e.g. say FB.<clinit>() calls another class which in turn uses FB.foo before it is initialised, it will see null rather than call bar()
Static fields are initialized in the order they are declared. So, if you would change the order of your fields to this:
import java.util.HashSet;
public class FB {
public static final String foo = bar("item");
private static final HashSet<String> collection = new HashSet<>();
public static String bar(String newItem) {
collection.add(newItem);
System.out.println("Yes, I've been invoked, and I currently store this: " + collection);
return newItem;
}
public static void main(String[] args) {
}
}
you will receive a NullpointerException inside the bar-method, when trying to access the not yet initialized collection. (Because your call to bar() happens in the progress of initializing the first variable)
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
at HelloWorld.bar(HelloWorld.java:14)
at HelloWorld.<clinit>(HelloWorld.java:10)
Static fields and static init blocks of a class are not always initialised/executed. If you do not use a class or do not load it it will not happen. For example - suppose you have:
class Test
{
static
{
System.out.println("Test");
}
public static int a = 4;
}
If you do not use Test class static block will not be executed. So you must for example instantiate a class to init static fields and invoke static init blocks:
Test t = new Test();
or use anything that is static like:
System.out.println(Test.a); // this will trigger 'initialisation'
After all - if you do not use static data in your code - why would JVM bother doing anythingh with it - deem it reasonable optimisation ;-).
Another example of using JDBC - you must call this once to let driver 'init' itself - ie. execute everything that is static and will be later needed:
Class.forName( "com.mysql.jdbc.Driver" ).newInstance();
BTW: you may init your collection this way:
private static final HashSet<String> collection = new HashSet<String>() {{add("item");}};

Changing static variable works Primitive Wrapper but not with Primitive type

I have a situation where I have to change java constant.
I have below code working
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Main {
public static final Integer FLAG = 44;
static void setFinalStatic(Class<?> clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String... args) throws Exception {
System.out.printf("Everything is %s%n", FLAG);
setFinalStatic(Main.class, "FLAG", 33);
System.out.printf("Everything is %s%n", FLAG);
}
}
If I run above , I get following output:
Everything is 44
Everything is 33
But if I change FLAG variable to int i.e.
public static final int FLAG = 44;
It does not work. The output is :
Everything is 44
Everything is 44
Is there any other way to make it work with Primitive Type int.
From jls-4.12.4
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.
Also section 13.1 says (emphasis mine)
3..References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted. No reference to such a field should be present in the code in a binary file (except in the class or interface containing the field, which will have code to initialize it). Such a field must always appear to have been initialized (§12.4.2); the default initial value for the type of such a field must never be observed.
It means that compile-time constant expression from constant variables will be put directly in code by compiler (it will be inlined) not read from final reference at runtime.
For instance if you execute main method from Bar class
class Foo{
static{
System.out.println("test if class will be loaded");
}
public static final int x = 42;
}
class Bar{
public static void main(String [] args){
System.out.println(Foo.x);
}
}
you will see no output from static block of Foo class which means Foo class hasn't been loaded, which means that value of Foo.x didn't come from this class. In fact Bar was compiled this way
class Bar{
public static void main(String [] args){
System.out.println(42); // reference Foo.x will be removed by compiler
// and replaced with actual value because
// compiler assumes that value can't/shouldn't
// change at runtime
}
}
So even changing value of Foo.x at runtime will not affect value printed in main method in Bar class.
You can't change that mechanism.
Possible way around would be initializing your final fields with values created at runtime time (they wouldn't exist at compile time, which will prevent them from being compile-time constant expressions). So instead of
public static final String x = "foo";
try
public static final String x = new String("foo");
or in case of primitive types use Unboxing like instead of
public static final int x = 42;
use
public static final int x = new Integer(42);
Primitive types get inlined.
In fact even non-primitive constants, when imported into other classes, will be copied, and the import forgotten. So there it will not work either. Only for constant caches, like the string pool, and Integer (Integer.valueOf(13)) caches you may overwrite their values.
This happens because static final fields of primitive or String type get inlined during compile time. The main method will looks something like this after compilation and de-compilation
public static void main(String... args) throws Exception {
System.out.printf("Everything is %s%n", 44);
setFinalStatic(Main.class, "FLAG", 33);
System.out.printf("Everything is %s%n", 44);
}
because FLAG gets replaced with actual value in compile-time.

Setting a static final variable in constructor

what i basicly want is this:
public class Test
{
private static final Integer a;
public Test(Integer a)
{
this.a = a;
}
}
This obviously doesn't work, cause the 2nd created instance would try to override the final variable.
So is there a way to give all the instances the same immutable value via the constructor?
Static final values should be initialized in a static context, not by instances.
One options is to set the value in the declaration:
private static final Integer a=FileConfig.getInstance().getA();
Each class can have a static {} block where code is called to initialize the static parts of the class.
static {
a = FileConfig.getInstance().getA();
}
Finally, you can set the value from a static method
private static int getA() {
return FileConfig.getInstance().getA();
}
private static final Integer a=getA();
In closure, static instance initialization does not belong in instance constructors.
If the configuration values change sometimes, there is simply no reason to store the value a in a static final variable. If you want to create each instance with the constant a in the constructor, what is the purpose of a static field in the first place? Somehow, when you call the constructor for the first time, you are passing in a value from somewhere. If the value deserves to be static and final, you can acquire it from within the static initializer. If the configuration is not a singleton, but every instance always produces the same value of a, you could easily do a = new FileConfig().getA();.
Other than that, you could make the value non-final, and rest assured that since you always put in the same value of a, the static variable will not change.
Still, you could make a a final instance variable of the class, set in the constructor.
So is there a way to give all the instances the same immutable value via the constructor?
I assume you want a value to be assigned to a the first time an object of type Test is created but not when any subsequent instance is created. In that case you cannot declare it final. a will be null initially, the constructor has to check if it is null and assign it a value in that case.
But I urge you to look at the design, especially why the caller have to provide the value. Isn't it counter-intuitive that after the second Test object is created Test.a does not change in the following case?
// assume this is the first `Test` object created:
Test t = new Test(5); // Test.a is 5
Test t = new Test(6); // Test.a is *still* 5

Categories

Resources