Подтвердить что ты не робот

Каковы правила, определяющие наследование статических переменных в Java?

У меня есть класс, Super:

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

У меня также есть еще один класс Sub, который расширяет Super:

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

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

Когда я запустил его, он распечатает bar.
Мой третий (и последний) класс 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);
    }
}

Отпечатки:

foo
foo
foo

Я не понимаю, почему содержимое foo меняется в зависимости от того, из какого класса вы обращаетесь к нему. Может кто-нибудь объяснить?

4b9b3361

Ответ 1

Я не понимаю, почему содержимое foo зависит от того, к какому классу вы обращаетесь.

В основном это вопрос инициализации типа. Значение foo устанавливается на "bar", когда Sub инициализируется. Однако в вашем классе Testing ссылка на Sub.foo фактически скомпилирована в ссылку на Super.foo, поэтому она не заканчивает инициализацию Sub, поэтому foo никогда не становится "bar".

Если вы измените свой тестовый код на:

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);
    }
}

Затем он будет печатать "бар" четыре раза, потому что первое выражение принудительно инициализировало Sub, что изменило бы значение foo. Это не вопрос того, откуда он вообще обращался.

Ответ 2

Статические переменные в Java не наследуются, они существуют только в классе, который их объявляет; однако они могут быть доступны неявно, ссылаясь на экземпляр или подкласс (или класс подкласса) класса, который определяет статическую переменную. (Обработка статических переменных - одна из немногих запутанных частей языка Java, IMHO.)

В вашем примере кода класс Sub имеет статический инициализатор, который изменяет значение Super.foo (несмотря на то, что похоже, что он меняет собственный унаследованный экземпляр переменной "foo" ). Однако класс Testing фактически не загружает класс Sub (возможно, через какой-то трюк компилятора?), Поэтому он не запускает статический инициализатор, поэтому значение "foo" никогда не изменяется.

Ответ 3

Статические элементы не наследуются в java, поскольку они являются свойствами класса, и они загружаются в область класса. они не имели никакого отношения к созданию объекта. однако только дочерние классы могут обращаться к статическим членам своих родительских классов.

Ответ 4

Не имеет значения, где вы изменяете значение статической переменной, это тот же самый переменный foo, в Sub или Super. Неважно, сколько объектов new Sub или new Super вы создаете, а затем изменяете. Это изменение будет видно повсюду, так как Super.foo, Sub.foo, obj.foo используют один и тот же кусок памяти. Это также работает с примитивными типами:

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;}
}

Вы можете ссылаться на статическую переменную/метод через объект (например, sc.a) или через это имя класса (например, StaticParent.a). Предпочтительно использовать ClassName.staticVariable, чтобы подчеркнуть статический характер метода variable/method и предоставить компилятору лучшие возможности для оптимизации.