HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#База знаний</a></p>
1 <p><a>#База знаний</a></p>
2 <ul><li>10 дек 2020</li>
2 <ul><li>10 дек 2020</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><h2>Создание объектов и конструкторы</h2>
4 </ul><h2>Создание объектов и конструкторы</h2>
5 <p>Разбираем основные моменты, связанные с написанием собственных конструкторов классов в Java.</p>
5 <p>Разбираем основные моменты, связанные с написанием собственных конструкторов классов в Java.</p>
6 <p>Программист, преподаватель Skillbox. Пишет про Java. </p>
6 <p>Программист, преподаватель Skillbox. Пишет про Java. </p>
7 <p>Процесс создания объектов - один из важных аспектов программирования на Java. Под созданием подразумевают как минимум две основные операции: создание нового объекта класса и инициализацию полей объекта.</p>
7 <p>Процесс создания объектов - один из важных аспектов программирования на Java. Под созданием подразумевают как минимум две основные операции: создание нового объекта класса и инициализацию полей объекта.</p>
8 <p>В этой статье мы рассмотрим:</p>
8 <p>В этой статье мы рассмотрим:</p>
9 <ul><li><a>что такое конструктор класса Java</a>;</li>
9 <ul><li><a>что такое конструктор класса Java</a>;</li>
10 <li><a>как работают параметризованные конструкторы</a>;</li>
10 <li><a>как работают параметризованные конструкторы</a>;</li>
11 <li><a>как происходит инициализация полей объекта</a>.</li>
11 <li><a>как происходит инициализация полей объекта</a>.</li>
12 </ul><p>Конструктор - это специальный метод, который имеет имя, совпадающее с именем класса, и вызывается при создании экземпляра объекта совместно с оператором<em>new.</em>Результатом работы этого метода всегда является экземпляр класса.</p>
12 </ul><p>Конструктор - это специальный метод, который имеет имя, совпадающее с именем класса, и вызывается при создании экземпляра объекта совместно с оператором<em>new.</em>Результатом работы этого метода всегда является экземпляр класса.</p>
13 <p>Следовательно, конструктор класса<em>Cat</em>называется<em>Cat()</em>. В результате работы этого конструктора будет создан новый объект класса<em>Cat.</em>Цель конструктора - правильно инициализировать объект перед его использованием.</p>
13 <p>Следовательно, конструктор класса<em>Cat</em>называется<em>Cat()</em>. В результате работы этого конструктора будет создан новый объект класса<em>Cat.</em>Цель конструктора - правильно инициализировать объект перед его использованием.</p>
14 <p>Самый простой способ создать объект - это строка вида:</p>
14 <p>Самый простой способ создать объект - это строка вида:</p>
15 <p>Рассмотрим порядок создания объекта. В этой строке выполняется три действия:</p>
15 <p>Рассмотрим порядок создания объекта. В этой строке выполняется три действия:</p>
16 <ul><li>Во-первых, задаётся переменная класса<em>Cat</em>под именем<em>murka</em>. Эта переменная ещё не определяет объект, она просто даёт возможность ссылаться на него.</li>
16 <ul><li>Во-первых, задаётся переменная класса<em>Cat</em>под именем<em>murka</em>. Эта переменная ещё не определяет объект, она просто даёт возможность ссылаться на него.</li>
17 <li>Во-вторых, создаётся физическая копия объекта, а ссылка на него присваивается переменной<em>murka</em>. Это делается с помощью оператора<em>new</em>. Оператор<em>new</em>динамически - во время выполнения программы - выделяет память для объекта и возвращает ссылку на него, которая представляет собой адрес области памяти.</li>
17 <li>Во-вторых, создаётся физическая копия объекта, а ссылка на него присваивается переменной<em>murka</em>. Это делается с помощью оператора<em>new</em>. Оператор<em>new</em>динамически - во время выполнения программы - выделяет память для объекта и возвращает ссылку на него, которая представляет собой адрес области памяти.</li>
18 <li>В-третьих, ссылка на объект сохраняется в переменной. За это отвечает оператор<em>=</em>.</li>
18 <li>В-третьих, ссылка на объект сохраняется в переменной. За это отвечает оператор<em>=</em>.</li>
19 </ul><p>Приведённый выше код можно разбить на две строки:</p>
19 </ul><p>Приведённый выше код можно разбить на две строки:</p>
20 Cat murka; // объявление ссылки на объект murka = new Cat(); // выделение памяти для объекта типа Cat и присвоение значения ссылке<p>Выполнять отдельно создание объекта не имеет смысла, так как в таком случае мы не сможем с ним работать - ведь ссылка на него нигде не сохранится.</p>
20 Cat murka; // объявление ссылки на объект murka = new Cat(); // выделение памяти для объекта типа Cat и присвоение значения ссылке<p>Выполнять отдельно создание объекта не имеет смысла, так как в таком случае мы не сможем с ним работать - ведь ссылка на него нигде не сохранится.</p>
21 new Cat(); // такая операция не позволит дальше работать с объектом<p>В первой строке кода переменная<em>murka</em> объявляется как ссылка на объект типа<em>Cat</em>. Здесь важно понять, что объектная переменная фактически не содержит никакого объекта. Значение любой объектной переменной в Java представляет собой ссылку на объект, размещённый в памяти. В данный момент переменная<em>murka</em>пока ещё не ссылается на объект (содержит пустое значение<em>null</em>).</p>
21 new Cat(); // такая операция не позволит дальше работать с объектом<p>В первой строке кода переменная<em>murka</em> объявляется как ссылка на объект типа<em>Cat</em>. Здесь важно понять, что объектная переменная фактически не содержит никакого объекта. Значение любой объектной переменной в Java представляет собой ссылку на объект, размещённый в памяти. В данный момент переменная<em>murka</em>пока ещё не ссылается на объект (содержит пустое значение<em>null</em>).</p>
22 <p>Во второй строке кода создаётся новый объект типа<em>Cat</em>, а ссылка на него присваивается переменной<em>murka</em>. С этого момента переменная<em>murka</em>оказывается ассоциированной с объектом. Чтобы работать с объектами, нужно сначала создать их и задать их исходное состояние. Затем к этим объектам можно применять методы.</p>
22 <p>Во второй строке кода создаётся новый объект типа<em>Cat</em>, а ссылка на него присваивается переменной<em>murka</em>. С этого момента переменная<em>murka</em>оказывается ассоциированной с объектом. Чтобы работать с объектами, нужно сначала создать их и задать их исходное состояние. Затем к этим объектам можно применять методы.</p>
23 <p>Теперь взглянем на код класса<em>Cat</em>(без методов):</p>
23 <p>Теперь взглянем на код класса<em>Cat</em>(без методов):</p>
24 public class Cat { private double originWeight; private double weight; private double minWeight; private double maxWeight; public Cat() { weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } }<p>Видно, что все объявленные в классе переменные в результате работы конструктора получили значение - и объект готов к использованию. Мы можем вызывать различные методы класса, просматривать значение переменных - никаких ошибок не появится.</p>
24 public class Cat { private double originWeight; private double weight; private double minWeight; private double maxWeight; public Cat() { weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } }<p>Видно, что все объявленные в классе переменные в результате работы конструктора получили значение - и объект готов к использованию. Мы можем вызывать различные методы класса, просматривать значение переменных - никаких ошибок не появится.</p>
25 <p><strong>Важно.</strong> Конструкторы имеются у всех классов, независимо от того, определите вы их или нет, поскольку Java автоматически предоставляет конструктор, используемый по умолчанию (без параметров) и инициализирующий все переменные экземпляра их значениями, заданными по умолчанию.</p>
25 <p><strong>Важно.</strong> Конструкторы имеются у всех классов, независимо от того, определите вы их или нет, поскольку Java автоматически предоставляет конструктор, используемый по умолчанию (без параметров) и инициализирующий все переменные экземпляра их значениями, заданными по умолчанию.</p>
26 <p><strong>Для справки.</strong> Для числовых типов данных значением по умолчанию является нулевое, для типа<em>boolean</em> - логическое значение<em>false</em>, а для ссылочных типов - пустое значение<em>null</em>.</p>
26 <p><strong>Для справки.</strong> Для числовых типов данных значением по умолчанию является нулевое, для типа<em>boolean</em> - логическое значение<em>false</em>, а для ссылочных типов - пустое значение<em>null</em>.</p>
27 <p>Но как только вы определите свой собственный конструктор, конструктор по умолчанию предоставляться не будет. Следовательно, если мы удалим конструктор из класса<em>Cat</em>и попытаемся создать объект через<em>new Cat()</em>, то объект будет создан, но все переменные в классе получат значения по умолчанию.</p>
27 <p>Но как только вы определите свой собственный конструктор, конструктор по умолчанию предоставляться не будет. Следовательно, если мы удалим конструктор из класса<em>Cat</em>и попытаемся создать объект через<em>new Cat()</em>, то объект будет создан, но все переменные в классе получат значения по умолчанию.</p>
28 <p>Сразу отметим, что полагаться на действия по умолчанию не следует. Если поля инициализируются неявно, программа становится менее понятной.</p>
28 <p>Сразу отметим, что полагаться на действия по умолчанию не следует. Если поля инициализируются неявно, программа становится менее понятной.</p>
29 <p>В предыдущем примере использовался конструктор без параметров, который также называется<em>конструктором по умолчанию</em>. В некоторых случаях этого оказывается достаточно, но зачастую конструктор должен иметь один или несколько параметров. Добавление параметров в конструктор происходит точно так же, как и добавление параметров в метод, - для этого достаточно объявить их в скобках после имени конструктора.</p>
29 <p>В предыдущем примере использовался конструктор без параметров, который также называется<em>конструктором по умолчанию</em>. В некоторых случаях этого оказывается достаточно, но зачастую конструктор должен иметь один или несколько параметров. Добавление параметров в конструктор происходит точно так же, как и добавление параметров в метод, - для этого достаточно объявить их в скобках после имени конструктора.</p>
30 <p><strong>Важно.</strong> Класс может иметь несколько различных конструкторов. Они (кстати, как и методы) отличаются между собой количеством, типом и порядком следования параметров. Если в классе несколько конструкторов с разным набором параметров, это называется<em>перегрузкой конструктора</em>.</p>
30 <p><strong>Важно.</strong> Класс может иметь несколько различных конструкторов. Они (кстати, как и методы) отличаются между собой количеством, типом и порядком следования параметров. Если в классе несколько конструкторов с разным набором параметров, это называется<em>перегрузкой конструктора</em>.</p>
31 <p>Например, конструкторы ниже являются разными и вполне могут существовать в одном классе и иметь разную логику:</p>
31 <p>Например, конструкторы ниже являются разными и вполне могут существовать в одном классе и иметь разную логику:</p>
32 public Cat(String name, int weight){ //код... } public Cat(String name, int weight, double tailLength){ //код... }<p>Заметьте, что конструкторы ниже для компилятора одинаковы и вызовут ошибку при запуске программы (тип, количество и порядок следования параметров идентичны):</p>
32 public Cat(String name, int weight){ //код... } public Cat(String name, int weight, double tailLength){ //код... }<p>Заметьте, что конструкторы ниже для компилятора одинаковы и вызовут ошибку при запуске программы (тип, количество и порядок следования параметров идентичны):</p>
33 public Cat(String newName, int weight){ //код... } public Cat(String newName, int initialOriginWeight){ //код... }<p>Разберём пример применения параметризованного конструктора класса<em>Cat</em>с добавленным полем имени:</p>
33 public Cat(String newName, int weight){ //код... } public Cat(String newName, int initialOriginWeight){ //код... }<p>Разберём пример применения параметризованного конструктора класса<em>Cat</em>с добавленным полем имени:</p>
34 public class Cat { private double originWeight; private double weight; private double minWeight; private double maxWeight; private String name; public Cat() { name = "Безымянная"; weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } public Cat(String name) { this.name = name; weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } }<p>Как видно, в этих конструкторах очень много кода повторяется, что в целом не очень удобно и повышает вероятность ошибки: ведь если нам потребуется поменять один конструктор, необходимо будет не забыть внести изменения в другой.</p>
34 public class Cat { private double originWeight; private double weight; private double minWeight; private double maxWeight; private String name; public Cat() { name = "Безымянная"; weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } public Cat(String name) { this.name = name; weight = 1500.0 + 3000.0 * Math.random(); originWeight = weight; minWeight = 1000.0; maxWeight = 9000.0; } }<p>Как видно, в этих конструкторах очень много кода повторяется, что в целом не очень удобно и повышает вероятность ошибки: ведь если нам потребуется поменять один конструктор, необходимо будет не забыть внести изменения в другой.</p>
35 <p>Для этого применяется вызов конструктора из конструктора с использованием ключевого слова<em>this</em>, которое означает ссылку на текущий объект. Обратиться к конструктору из другого конструктора можно через вызов<em>this()</em> - так будет выполнен конструктор без параметров. Если же нужен конструктор с параметрами, их указывают в скобках.</p>
35 <p>Для этого применяется вызов конструктора из конструктора с использованием ключевого слова<em>this</em>, которое означает ссылку на текущий объект. Обратиться к конструктору из другого конструктора можно через вызов<em>this()</em> - так будет выполнен конструктор без параметров. Если же нужен конструктор с параметрами, их указывают в скобках.</p>
36 <p>Применять ключевое слово<em>this</em>для вызова другого конструктора очень удобно - нужно лишь один раз написать общий код для конструирования объекта.</p>
36 <p>Применять ключевое слово<em>this</em>для вызова другого конструктора очень удобно - нужно лишь один раз написать общий код для конструирования объекта.</p>
37 <p><strong>Важно.</strong> Вызов другого конструктора всегда должен стоять первой строкой в конструкторе.</p>
37 <p><strong>Важно.</strong> Вызов другого конструктора всегда должен стоять первой строкой в конструкторе.</p>
38 <p>Вот пример оптимизации кода первого из конструкторов:</p>
38 <p>Вот пример оптимизации кода первого из конструкторов:</p>
39 public Cat() { this("Безымянная"); //конструктор по умолчанию сокращён до одной строки }<p><strong>Обратите внимание.</strong> Несколько слов насчёт ключевого слова<em>this</em>. Синтаксис языка Java не запрещает использовать имена параметров или локальных переменных, совпадающие с именами переменных экземпляра (класса). В таком случае говорят, что локальная переменная или параметр<em>скрывает</em>переменную экземпляра. При этом доступ к скрытой переменной экземпляра обеспечивается с помощью ключевого слова<em>this</em>.</p>
39 public Cat() { this("Безымянная"); //конструктор по умолчанию сокращён до одной строки }<p><strong>Обратите внимание.</strong> Несколько слов насчёт ключевого слова<em>this</em>. Синтаксис языка Java не запрещает использовать имена параметров или локальных переменных, совпадающие с именами переменных экземпляра (класса). В таком случае говорят, что локальная переменная или параметр<em>скрывает</em>переменную экземпляра. При этом доступ к скрытой переменной экземпляра обеспечивается с помощью ключевого слова<em>this</em>.</p>
40 <p>Приведённый ниже пример конструктора класса<em>Cat</em>показывает, каким образом лучше выполнять присваивание переданных в конструктор параметров переменным класса:</p>
40 <p>Приведённый ниже пример конструктора класса<em>Cat</em>показывает, каким образом лучше выполнять присваивание переданных в конструктор параметров переменным класса:</p>
41 public Cat(String name) { this.name = name; //значение параметра присваивается переменной класса // остальной код конструктора... }<p>При выборе имён параметров рекомендую в первую очередь ориентироваться на читаемость кода. Чтобы, взглянув на этот код спустя некоторое время, вы сразу могли понять, что здесь происходит.</p>
41 public Cat(String name) { this.name = name; //значение параметра присваивается переменной класса // остальной код конструктора... }<p>При выборе имён параметров рекомендую в первую очередь ориентироваться на читаемость кода. Чтобы, взглянув на этот код спустя некоторое время, вы сразу могли понять, что здесь происходит.</p>
42 <p>Но в целом читаемость кода - это отдельная и довольно обширная тема.</p>
42 <p>Но в целом читаемость кода - это отдельная и довольно обширная тема.</p>
43 <p>Основная задача конструкторов - подготовка объекта к работе с ним и установка значений для полей (переменных) объекта. Но есть и другие варианты установки значения для полей. Это<em>явная инициализация</em>и так называемые<em>блоки инициализации</em>.</p>
43 <p>Основная задача конструкторов - подготовка объекта к работе с ним и установка значений для полей (переменных) объекта. Но есть и другие варианты установки значения для полей. Это<em>явная инициализация</em>и так называемые<em>блоки инициализации</em>.</p>
44 <p><strong>Явная инициализация</strong> - это возможность присвоить полю соответствующее значение указанным ниже образом:</p>
44 <p><strong>Явная инициализация</strong> - это возможность присвоить полю соответствующее значение указанным ниже образом:</p>
45 public class Cat { ... private double minWeight = 1000.0; private double maxWeight = 9000.0;<p>Здесь присваивание выполняется<em>до вызова</em>конструктора. Такой подход оказывается полезным, когда требуется, чтобы поле имело конкретное значение - независимо от вызова конструктора класса.</p>
45 public class Cat { ... private double minWeight = 1000.0; private double maxWeight = 9000.0;<p>Здесь присваивание выполняется<em>до вызова</em>конструктора. Такой подход оказывается полезным, когда требуется, чтобы поле имело конкретное значение - независимо от вызова конструктора класса.</p>
46 <p><strong>Блоки инициализации</strong>выглядят так:</p>
46 <p><strong>Блоки инициализации</strong>выглядят так:</p>
47 public class Cat { private static int count; private int id; ... { id = count; count++; }<p>Такой блок выполняется каждый раз, когда создаётся объект данного класса. В этом примере начальное значение поля<em>id</em>задаётся в блоке инициализации объекта. Причём неважно, какой именно конструктор используется для создания экземпляра класса. Первым выполняется блок инициализации, а вслед за ним - тело конструктора.</p>
47 public class Cat { private static int count; private int id; ... { id = count; count++; }<p>Такой блок выполняется каждый раз, когда создаётся объект данного класса. В этом примере начальное значение поля<em>id</em>задаётся в блоке инициализации объекта. Причём неважно, какой именно конструктор используется для создания экземпляра класса. Первым выполняется блок инициализации, а вслед за ним - тело конструктора.</p>
48 <p>Блоки инициализации могут быть и статическими. Перед ними пишется ключевое слово<em>static</em><strong>.</strong></p>
48 <p>Блоки инициализации могут быть и статическими. Перед ними пишется ключевое слово<em>static</em><strong>.</strong></p>
49 <p>В таком случае блок инициализации будет выполнен при первом обращении к этому классу. Попробуйте выполнить вот такой код:</p>
49 <p>В таком случае блок инициализации будет выполнен при первом обращении к этому классу. Попробуйте выполнить вот такой код:</p>
50 public class Main { static { System.out.println("Static"); } public static void main(String[] args) { System.out.println("Test"); } }<p>При таком многообразии способов инициализации полей класса трудно отследить все возможные пути создания объекта. Поэтому рассмотрим подробнее действия, которые происходят при вызове конструктора:</p>
50 public class Main { static { System.out.println("Static"); } public static void main(String[] args) { System.out.println("Test"); } }<p>При таком многообразии способов инициализации полей класса трудно отследить все возможные пути создания объекта. Поэтому рассмотрим подробнее действия, которые происходят при вызове конструктора:</p>
51 <p>1. Если в первой строке кода одного конструктора вызывается второй конструктор, то второй конструктор выполняется с предоставляемыми аргументами.</p>
51 <p>1. Если в первой строке кода одного конструктора вызывается второй конструктор, то второй конструктор выполняется с предоставляемыми аргументами.</p>
52 <p>2. Иначе:</p>
52 <p>2. Иначе:</p>
53 <ul><li>все поля инициализируются значениями, предусмотренными по умолчанию (<em>0</em>,<em>false</em>или<em>null</em>);</li>
53 <ul><li>все поля инициализируются значениями, предусмотренными по умолчанию (<em>0</em>,<em>false</em>или<em>null</em>);</li>
54 <li>инициализируются все поля и блоки инициализации в порядке их следования в объявлении класса.</li>
54 <li>инициализируются все поля и блоки инициализации в порядке их следования в объявлении класса.</li>
55 </ul><p>3. Выполняется тело конструктора.</p>
55 </ul><p>3. Выполняется тело конструктора.</p>
56 <p>Естественно, код, отвечающий за инициализацию полей, нужно организовать так, чтобы в нём можно было легко разобраться. Например, было бы странным, если бы вызов конструкторов класса зависел от порядка объявления полей. Такой подход чреват ошибками.</p>
56 <p>Естественно, код, отвечающий за инициализацию полей, нужно организовать так, чтобы в нём можно было легко разобраться. Например, было бы странным, если бы вызов конструкторов класса зависел от порядка объявления полей. Такой подход чреват ошибками.</p>
57 <p>Инициализировать статическое поле следует, задавая его начальное значение или используя статический блок инициализации - если для инициализации статического поля требуется сложный код.</p>
57 <p>Инициализировать статическое поле следует, задавая его начальное значение или используя статический блок инициализации - если для инициализации статического поля требуется сложный код.</p>
58 <p><strong>На заметку.</strong> Важный момент, о котором часто забывают при написании собственных конструкторов, -<em>в конструкторах не должно содержаться никакой бизнес-логики</em>. Их задача - корректное создание объектов и подготовка их к дальнейшему использованию. Вся логика должна находиться в соответствующих методах.</p>
58 <p><strong>На заметку.</strong> Важный момент, о котором часто забывают при написании собственных конструкторов, -<em>в конструкторах не должно содержаться никакой бизнес-логики</em>. Их задача - корректное создание объектов и подготовка их к дальнейшему использованию. Вся логика должна находиться в соответствующих методах.</p>
59 <p>Для изучения - пример, который содержит все элементы, описанные в статье. В нём есть:</p>
59 <p>Для изучения - пример, который содержит все элементы, описанные в статье. В нём есть:</p>
60 <ul><li>применение конструктора без аргументов;</li>
60 <ul><li>применение конструктора без аргументов;</li>
61 <li>перегрузка конструкторов;</li>
61 <li>перегрузка конструкторов;</li>
62 <li>вызов одного конструктора из другого;</li>
62 <li>вызов одного конструктора из другого;</li>
63 <li>инициализация полей явным способом;</li>
63 <li>инициализация полей явным способом;</li>
64 <li>инициализация полей с использованием блока инициализации.</li>
64 <li>инициализация полей с использованием блока инициализации.</li>
65 </ul>public class Cat { private static int count = 0; static { //статический блок инициализации count = 1; } private double maxWeight = 9000.0; //явная инициализация private double minWeight = 1000.0; private double originWeight; private double weight; private String name; private int id; { //блок инициализации id = count; //count к этому моменту получит значение 1 count++; } //перегрузка конструкторов public Cat() { //вызов конструктора с именем, в качестве параметра передаётся имя по умолчанию this("Безымянная"); } public Cat(String newName) { //вызов конструктора с именем и весом, вес генерируется случайным образом this(newName, 1500.0 + 3000.0 * Math.random()); } public Cat(String newName, double initialWeight){ name = newName; weight = initialWeight; } }<p>Мы рассмотрели все возможные пути создания объектов и инициализации их полей в Java. Теперь вы сможете создавать свои объекты любым из представленных способов, а также осознанно читать в коде порядок создания объектов других классов.</p>
65 </ul>public class Cat { private static int count = 0; static { //статический блок инициализации count = 1; } private double maxWeight = 9000.0; //явная инициализация private double minWeight = 1000.0; private double originWeight; private double weight; private String name; private int id; { //блок инициализации id = count; //count к этому моменту получит значение 1 count++; } //перегрузка конструкторов public Cat() { //вызов конструктора с именем, в качестве параметра передаётся имя по умолчанию this("Безымянная"); } public Cat(String newName) { //вызов конструктора с именем и весом, вес генерируется случайным образом this(newName, 1500.0 + 3000.0 * Math.random()); } public Cat(String newName, double initialWeight){ name = newName; weight = initialWeight; } }<p>Мы рассмотрели все возможные пути создания объектов и инициализации их полей в Java. Теперь вы сможете создавать свои объекты любым из представленных способов, а также осознанно читать в коде порядок создания объектов других классов.</p>
66 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
66 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>