JavaRush /Курсы /Java Syntax Pro /Инициализация

Инициализация

Java Syntax Pro
10 уровень , 2 лекция
Открыта

1. Инициализация переменных

Как вы уже знаете, в вашем классе можно объявить несколько переменных класса, и не просто объявить, а сразу инициализировать их стартовыми значениями.

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

Код Примечание
class Cat
{
   public String name;
   public int age = -1;

   public Cat(String name, int age)
   {
     this.name = name;
     this.age = age;
   }

   public Cat()
   {
     this.name = "Безымянный";
   }
}



Переменной age присваивается стартовое значение




Стартовое значение перетирается


Для age используется стартовое значение
 Cat cat = new Cat("Васька", 2);
Так можно: вызовется первый конструктор
 Cat cat = new Cat();
Так можно: вызовется второй конструктор

Вот что будет происходить при выполнении кода Cat cat = new Cat("Васька", 2);:

  • Создается объект типа Cat
  • Инициализируются все переменные класса своими стартовыми значениями
  • Вызывается конструктор и выполняется его код.

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


2. Порядок инициализации переменных класса

Переменные не просто инициализируются до работы конструктора: они еще и инициализируются в четко заданном порядке — порядке объявления в классе.

Давайте рассмотрим такой интересный код:

Код Примечание
public class Solution
{
   public int a = b + c + 1;
   public int b = a + c + 2;
   public int c = a + b + 3;
}

Такой код не скомпилируется, т.к. на момент создания переменной а, переменных b и c еще нет. А вот так записать можно, и этот код отлично скомпилируется и будет работать.

Код Примечание
public class Solution
{
   public int a;
   public int b = a + 2;
   public int c = a + b + 3;
}


0
0+2
0+2+3

Примечание: но вы же помните, что ваш код должен быть прозрачен для других разработчиков, так что такие приемы лучше не использовать — это ухудшает читаемость кода.

Тут нужно помнить, что все переменные класса до того, как им присвоили какое-либо значение, имеют значение по умолчанию. Для типа int это ноль.

Когда JVM будет инициализировать переменную а, просто присвоит ей значение по умолчанию для типа int — 0.

Когда очередь дойдет до b, переменная a уже будет известна и содержать значение, поэтому JVM присвоит ей значение 2.

Ну а когда дело дойдет до переменной c, переменные а и b уже будет проинициализированы, и JVM легко вычислит стартовое значение для с: 0+2+3.

Если вы создали переменную внутри метода, вы не можете ее использовать, если прежде не присвоили ей какое-нибудь значение. А с переменными класса это не так. Если переменной класса не присвоено стартовое значение, значит ей присваивается значение по умолчанию.


3. Константы

Раз уж мы продолжаем разбирать процесс создания объекта, стоит затронуть вопрос инициализации констант — переменных класса, которые имеют модификатор final.

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

Но вот чего вы не знаете, так это того, что стартовое значение можно сразу не присваивать, если присвоить его в конструкторе. И это отлично будет работать для final-переменной. Единственное требование — если конструкторов несколько, final переменной должно быть присвоено значение во всех конструкторах.

Пример:

public class Cat
{
   public final int maxAge = 25;
   public final int maxWeight;

   public Cat (int weight)
   {
      this.maxWeight = weight; // занесение стартового значения в константу
   }
}


4. Код в конструкторе

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

Например, одно важное замечание насчет конструкторов. Теоретически в конструкторе можно писать код любой сложности. Но не нужно этого делать. Пример:

class FilePrinter
{
   public String content;

   public FilePrinter(String filename) throws Exception
   {
      FileInputStream input = new FileInputStream(filename);
      byte[] buffer = input.readAllBytes();
      this.content = new String(buffer);
   }

   public void printFile()
   {
      System.out.println(content);
   }
}






Открываем поток чтения файла
Читаем файл в массив байт
Сохраняем массив байт в виде строки




