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

Как статический модификатор влияет на этот код?

Вот мой код:

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

Вывод 1 0, но я не могу понять.

Может кто-нибудь объяснить это мне?

4b9b3361

Ответ 1

В Java существуют две фазы: 1. Идентификация, 2. Исполнение

  • В фазе идентификации все статические переменные обнаруживаются и инициализируются значениями по умолчанию.

    Итак, теперь значения:
    A obj=null
    num1=0
    num2=0

  • Вторая фаза выполнение начинается сверху вниз. В Java выполнение начинается с первых статических членов.
    Здесь ваша первая статическая переменная static A obj = new A();, поэтому сначала она создаст объект этой переменной и вызовет конструктор, поэтому значение num1 и num2 станет 1.
    И затем снова будет выполняться static int num2=0;, что делает num2 = 0;.

Теперь предположим, что ваш конструктор выглядит следующим образом:

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

Это вызовет NullPointerException, поскольку obj еще не получил ссылку class A.

Ответ 2

Что означает модификатор static при применении к объявлению переменной, так это то, что переменная является переменной класса, а не переменной экземпляра. Другими словами... существует только одна переменная num1 и только одна переменная num2.

(Кроме того, статическая переменная похожа на глобальную переменную в некоторых других языках, за исключением того, что ее имя не отображается везде. Даже если оно объявлено как public static, неквалифицированное имя отображается только в том случае, если оно объявлено в текущий класс или суперкласс, или если он импортируется с использованием статического импорта. Это различие. Истинное глобальное видимо без какой-либо квалификации.)

Поэтому, когда вы ссылаетесь на obj.num1 и obj.num2, вы на самом деле ссылаетесь на статические переменные, чьи реальные обозначения A.num1 и A.num2. Аналогично, когда конструктор увеличивает num1 и num2, он увеличивает одинаковые переменные (соответственно).

Запутанная морщина в вашем примере находится в инициализации класса. Класс инициализируется первой инициализацией по умолчанию всех статических переменных, а затем выполняется выполнение объявленных статических инициализаторов (и статических блоков инициализатора) в том порядке, в котором они появляются в классе. В этом случае у вас есть следующее:

static A obj = new A();
static int num1;
static int num2=0;

Это происходит следующим образом:

  • Статика начинается с начальных значений по умолчанию; A.obj null и A.num1/A.num2 равны нулю.

  • Первое объявление (A.obj) создает экземпляр A(), а конструктор для A увеличивает A.num1 и A.num2. Когда объявление завершается, A.num1 и A.num2 являются 1, а A.obj ссылается на вновь созданный экземпляр A.

  • Второе объявление (A.num1) не имеет инициализатора, поэтому A.num1 не изменяется.

  • В третьем объявлении (A.num2) есть инициализатор, который присваивает нулю значение A.num2.

Таким образом, в конце инициализации класса A.num1 есть 1 и A.num2 is 0... и что показывают ваши утверждения печати. ​​

Это запутанное поведение действительно связано с тем, что вы создаете экземпляр до завершения статической инициализации и что используемый вами конструктор зависит от и модифицирует статику, которая еще не инициализирована. Это то, что вам следует избегать в реальном коде.

Ответ 3

1,0 правильно.

Когда класс загружается, все статические данные инициализируются в oder, которые они объявляются. По умолчанию int равно 0.

  • сначала создается A. num1 и num2 становятся 1 и 1
  • чем static int num1; ничего не делает
  • чем static int num2=0;, это записывает 0 в num2

Ответ 4

Это из-за порядка статических инициализаторов. Статические выражения в классах оцениваются в порядке сверху вниз.

Первым вызываемым является конструктор A, который устанавливает num1 и num2 как 1:

static A obj = new A();

Затем

static int num2=0;

вызывается и снова устанавливает num2 = 0.

Вот почему num1 равно 1 и num2 равно 0.

В качестве побочного примечания конструктор не должен изменять статические переменные, это очень плохой дизайн. Вместо этого попробуйте другой подход к внедрению Singleton в Java.

