I have just spent half an hour figuring this thing out, I have managed to fix my code, but I don't fully understand what is going on and wondered if someone could shed some light on it.
I have a utils type class, that contains a few static fields (a database connection endpoint for example) that are used by various other programs depending on the task at hand. Essentially a library.
This is how it looked previously (while still broken);
//DBUtils.java
public final class DBUtils {
private static DBConnection myDBConnection = spawnDBConnection();
private static DBIndex myDBIndex = null;
private static DBConnection spawnDBConnection() {
//connect to the database
//assign a value to myDBIndex (by calling a method on the DBConnection object) <- IMPORTANT
//myDbIndex NOT NULL HERE
System.out.println("database connection completed");
//return the DBConnection object
}
public static searchDB(String name) {
//use the myDBIndex to find a row and return it
}
}
So briefly, I am using the static spawnDBConnection() method to assign a value to both myDBConnection and myDBIndex. This works perfectly, the first output line from my program is always "database connection completed", neither myDBConnection or myDBIndex are null at the end of the spawnDBConnection() method, everything is as it should be.
My external program looks like this;
//DoSomethingUsefulWithTheDatabase.java
public final class DoSomethingUsefulWithTheDatabase {
public static void main(String args[]) {
DBUtils.searchDB("John Smith"); //fails with NullPointerException on myDBIndex!
}
}
This call to searchDB happens after the spawnDBConnection has finished, I have used the standard output extensively to show this. However, once inside the searchDB method, the value of myDBIndex is null! It's a static field, and it was not null by the end of spawnDBConnection, no other assignments have been made, and now it's null :(
The simple fix was to remove the "= null" so the field declaration now looks like;
private static DBIndex myDBIndex;
Why did that make a difference? I'm thoroughly confused by this one.
The only way to initialize static final variables other than the declaration statement is Static block. A static block is a block of code with a static keyword. In general, these are used to initialize the static members. JVM executes static blocks before the main method at the time of class loading.
A Static Initialization Block in Java is a block that runs before the main( ) method in Java. Java does not care if this block is written after the main( ) method or before the main( ) method, it will be executed before the main method( ) regardless.
Static variables are initialized only once. Compiler persist the variable till the end of the program. Static variable can be defined inside or outside the function. They are local to the block.
Declaring variables only as static can lead to change in their values by one or more instances of a class in which it is declared. Declaring them as static final will help you to create a CONSTANT. Only one copy of variable exists which can't be reinitialize.
That's because the assignment of null
to myDBIndex
is done after
private static DBConnection myDBConnection = spawnDBConnection();
e.g. overrides the assignment in spawnDBConnection
The sequence is:
myDBConnection
, myDBIndex
Initialize myDBConnection = spawnDBConnection();
Which includes calling spawnDBConnection
and assignment of the return value to myDBConnection
myDBIndex
(with null)In your second example, the 3rd step does not exist.
Why did that make a difference? I'm thoroughly confused by this one.
The initializer for spawnDBConnection
was running, then the initializer for myDBIndex
was running. The initializer for myDBIndex
set the value to null. As this happened after spawnDBConnection
set it to a non-null value, the final result was that it was null.
Try not to do this - it's odd for a method called by a static initializer to set other static variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With