Uninitialized Variables and Members in Java

What happens to a declared, uninitialized variable in Java?

How exactly a JVM does this is entirely up to the JVM and shouldn't matter for a programmer, since the compiler ensures that you do not read uninitialized local variables.

Fields however are different. They need not be assigned before reading them (unless they are final) and the value of a field that has not been assigned is null for reference types or the 0 value of the appropriate primitive type, if the field has a primitive type.

Using s.isEmpty() for a field String s; that has not been assigned results in a NullPointerException.


So as I see now, it makes a big difference if the variable is declared locally or in the Class, though I seem to be unable to understand why when declaring in the class it gives no error, but when declaring in the main it produces the "Not Initialized" error.

In general it's undesirable to work with values that do not have a value. For this reason the language designers had 2 choices:

a) define a default value for variables not yet initialized

b) prevent the programmers from accessing the variable before writing to them.

b) is hard to achieve for fields and therefore option a) was chosen for fields. (There could be multiple methods reading/writing that could be valid or invalid depending on the order of calls, which could only be determined at runtime).

For local variables option b) is viable, since all possible paths of the execution of the method can be checked for assignment statements. This option was chosen during the language design for local variables, since it can help to find many easy mistakes.

Uninitialized variables and members in Java

The rules for definite assignment are quite difficult (read chapter 16 of JLS 3rd Ed). It's not practical to enforce definite assignment on fields. As it stands, it's even possible to observe final fields before they are initialised.

Avoid uninitialized non final member class/ class variable

The behavior you seem to be looking for is more likely to produce unnecessary warnings than to help the design.

The fact that class-level fields get a JVM-assigned default value is by design, and it's OK for many reasons, including:

  • It's possible that the field's value won't be known until after object construction. This is typical, because many field values only get set in setters.
  • It's possible that the current class itself doesn't know what the value is. In this case, the responsibility to set the value is outside the class, so the warning would simply be an annoyance.
  • What if the field won't be used in the current class at all?

This is the area where the responsibility lies with the developer. Only you know the order of execution, only you know what data the code depends on, and only you have the responsibility to test the code and ensure that it's bug-free.

Do uninitialized primitive instance variables use memory?

All members defined in your classes have default values, even if you don't initialize them explicitly, so they do use memory.

For example, every int will be initialized by default to 0, and will occupy 4 bytes.

For class members :

int i;

is the same as :

int i = 0;

Here's what the JLS says about instance variables :

If a class T has a field a that is an instance variable, then a new instance variable a is created and initialized to a default value (§4.12.5) as part of each newly created object of class T or of any class that is a subclass of T (§8.1.4). The instance variable effectively ceases to exist when the object of which it is a field is no longer referenced, after any necessary finalization of the object (§12.6) has been completed.

Why can't we access static content via uninitialized local variable?

§15.11. Field Access Expressions:

If the field is static:

The Primary expression is evaluated, and the result is discarded. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.

Where earlier it states that field access is identified by Primary.Identifier.

This shows that even though it seems to not use the Primary, it is still evaluated and the result is then discarded which is why it will need to be initialized. This can make a difference when the evaluation halts the access as stated in the quote.

EDIT:

Here is a short example just to demonstrate visually that the Primary is evaluated even though the result is discarded:

class Foo {
public static int x = 1;

public static Foo dummyFoo() throws InterruptedException {
Thread.sleep(5000);
return null;
}

public static void main(String[] args) throws InterruptedException {
System.out.println(dummyFoo().x);
System.out.println(Foo.x);
}
}

Here you can see that dummyFoo() is still evaluated because the print is delayed by the 5 second Thread.sleep() even though it always returns a null value which is discarded.

If the expression was not evaluated the print would appear instantly, which can be seen when the class Foo is used directly to access x with Foo.x.

Note: Method invocation is also considered a Primary shown in §15.8 Primary Expressions.

Java - uninitialized static variable in .class file

All fields have an entry in the classfile giving the name, type, flags (final, public, volatile, static, etc.), and other data.

If you think about it, it has to be this way. Java isn't C, where the variable is just a location in bss or whatever. You have reflection as well as runtime type checking, so all that information has to be maintained.

If the field is initialized, there will be additional data in the classfile to do the initialization (either a ConstantValue attribute or bytecode in the method). However, even an uninitialized field has to have the field entry in the classfile.

Why attempt to print uninitialized variable does not always result in an error message

In the JLS, §8.3.3. Forward References During Field Initialization, its stated that there's a compile-time error when:

Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables
are in scope. Specifically, it is a compile-time error if all of the
following are true:

  • The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;

  • The use is a simple name in either an instance variable initializer of C or an instance 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.

The following rules come with a few examples, of which the closest to yours is this one:

class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}

Accesses [to static or instance variables] by methods are not checked in this way, so the code above produces output 0, because the variable initializer for i uses the class method peek() to access the value of the variable j before j has been initialized by its variable initializer, at which point it still has its default value (§4.12.5 Initial Values of Variables).

So, to summarize, your second example compiles and executes fine, because the compiler does not check if the x variable was already initialized when you invoke printX() and when printX() actually takes place at Runtime, the x variable will be assigned with its default value (0).

What type of error occurs when using an uninitialized variable?

If int i; is declaring a local variable, it is a compilation error to use it before it is assigned to; see @Eran's answer for the relevant section of the JLS.

Compilation error and compile-time errors are synonyms. (Compiler error is another synonym, though sometimes people use that to refer to bugs in the compiler.)

Does that make it a syntax/compiler error?

It is a compilation error. But it is not a syntax error.

This type of compilation error is typically called a semantic error.

A syntax error means that the code doesn't conform to the language's specified syntax (as defined by the grammar). In other words, the parser can't parse it.

A semantic error is any compilation error that isn't a syntax error. Depending on the programming language, semantic errors may include such things as:

  • compile time type errors
  • symbols that cannot be resolved by the compiler
  • symbols that have the wrong kind for the context
  • unreachable code
  • use of uninitialized variables
  • and so on.

Or a runtime error that the IDE is just smart enough to catch?

It is not a runtime error.

Actually, is it a compiler error but not technically a syntax error?

Correct.

I always thought of them as synonymous, but maybe syntax errors are just a type of compiler error...

They are not synonymous. Syntax errors are just one kind of compilation error.

(Unfortunately some Javascript implementations confusingly refer to all compilation errors as "Syntax Error". But that is not relevant if you are teaching Java. Java is not Javascript.)

Uninitialized class members in Java do not issue any compiler errors. local variables however do. Why?

When in doubt, check the Java Language Specification (JLS).

In the introduction you'll find:

Chapter 16 describes the precise way in which the language ensures
that local variables are definitely set before use. While all other
variables are automatically initialized to a default value, the Java
programming language does not automatically initialize local variables
in order to avoid masking programming errors.

The first paragraph of chapter 16 states,

Each local variable and every blank final field must have a definitely
assigned value when any access of its value occurs....A Java compiler
must carry out a specific conservative flow analysis to make sure
that, for every access of a local variable or blank final field f, f
is definitely assigned before the access; otherwise a compile-time
error must occur.

The default values themselves are in section 4.12.5. The section opens with:

Each class variable, instance variable, or array component is
initialized with a default value when it is created.

...and then goes on to list all the default values.

The JLS is really not that hard to understand and I've found myself using it more and more to understand why Java does what it does...after all, it's the Java bible!



Related Topics



Leave a reply



Submit