Uninitialized variables and members in Java

2019-01-01 11:46发布

问题:

Consider this:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = \"initialized\";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // \"Local variable c may not have been initialised\"
    }

}

I don\'t get it. \"b\" is never initialized but will give the same run-time error as \"c\", which is a compile-time error. Why the difference between local variables and members?

Edit: making the members private was my initial intention, and the question still stands...

回答1:

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.



回答2:

The language defines it this way.

Instance variables of object type default to being initialized to null. Local variables of object type are not initialized by default and it\'s a compile time error to access an undefined variable.

See section 4.5.5 in here http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5



回答3:

Here\'s the deal. When you call

TestClass tc = new TestClass();

the new command performs four important tasks:

  1. Allocates memory on the heap for the new object.
  2. Initiates the class fields to their default values (numerics to 0, boolean to false, objects to null).
  3. Calls the constructor (which may re-initiate the fields, or may not).
  4. Returns a reference to the new object.

So your fields \'a\' and \'b\' are both initiated to null, and \'a\' is re-initiated in the constructor. This process is not relevant for method calling, so local variable \'c\' is never initialized.

HTH

PS: for the gravely insomniac, read this.



回答4:

The compiler can figure out that c will never be set. The b variable could be set by someone else after the constructor is called, but before doSomething(). Make b private and the compiler may be able to help.



回答5:

The compiler can tell from the code for doSomething() that c is declared there and never initialized. Because it is local, there is no possibility that it is initialized elsewhere.

It can\'t tell when or where you are going to call doSomething(). b is a public member. It is entirely possible that you would initialize it in other code before calling the method.



回答6:

Member-variables are initialized to null or to their default primitive values, if they are primitives.

Local variables are UNDEFINED and are not initialized and you are responsible for setting the initial value. The compiler prevents you from using them.

Therefore, b is initialized when the class TestClass is instantiated while c is undefined.

Note: null is different from undefined.



回答7:

You\'ve actually identified one of the bigger holes in Java\'s system of generally attempting to find errors at edit/compile time rather than run time because--as the accepted answer said--it\'s difficult to tell if b is initialized or not.

There are a few patterns to work around this flaw. First is \"Final by default\". If your members were final, you would have to fill them in with the constructor--and it would use path-analysis to ensure that every possible path fills in the finals (You could still assign it \"Null\" which would defeat the purpose but at least you would be forced to recognize that you were doing it intentionally).

A second approach is strict null checking. You can turn it on in eclipse settings either by project or in default properties. I believe it would force you to null-check your b.notify() before you call it. This can quickly get out of hand so it tends to go with a set of annotations to make things simpler:

The annotations might have different names but in concept once you turn on strict null checking and the annotations the types of variables are \"nullable\" and \"NotNull\". If you try to place a Nullable into a not-null variable you must check it for null first. Parameters and return types are also annotated so you don\'t have to check for null every single time you assign to a not-null variable.

There is also a \"NotNullByDefault\" package level annotation that will make the editor assume that no variable can ever have a null value unless you tag it Nullable.

These annotations mostly apply at the editor level--You can turn them on within eclipse and probably other editors--which is why they aren\'t necessarily standardized. (At least last time I check, Java 8 might have some annotations I haven\'t found yet)