50

I have a class, Super:

public class Super {
    public static String foo = "foo";
}

I also have another class, Sub that extends Super:

public class Sub extends Super {
    static {
        foo = "bar";
    }

    public static void main (String[] args) {
        System.out.println(Super.foo);
    }
}

When I run it, it prints out bar.
My third (and last) class is Testing:

public class Testing {
    public static void main (String[] args) {
        System.out.println(Super.foo);
        System.out.println(Sub.foo);
        System.out.println(Super.foo);
    }
}

This prints:

foo
foo
foo

I don't understand why the contents of foo vary depending on what class you're accessing it from. Can anyone explain?

jmgrosen
  • 1,030
  • 2
  • 10
  • 18
  • 2
    What I meant by that statement was that when I access it from `Testing` it returns something different than when I access it from `Sub`. – jmgrosen Mar 27 '12 at 21:43
  • `@jmgrosen:` Ah, with you now. – T.J. Crowder Mar 27 '12 at 21:46
  • 3
    FWIW, note the important distinction between what you have above and what you'd have if `Sub` contained `public static String foo = "bar";` (whereupon you get "foo", "bar", "foo" as you probably expect). – T.J. Crowder Mar 27 '12 at 21:48
  • I'm not sure if the behavior of this code is an argument against having static variables that are neither private nor final, or against letting static initializers (even implicitly) mess with other classes' static variables. Probably both. – Ilmari Karonen Sep 19 '17 at 19:32
  • You can do it in Php! https://www.php.net/manual/en/language.oop5.late-static-bindings.php They call it "late static binding", Java do not support it for "final" / "private" / "static" variables. – Thomas Decaux Nov 12 '19 at 18:22

3 Answers3

44

I don't understand why the contents of foo vary depending on what class you're accessing it from.

Basically it's a matter of type initialization. The value of foo is set to "bar" when Sub is initialized. However, in your Testing class, the reference to Sub.foo is actually compiled into a reference to Super.foo, so it doesn't end up initializing Sub, so foo never becomes "bar".

If you change your Testing code to:

public class Testing {
    public static void main (String[] args) {
        Sub.main(args);
        System.out.println(Super.foo);
        System.out.println(Sub.foo);
        System.out.println(Super.foo);
    }
}

Then it would print out "bar" four times, because the first statement would force Sub to be initialized, which would change the value of foo. It's not a matter of where it's accessed from at all.

Note that this isn't just about class loading - it's about class initialization. Classes can be loaded without being initialized. For example:

public class Testing {
    public static void main (String[] args) {
        System.out.println(Super.foo);
        System.out.println(Sub.class);
        System.out.println(Super.foo);
    }
}

That still prints "foo" twice, showing that Sub isn't initialized - but it's definitely loaded, and the program will fail if you delete the Sub.class file before running it, for example.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    This seems correct, but do you happen to know why `Sub.foo` is compiled into `Super.foo`? – jmgrosen Mar 27 '12 at 21:43
  • 4
    @jmgrosen: Yes - because there's only one variable, declared by `Super`. As maerics says, it's accessible "via" `Sub`, but that doesn't mean it's a different variable. – Jon Skeet Mar 27 '12 at 21:46
  • is the same case for final members also – Prashant Shilimkar Dec 19 '13 at 07:53
  • 2
    @PrashantShilimkar: I'm not sure exactly what you're asking about, to be honest... a final member can only be assigned once anyway. – Jon Skeet Dec 19 '13 at 07:59
  • So if we had a static variable defined in `Sub` it would force to initialize the `Sub` class and run the static block? – Andrea Bergonzo Nov 13 '17 at 13:52
  • @AndreaBergonzo: Only if you refer to that variable. It's not entirely clear what you're asking, but it sounds like you should be able to test it for yourself. – Jon Skeet Nov 13 '17 at 13:53
  • Yep, I just tested it. Thanks! One last thing: does the fact that `Sub.foo` is compiled into a reference to `Super.foo` and it doesn't initialize the `Sub` class has a name? – Andrea Bergonzo Nov 13 '17 at 14:04
  • @AndreaBergonzo: Not that I'm aware of. – Jon Skeet Nov 13 '17 at 14:05
  • 2
    "Basically it's a matter of type initialization" No really, it's a matter of class loading. Static blocks run when the class is loaded, first instantiation(or calling a static method) being a cause of class being loaded – Georgian Benetatos Feb 08 '18 at 14:57
  • @ Benetatos , Exactly, in this case Sub class is never loaded, because its never referenced. – sandejai Aug 08 '18 at 14:16
  • @sandejai: The Sub type isn't being initialized because it's never being loaded because it's never being referenced. If a class *could* be loaded without being initialized (e.g. via reflection) then `foo` still wouldn't be set to `"bar"`. Class loading is what usually triggers type initialization, but I still maintain it's the type initialization that's key here. – Jon Skeet Aug 08 '18 at 22:30
  • @sandejai: And in fact using `Sub.class` *will* load the class without initializing it, proving that it really is type initialization rather than class loading that's important. Will edit to note that. – Jon Skeet Aug 08 '18 at 22:32
0

It does not matter where you change value of a static variable, it is the same variabile foo, in Sub or Super. It does not matter neither how many new Sub or new Super objects you create and then modify. The change will be seen everywhere, since Super.foo, Sub.foo, obj.foo share the same piece of storage.This works with primitive types also:

class StaticVariable{
        public static void main(String[] args){
            System.out.println("StaticParent.a = " + StaticParent.a);// a = 2
            System.out.println("StaticChild.a = " + StaticChild.a);// a = 2

            StaticParent sp = new StaticParent();
            System.out.println("StaticParent sp = new StaticParent(); sp.a = " + sp.a);// a = 2

            StaticChild sc = new StaticChild();
            System.out.println(sc.a);// a = 5
            System.out.println(sp.a);// a = 5
            System.out.println(StaticParent.a);// a = 5
            System.out.println(StaticChild.a);// a = 5
            sp.increment();//result would be the same if we use StaticParent.increment(); or StaticChild.increment();
            System.out.println(sp.a);// a = 6
            System.out.println(sc.a);// a = 6
            System.out.println(StaticParent.a);// a = 6
            System.out.println(StaticChild.a);// a = 6
            sc.increment();
            System.out.println(sc.a);// a = 7
            System.out.println(sp.a);// a = 7
            System.out.println(StaticParent.a);// a = 7
            System.out.println(StaticChild.a);// a = 7
        }
}
class StaticParent{
        static int a = 2;
        static void increment(){
            a++;
        }
}
class StaticChild extends StaticParent{
         static { a = 5;}
}

You can refer to a static variable/method via object (like sc.a) or via it's class name (like StaticParent.a). It is preferable to use the ClassName.staticVariable to emphasize the static nature of the variable/method and to give the compiler better opportunities for optimization.

-3

Static members are not inherited in java as they are the properties of class and they are loaded in class area. they had nothing to do with object creation. however only the child classes can access the static members of their parent classes.

  • This is true (they aren't "inherited" so much as "shared"), guessing it's down-voted because it doesn't answer the question. – chad Jul 25 '19 at 19:25