Ответ 5

Раздел в JLS можно найти: §12.4.2.

Подробная процедура инициализации:

9.Next, выполните либо инициализаторы переменной класса, и статические инициализаторы класса, либо инициализаторы полей интерфейса, в текстовом порядке , как если бы они были одним блоком, за исключением того, что конечные переменные класса и поля интерфейсов, значения которых константы времени компиляции сначала инициализируются

Таким образом, три статические переменные будут инициализированы один за другим в текстовом порядке.

So

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

Если я изменил порядок на:

static int num1;
static int num2=0;
static A obj = new A();

Результат будет 1,1.

Обратите внимание, что static int num1; не является инициализатором переменной, потому что (§8.3.2):

Если декларатор поля содержит инициализатор переменной, то он имеет семантику присваивания (§15.26) к объявленной переменной и: если декларатор предназначен для переменной класса (то есть статического поля), то инициализатор переменной оценивается и выполняется выполнение задания ровно один раз, когда класс инициализируется

И эта переменная класса инициализируется при создании класса. Это происходит сначала (§4.12.5).

Каждая переменная в программе должна иметь значение, прежде чем его значение будет used: Каждая переменная класса, переменная экземпляра или компонент массива инициализируется значением по умолчанию при его создании (§15.9, §15.10): Для байта типа значение по умолчанию равно нулю, то есть значение (Байт) 0. Для типа short значение по умолчанию равно нулю, то есть значение of (короткий) 0. Для типа int значение по умолчанию равно нулю, то есть 0. Для type long, значение по умолчанию равно нулю, то есть 0L. Для типа float значение по умолчанию - положительное ноль, то есть 0.0f. Для типа double, значение по умолчанию - положительное ноль, то есть 0.0d. Для типа charЗначение по умолчанию - это нулевой символ, то есть '\ u0000'. Для типа boolean, значение по умолчанию - false. Для всех ссылочных типов (§ 4.3), значение по умолчанию равно null.

Ответ 6

Возможно, это поможет подумать об этом таким образом.

Классы - это чертежи для объектов.

Объекты могут иметь переменные при их создании.

Классы также могут иметь переменные. Они объявляются как статические. Поэтому они устанавливаются в классе, а не в экземплярах объектов.

Каждый из приложений может иметь только один класс, поэтому для этого класса он похож на глобальное хранилище. Разумеется, эти статические переменные могут быть доступны и изменены из любого места приложения (при условии, что они являются общедоступными).

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

"Собака" - это облако, а оранжевые - экземпляры "Собака" .

Dog class

читать дальше

Надеюсь, это поможет!

Если вы чувствуете, что некоторые мелочи, эта идея была впервые представлена ​​Plato

Ответ 7

Статическое ключевое слово используется в java в основном для управления памятью. Мы можем применить статическое ключевое слово с переменными, методами, блоками и вложенным классом. Ключевое слово static принадлежит классу, чем экземпляр класса. Для краткого объяснения статического ключевого слова:

http://www.javatpoint.com/static-keyword-in-java

Ответ 8

java не инициализирует значение какого-либо статического или нестатического элемента данных до тех пор, пока он не будет вызываться, но он его создает.

Итак, чтобы здесь, когда num1 и num2 будут вызваны в main, тогда он будет инициализирован со значениями

num1 = 0 + 1; и

пит2 = 0;

Ответ 9

Многие из приведенных выше ответов верны. Но действительно, чтобы проиллюстрировать, что происходит, я сделал несколько небольших изменений ниже.

Как уже упоминалось несколько раз, что происходит, экземпляр класса A создается до того, как класс A полностью загружен. Итак, что считается нормальным "поведением", не наблюдается. Это не слишком отличается от вызова методов из конструктора, который может быть переопределен. В этом случае переменные экземпляра могут не находиться в интуитивном состоянии. В этом примере переменные класса не находятся в интуитивном состоянии.

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

Выход

Constructing singleton instance of A
Setting num2 to 0
1
0