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

Почему переменные экземпляра имеют значения по умолчанию в java?

Почему переменные, объявленные в классе, имеют значения по умолчанию, но переменные, объявленные внутри методов, называемые "локальными переменными", не имеют значений по умолчанию в Java?

Например

class abc
{
   int a;

   public static void main(String ss[])
   {
        int b;

          abc aa=new abc();
          System.out.println(aa.a);
          System.out.println(b);
    }
 }

В приведенном выше примере переменная a имеет значение по умолчанию 0, но переменная b дает ошибку, что она, возможно, не была инициализирована.

4b9b3361

Ответ 1

Все переменные-члены должны загружаться в кучу, поэтому они должны инициализироваться значениями по умолчанию при создании экземпляра класса. В случае локальных переменных они не загружаются в кучу, они хранятся в стеке до тех пор, пока они не будут использоваться до java 7, поэтому нам необходимо явно их инициализировать. Теперь "Компилятор сервера Java Hotspot" выполняет "анализ отклика" и решает выделить некоторые переменные в стеке вместо кучи.

Ответ 2

Локальные переменные Инициализация

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

Пример:

public class SomeClassName{

public static void main(String args[]){
int total;
System.out.println("The incremented total is " + total + 3); //(1)
}
}

Компилятор жалуется, что значение локальной переменной, используемое в инструкции println в (1), не может быть инициализировано. Инициализация локальной переменной total перед использованием решает проблему:

public class SomeClassName{

public static void main(String args[]){
int total = 45; //Local variable initialized with value 45 System.out.println("The incremented total is " + total+ 3); //(1)
}
}

Инициализация полей

Если инициализация не указана для экземпляра или статической переменной, либо при объявлении, либо в блоке инициализатора, то она неявно инициализируется значением по умолчанию для своего типа, Переменная экземпляра инициализируется значением по умолчанию его типа при каждом экземпляре класса, то есть для каждого объекта, созданного из класса. Статическая переменная инициализируется значением по умолчанию его типа при первом загрузке класса.

Ответ 3

Поскольку локальные переменные выделяются в стеке, ячейка памяти для локальной переменной выделяется, когда ей присваивается значение.

Простой пример

class Abc {
   int i = -111;
   int e;

   int doSomething() {
        int a = 10;
        int b = a + i;    
        int c = b + 100;

        Abc d = new Abc();

        e = b + c + d.a;

        return e + 1000;
    }
 }

и байт-код из javap -c Abc

Compiled from "Abc.java"
class Abc {
  int i;
  int e;

  Abc();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: bipush        -111
       7: putfield      #2                  // Field i:I
      10: return

  int doSomething();
    Code:
       0: bipush        10
       2: istore_1
       3: iload_1
       4: aload_0
       5: getfield      #2                  // Field i:I
       8: iadd
       9: istore_2
      10: iload_2
      11: bipush        100
      13: iadd
      14: istore_3
      15: new           #3                  // class Abc
      18: dup
      19: invokespecial #4                  // Method "<init>":()V
      22: astore        4
      24: aload_0
      25: iload_2
      26: iload_3
      27: iadd
      28: aload         4
      30: getfield      #2                  // Field i:I
      33: iadd
      34: putfield      #5                  // Field e:I
      37: aload_0
      38: getfield      #5                  // Field e:I
      41: sipush        1000
      44: iadd
      45: ireturn
}

При использовании метода inawked выделяется пространство памяти в стеке текущий кадр

Если вы посмотрите внимательно, то даже int a=-111; назначение происходит в неявной функции init Abc()!

       int a = -111;

       5: bipush        -111
       7: putfield      #2                  // Field a:I

Поскольку переменной поля e не присвоено никакого значения, оно будет 0, если оно является примитивным или null, если ссылка объекта

И если вы посмотрите doSomething()

        int a = 10;
        0: bipush        10

для локального использования в этом случае начальное значение нужно вставить в стек. 10. без этой "push" [инициализации] значение не доступно для последующих операторов (поскольку значение не находится в стеке). после того, как значение будет нажато для стека, другие операции, такие как iadd istore и т.д., выполняются в стеке

ниже оператор фактически создает объект в кучном пространстве и вызывает метод init. Здесь un инициализированные переменные типа 'e' получают значения по умолчанию

      15: new           #3                  // class Abc
      18: dup

Я оставляю сравнение с байт-кодом до вас;) но я надеюсь, что это ясно.

Ответ 4

tl; dr: Это был более или менее произвольный выбор

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

Обоснованием значений по умолчанию является безопасность. Когда экземпляр объекта создается, будет выделен фрагмент памяти для объекта, который содержит переменные экземпляра, указывающие на и т.д. Разработчики Java решили, что было бы неплохо стереть эту часть памяти с нулями и нулями. Таким образом, вы никогда не будете читать мусор, который был там до того, как объект был выделен. Они могли принудительно инициализировать; нет ничего принципиального в выборе. Вероятно, это упростило реализацию и придало достаточный смысл разработчикам Java.

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