Выводим содержимое файла на экран

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

А что если бы такого файла не было? А если бы были проблемы с его чтением? А если бы он был слишком большим?

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

Пример 1 – Сериализация

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

И вот тут окажется, что на другом компьютере нет вашего файла, в конструкторе возникнет ошибка, и никто ее не обработает — что вполне себе способно привести к закрытию программы.

Пример 2 — Инициализация полей класса

Если конструктор вашего класса может выбросить checked-исключения – содержит ключевое слово throws, вы обязаны перехватить это исключение в методе, который создает ваш объект.

А если такого метода нет? Пример:

Код  Примечание
class Solution
{
   public FilePrinter reader = new FilePrinter("c:\\readme.txt");
}
Такой код не скомпилируется.

Конструктор класса FilePrinter содержит checked-исключения: вы не можете создать объект FilePrinter, не обернув его в try-catch. А try-catch можно писать только в методе



5. Конструктор базового класса

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

Если вы унаследуете свой класс от другого класса, фактически в объект вашего класса будет встроен объект класса-родителя. Причем этот класс-родитель имеет свои переменные класса и свои конструкторы.

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

Классы

Как же нам узнать, в каком порядке инициализируются переменные и вызываются конструкторы? Давайте для начала напишем код двух классов, один из которых наследуется от другого:

Код Примечание
class ParentClass
{    
   public String a;
   public String b;

   public ParentClass()
   {
   }
}

class ChildClass extends ParentClass
{
   public String c;
   public String d;

   public ChildClass()
   {
   }
}










Класс ChildClass наследуется от класса ParentClass.

Нам нужно определить, в каком же порядке инициализируются переменные и вызываются конструкторы. Сделать это нам поможет логирование.

Логирование

Логированием называется запись в консоль или файл действий, которые происходят во время работы программы.

Определить, что вызвался конструктор, довольно просто: нужно в теле конструктора написать в консоль сообщение об этом. А вот как определить, что переменная инициализировалась?

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

Финальный код

public class Main
{
   public static void main(String[] args)
   {
      ChildClass obj = new ChildClass();
   }

   public static String print(String text)
   {
      System.out.println(text);
      return text;
   }
}

class ParentClass
{
   public String a = Main.print("ParentClass.a");
   public String b = Main.print("ParentClass.b");

   public ParentClass()
   {
      Main.print("ParentClass.constructor");
   }
}

class ChildClass extends ParentClass
{
   public String c = Main.print("ChildClass.c");
   public String d = Main.print("ChildClass.d");

   public ChildClass()
   {
      Main.print("ChildClass.constructor");
   }
}




Создаем объект типа ChildClass


Этот метод пишет в консоль переданный текст и возвращает его же





Объявляем ParentClass

Пишем текст и им же инициализируем переменные




Пишем в консоль сообщение о вызове конструктора. Возвращаемое значение игнорируем.


Объявляем ChildClass

Пишем текст и им же инициализируем переменные




Пишем в консоль сообщение о вызове конструктора. Возвращаемое значение игнорируем.

Если выполнить этот код, на экран выведется текст:

Вывод на экран метода Main.print()
ParentClass.a ParentClass.b ParentClass.constructor
ChildClass.c ChildClass.d ChildClass.constructor

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


Комментарии (448)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
C0N5P1RACY Уровень 12
12 декабря 2025
Grigoryvvv Уровень 11 Expert
1 декабря 2025
01.12.2025 / 11 уровень По ключевому слову super() в обсуждении задачи "Кто тут наследник?" оставил ссылку на лекцию.
Vadim Уровень 14
24 сентября 2025
а зачем я покупаю курсы учиться и еще гуглю как их задачи решать раз они меня этому не учат? тогда пошел сразу гуглить и учить с гуглом программирование или другие ресурсы из поисковика либо дипсик поможет. Логика конечно невероятная. Купи курсы и гугли как тут задачи решать что дают. Новый курс такой же бездарный?
Pukidmi Уровень 7
27 октября 2025
А какой смысл в таком обучении, если ты гуглить не хочешь и самостоятельно разобраться? 100% есть вопросы к этой задаче, в лекции нет никакого объяснения что за ссылки на конструкторы, но оно будет дальше, а у тебя все равно остается материя для открытия следующей лекции, всегда можно вернуться к нерешенным задачам. Не существует таких курсов, где будет разбираться все классы и методы стандартной библиотеки. Учеба, внезапно, это труд.
Семен Уровень 12
20 ноября 2025
Время.... ты тратишь неоправданно много времени на поиск, а если бы разжевали - сразу бы понял...
Pukidmi Уровень 7
1 декабря 2025
Повторюсь, нет такого источника где все в одном месте и подробно. Разве что документация, и то не подробно. А учить все подряд - это и есть пустая трата времени.
Anonymous #3555639 Уровень 26
26 июля 2025
Коротко дополните статью: super — это ключевое слово в Java, которое используется для обращения к членам (полям, методам, конструкторам) родительского класса (суперкласса) из его наследника (подкласса).
Anonymous #3585174 Уровень 33
23 июня 2025
like
Den Winchester Уровень 32
8 июня 2025
Бился над последней задачей около часа, пока не нашёл этот комментарий, может кому-нибудь он тоже поможет: Решал задачу по super. Как итог понял, что:

Super("Параметр")
вызывает конструктор родителя с Параметром который я указал в Super. У меня конструктор родитель:

public Box(String material) {
        System.out.println("Коробка из материала " + material);  }
когда я в классе ребенке хочу вызвать родительский класс, то вызываю только

public Plastic() {
        super("Пластик");} // здесь указываю параметр как будто 
                           //использую конструктор public Box(String material).
и вывод на печать будет как у родителя, т.е. я использовал одну команду Super и вызвал целый конструктор в него. конечно создав в main конструктор

Мне помогло разобраться.
public static void main(String[] args) {
        Plastic plastic = new Plastic();}
Владимир Уровень 20
21 мая 2025
что я делаю не правильно? Есть класс Main, там public class CarConcern есть конструктор и прописаны константы. Хочу создать переменную с типом CarConcern, и вывести на экран константу. В идее выдаёт ошибку: java: non-static variable this cannot be referenced from a static context текст класса: public class Main { public class CarConcern { private final String manufacturer = "Lamborghini"; private final String model; private final int year; private final String color; public CarConcern() { //напишите тут ваш код model=""; year=4321; color="Оранжевый"; System.out.println(color+" "+year); } } public static void main(String[] args) { CarConcern carConcern =new CarConcern(); } }
Anastasia Panferova Уровень 14
22 мая 2025
в статическом методе main нельзя напрямую создать объект нестатического внутреннего класса
Владимир Уровень 20
24 мая 2025
А как не напрямую?
Владимир Уровень 20
24 мая 2025
Всё получилось, спасибо)) public class Main { private final String manufacturer = "Lamborghini"; private final String model; private final int year; private final String color; public Main() { //напишите тут ваш код model=""; year=4321; color="Оранжевый"; System.out.println(color+" "+year); } public static void main(String[] args) { Main carConcern =new Main(); } } Вот код, он работает)
spector Уровень 11
20 мая 2025
время, потраченное на решение последней задачи, было проведено просто super("no");
Anonymous #3478746 Уровень 11
3 мая 2025
если допустим у конструктора родительского класса есть несколько параметров, как команда super("GasCar"); узнает какому параметру присвоить значение "GasCar" ? задачу я решил, но есть ощущение что я не доконца понял как это работает
Виктор Уровень 23
15 мая 2025
По очереди будет присваивать согласно очереди в конструкторе наверное.
Bilal Уровень 28
26 апреля 2025
Крайне рекомендую после этого урока сразу ознакомиться с доп статьей Конструкторы базовых классов