Перейти к содержимому

Как создать конструктор

  • автор:

printКлассы

printКонструктор по умолчанию

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

имя класса();
имя класса(тип параметра1 =значение по умолчанию, тип параметра2 =значение по умолчанию, …);

Задача: Такая же, как у любого конструктора – инициализировать все поля.

Назначение: Без этого конструктора невозможно создать массив объектов.

  • Если в классе не определен ни один конструктор, компилятор создает сам конструктор по умолчанию. В этом автоматически созданном конструкторе вызываются конструкторы по умолчанию для всех базовых классов и полей.
  • Если у какого-то поля или базового класса нет конструктора по умолчанию, компилятор при попытке самостоятельно создать конструктор по умолчанию выдаст сообщение об ошибке.
class String < int len; char * str; public: String(const char *s=""); // конструктор по умолчанию . >; String::String(const char *s):len(strlen(s)),str(new char[len+1]) < strcpy(str,s); >int main() < String a; //создание строки, используя конструктор по умолчанию String b[100]; // создание массива строк String c(); // это не определение объекта, а объявление функции без параметров. >

Как создать конструктор

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

В прошлой теме был разработан следующий класс:

#include class Person < public: std::string name; unsigned age; void print() < std::cout >; int main() < Person person; // вызов конструктора person.name = "Tom"; person.age = 22; person.print(); >

Здесь при создании объекта класса Person, который называется person

Person person;

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

Теперь определим свой конструктор. Например, в примере выше мы устанавливаем значения для полей класса Person. Но, допустим, мы хотим, чтобы при создании объекта эти поля уже имели некоторые значения по умолчанию. Для этой цели определим конструктор:

#include class Person < public: std::string name; unsigned age; void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout >; int main() < Person tom("Tom", 38); // создаем объект - вызываем конструктор tom.print(); >

Теперь в классе Person определен конструктор:

Person(std::string p_name, unsigned p_age)

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

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

Вызов конструктора получает значения для параметров и возвращает объект класса:

Person tom("Tom", 38);

После этого вызова у объекта person для поля name будет определено значение «Tom», а для поля age — значение 38. Вполедствии мы также сможем обращаться к этим полям и переустанавливать их значения.

В качестве альтернативы для создания объекта можно использовать инициализатор в фигурных скобках:

Person tom;

Тажке можно присвоить объекту результат вызова конструктора:

Person tom = Person("Tom", 38);

По сути она будет эквивалетна предыдущей.

Консольный вывод определенной выше программы:

Person has been created Name: Tom Age: 38

Конструкторы облегчают нам создание нескольких объектов, которые должны иметь разные значения:

#include class Person < public: std::string name; unsigned age; void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout >; int main() < Person tom; Person bob; Person sam; tom.print(); bob.print(); sam.print(); >

Здесь создаем три разных объекта класса Person (условно трех разных людей), и соответственно в данном случае консольный вывод будет следующим:

Person has been created Person has been created Person has been created Name: Tom Age: 38 Name: Bob Age: 42 Name: Sam Age: 25

Определение нескольких конструкторов

Подобным образом мы можем определить несколько конструкторов и затем их использовать:

#include class Person < std::string name<>; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; >Person(std::string p_name) < name = p_name; age = 18; >Person() < name = "Undefined"; age = 18; >>; int main() < Person tom; // вызываем конструктор Person(std::string p_name, unsigned p_age) Person bob; // вызываем конструктор Person(std::string p_name) Person sam; // вызываем конструктор Person() tom.print(); bob.print(); sam.print(); >

В классе Person определено три конструктора, и в функции все эти конструкторы используются для создания объектов:

Name: Tom Age: 38 Name: Bob Age: 18 Name: Undefined Age: 18

Хотя пример выше прекрасно работает, однако мы можем заметить, что все три конструктора выполняют фактически одни и те же действия — устанавливают значения переменных name и age. И в C++ можем сократить их определения, вызова из одного конструктора другой и тем самым уменьшить объем кода:

#include class Person < std::string name<>; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout Person(std::string p_name): Person(p_name, 18) // вызов первого конструктора < std::cout Person(): Person(std::string("Undefined")) // вызов второго конструктора < std::cout >; int main() < Person sam; // вызываем конструктор Person() sam.print(); >

Запись Person(string p_name): Person(p_name, 18) представляет вызов конструктора, которому передается значение параметра p_name и число 18. То есть второй конструктор делегирует действия по инициализации переменных первому конструктору. При этом второй конструктор может дополнительно определять какие-то свои действия.

Таким образом, следующее создание объекта

Person sam;

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

Данная техника еще называется делегированием конструктора , поскольку мы делегируем инициализацию другому конструктору.

Параметры по умолчанию

Как и другие функции, конструкторы могут иметь параметры по умолчанию:

#include class Person < std::string name; unsigned age; public: // передаем значения по умолчанию Person(std::string p_name = "Undefined", unsigned p_age = 18) < name = p_name; age = p_age; >void print() < std::cout >; int main() < Person tom; Person bob; Person sam; tom.print(); // Name: Tom Age: 38 bob.print(); // Name: Bob Age: 18 sam.print(); // Name: Undefined Age: 18 >

Инициализация констант и списки инициализации

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

class Person < const std::string name; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; >>;

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

#include class Person < const std::string name; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) : name  < age = p_age; >>; int main() < Person tom; tom.print(); // Name: Tom Age: 38 >

Списки инициализации представляют перечисления инициализаторов для каждой из переменных и констант через двоеточие после списка параметров конструктора:

Person(std::string p_name, unsigned p_age) : name

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

Person(std::string p_name, unsigned p_age) : name(p_name)

Списки инициализации пободным образом можно использовать и для присвоения значений переменным:

class Person < const std::string name; unsigned age; public: void print() < std::cout Person(std::string p_name, unsigned p_age) : name(p_name), age(p_age) < >>;

При использовании списков инициализации важно учитывать, что передача значений должна идти в том порядке, в котором константы и переменные определены в классе. То есть в данном случае в классе сначала определена константа name, а потом переменная age. Соответственно в таком же порядке идет передача им значений. Поэтому при добавлении дополнительных полей или изменения порядка существующих придется следить, чтобы все инициализировалось в належащем порядке.

Конструктор

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

Конструктор инициализирует объект непосредственно во время создания. Имя конструктора совпадает с именем класса, включая регистр, а по синтаксису конструктор похож на метод без возвращаемого значения.

 private int Cat(); // так выглядит метод по имени Cat Cat(); // так выглядит конструктор класса Cat 

В отличие от метода, конструктор никогда ничего не возвращает.

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

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

 class Box < int width; // ширина коробки int height; // высота коробки int depth; // глубина коробки // Конструктор Box() < width = 10; height = 10; depth = 10; >// вычисляем объём коробки int getVolume() < return width * height * depth; >> 

Мы временно удалили метод setDim() и добавили конструктор. Посмотрим, что получится:

 Box catBox = new Box(); mInfoTextView.setText("Объём коробки: " + catBox.getVolume()); 

Программа выведет объём коробки, хотя мы не задавали никаких размеров для неё. Благодаря конструктору любая создаваемая коробка будет иметь какой-то зафиксированный объём.

Естественно, вы можете вернуть обратно метод setDim() (см. статью про классы) и установить свои размеры для коробки:

 Box catBox = new Box(); // установим свои размеры для коробки catBox.setDim(10, 20, 30); mInfoTextView.setText("Объём коробки: " + catBox.getVolume()); 

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

Возникает вопрос — но ведь сначала при создании класса мы не создавали конструктор, однако код new Box() работал. Дело в том, что если конструктор не определён явно, то Java создаст конструктор, который будет использоваться по умолчанию. В этом случае он просто присвоит всем переменным нулевые значения. Если вы создали сами конструктор, то конструктор по умолчанию использоваться не будет.

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

 Cat cat = new Cat(8); // коту 8 лет 

Если Cat(int) является единственным конструктором класса, то компилятор не позволит создавать объекты Cat каким-либо другим способом.

Однако вернёмся к коробкам для котов. Созданный нами конструктор не особо полезен, так как создаёт одинаковые коробки. Создадим конструктор с параметрами в классе Box и закомментируйте первый конструктор без параметров:

 // Второй конструктор Box(int w, int h, int d)

Если класс содержит один конструктор с параметрами, то вам придётся обязательно указать значения при объявлении класса:

 // Это конструктор теперь не допустим // Box catBox = new Box(); // В конструкторе нужно указать значения размеров коробки Box catBox = new Box(100, 200, 100); mInfoTextView.setText("Объём коробки: " + catBox.getVolume()); 

Кстати, с таким конструктором метод setDim() нам уже не нужен. Мы можем задать размеры коробки сразу в конструкторе. Так как скорее всего коробка постоянна и не меняет своих размеров, то метод, пожалуй, лишний. Но если мы будем менять размеры коробки, то метод придётся оставить.

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

 Box defaultBox = new Box(); mInfoTextView.setText("Объём стандартной коробки: " + defaultBox.getVolume()); Box bigBox = new Box(100, 200, 200); mInfoTextView.append("\nОбъём большой коробки: " + bigBox.getVolume()); 

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

Например, мы можем создать ещё один конструктор специально для коробки в виде куба, где все стороны равны:

 // Третий конструктор для куба Box(int len)

Вычисляем размер куба:

 Box cube = new Box(5); int vol = cube.getVolume(); mInfoTextView.setText("Объём куба: " + vol); 

Используем объект в качестве параметров

Мы пока использовали в качестве параметров в конструкторах простые типы. Но можно передать и объект самого класса. Добавим ещё один конструктор:

 // Используем объект типа Box Box(Box ob)

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

 Box box1 = new Box(100, 200, 100); Box cloneBox = new Box(box1); int vol = cloneBox.getVolume(); mInfoTextView.setText("Объём коробки: " + vol); 

Класс Box (исходник)

 package ru.alexanderklimov.box; class Box < int width; // ширина коробки int height; // высота коробки int depth; // глубина коробки // Конструктор Box() < width = 10; height = 10; depth = 10; >// Второй конструктор Box(int w, int h, int d) < width = w; height = h; depth = d; >// Третий конструктор для куба Box(int len) < width = height = depth = len; >// Используем объект типа Box Box(Box ob) < width = ob.width; height = ob.height; depth = ob.depth; >// вычисляем объём коробки int getVolume() < return width * height * depth; >// устанавливаем размеры коробки void setDim(int w, int h, int d) < width = w; height = h; depth = d; >> 

Вызов перегруженных конструкторов через this()

Имея дело с перегруженными конструкторами, удобно один конструктор вызывать из другого через ключевое слово this. При выполнении конструктора this() сначала выполняется перегруженный конструктор, который соответствует списку параметров. Затем выполняются операторы, находящиеся внутри исходного конструктора, если таковые существуют. Вызов конструктора this() должен быть первым оператором в конструкторе.

Для начала создадим класс, который не использует конструктор this(), чтобы понять разницу.

 class Cat < int age; int birthday; // Инициализируем переменные явно Cat(int i, int j) < age = i; birthday = j; >// Инициализируем переменные одним и тем значением Cat(int i) < age = i; birthday = i; >// Присвоим значения по умолчанию 0 Cat() < age = 0; birthday = 0; >> 

Мы создали класс с тремя конструкторами. Перепишем класс, используя конструктор this().

 class Cat < int age; int birthday; // Инициализируем переменные явно Cat(int i, int j) < age = i; birthday = j; >// Инициализируем переменные одним и тем значением Cat(int i) < this(i, i); // вызывается Cat(i, i); >// Присвоим значения по умолчанию 0 Cat() < this(0); // вызывается Cat(0); >> 

У нас теперь только один конструктор, который присваивает значения полям — Cat(int, int). Что происходит при выполнении оператора:

 Cat cat = new Cat(8); 

Вызов конструктора Cat(8) приводит к выполнению конструктора this(8, 8), что равнозначно вызову конструктора Cat(8, 8).

Что происходит при выполнении оператора:

 Cat cat2 = new Cat(); 

В этом случае вызывается конструктор this(0), что приводит к выполнению конструктора Cat(0), поскольку именно эта версия конструктора подходит по списку параметров. При этом конструктор Cat(0) по сути вызывает конструктор Cat(0, 0).

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

Но следует быть осторожным, так как конструкторы, которые вызывают конструктор this(), выполняются немного медленнее.

Закрытый конструктор

Иногда класс создаётся только для хранения каких-то статических полей и статических методов. Таким классам принято давать имена Utils, но это не обязательно. Такому классу не нужен конструктор, но если автор класса его не создал, то система сама создаст конструктор по умолчанию. Такой конструктор не имеет смысла, а также может послужить источником ошибок. Чтобы предохраниться от подобной проблемы вы сами явно должны создать пустрой конструктор и сделать его закрытым.

 public class Utils < private Utils() < throw new AssertionError(); >. //ваш правильный код // Неправильный код, только для демонстрации! public static void someMethod() < Utils utils = new Utils(); utils.toString(); >> 

Строка throw new AssertionError() не является обязательной, но она поможет выявить ошибку, если вы вызовете конструктор в самом классе. Компилятор пропустит такой вариант, но программа завершится с ошибкой.

 Utils.someMethod(); // программа закроется с ошибкой 

Подкласс для данного класса вы создать не сможете.

Создание объектов и конструкторы

Разбираем основные моменты, связанные с написанием собственных конструкторов классов в Java.

Василий Порядин

Василий Порядин
Программист, преподаватель Skillbox. Пишет про Java.

Процесс создания объектов — один из важных аспектов программирования на Java. Под созданием подразумевают как минимум две основные операции: создание нового объекта класса и инициализацию полей объекта.

В этой статье мы рассмотрим:

  • что такое конструктор класса Java;
  • как работают параметризованные конструкторы;
  • как происходит инициализация полей объекта.

Что такое конструкторы классов

Конструктор — это специальный метод, который имеет имя, совпадающее с именем класса, и вызывается при создании экземпляра объекта совместно с оператором new. Результатом работы этого метода всегда является экземпляр класса.

Следовательно, конструктор класса Cat называется Cat(). В результате работы этого конструктора будет создан новый объект класса Cat. Цель конструктора — правильно инициализировать объект перед его использованием.

Самый простой способ создать объект — это строка вида:

Cat murka = new Cat();

Рассмотрим порядок создания объекта. В этой строке выполняется три действия:

  • Во-первых, задаётся переменная класса Cat под именем murka. Эта переменная ещё не определяет объект, она просто даёт возможность ссылаться на него.
  • Во-вторых, создаётся физическая копия объекта, а ссылка на него присваивается переменной murka. Это делается с помощью оператора new. Оператор new динамически — во время выполнения программы — выделяет память для объекта и возвращает ссылку на него, которая представляет собой адрес области памяти.
  • В-третьих, ссылка на объект сохраняется в переменной. За это отвечает оператор =.

Приведённый выше код можно разбить на две строки:

Cat murka; // объявление ссылки на объект murka = new Cat(); // выделение памяти для объекта типа Cat и присвоение значения ссылке

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

new Cat(); // такая операция не позволит дальше работать с объектом

В первой строке кода переменная murka объявляется как ссылка на объект типа Cat. Здесь важно понять, что объектная переменная фактически не содержит никакого объекта. Значение любой объектной переменной в Java представляет собой ссылку на объект, размещённый в памяти. В данный момент переменная murka пока ещё не ссылается на объект (содержит пустое значение null).

Во второй строке кода создаётся новый объект типа Cat, а ссылка на него присваивается переменной murka. С этого момента переменная murka оказывается ассоциированной с объектом. Чтобы работать с объектами, нужно сначала создать их и задать их исходное состояние. Затем к этим объектам можно применять методы.

Теперь взглянем на код класса Cat (без методов):

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

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

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

Для справки. Для числовых типов данных значением по умолчанию является нулевое, для типа boolean — логическое значение false, а для ссылочных типов — пустое значение null.

Но как только вы определите свой собственный конструктор, конструктор по умолчанию предоставляться не будет. Следовательно, если мы удалим конструктор из класса Cat и попытаемся создать объект через new Cat(), то объект будет создан, но все переменные в классе получат значения по умолчанию.

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

Как работают параметризованные конструкторы

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

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

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

public Cat(String name, int weight)< //код. > public Cat(String name, int weight, double tailLength)< //код. >

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

public Cat(String newName, int weight)< //код. > public Cat(String newName, int initialOriginWeight)< //код. >

Разберём пример применения параметризованного конструктора класса Cat с добавленным полем имени:

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

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

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

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

Важно. Вызов другого конструктора всегда должен стоять первой строкой в конструкторе.

Вот пример оптимизации кода первого из конструкторов:

public Cat() < this("Безымянная"); //конструктор по умолчанию сокращён до одной строки >

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

Приведённый ниже пример конструктора класса Cat показывает, каким образом лучше выполнять присваивание переданных в конструктор параметров переменным класса:

public Cat(String name) < this.name = name; //значение параметра присваивается переменной класса // остальной код конструктора. >

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

Но в целом читаемость кода — это отдельная и довольно обширная тема.

Инициализация полей объекта

Основная задача конструкторов — подготовка объекта к работе с ним и установка значений для полей (переменных) объекта. Но есть и другие варианты установки значения для полей. Это явная инициализация и так называемые блоки инициализации.

Явная инициализация — это возможность присвоить полю соответствующее значение указанным ниже образом:

public class Cat < . private double minWeight = 1000.0; private double maxWeight = 9000.0;

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

Блоки инициализации выглядят так:

public class Cat < private static int count; private int id; .

Такой блок выполняется каждый раз, когда создаётся объект данного класса. В этом примере начальное значение поля id задаётся в блоке инициализации объекта. Причём неважно, какой именно конструктор используется для создания экземпляра класса. Первым выполняется блок инициализации, а вслед за ним — тело конструктора.

Блоки инициализации могут быть и статическими. Перед ними пишется ключевое слово static.

static

В таком случае блок инициализации будет выполнен при первом обращении к этому классу. Попробуйте выполнить вот такой код:

public class Main < static < System.out.println("Static"); > public static void main(String[] args) < System.out.println("Test"); > >

Что в итоге

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

1. Если в первой строке кода одного конструктора вызывается второй конструктор, то второй конструктор выполняется с предоставляемыми аргументами.

  • все поля инициализируются значениями, предусмотренными по умолчанию (0, false или null);
  • инициализируются все поля и блоки инициализации в порядке их следования в объявлении класса.

3. Выполняется тело конструктора.

Естественно, код, отвечающий за инициализацию полей, нужно организовать так, чтобы в нём можно было легко разобраться. Например, было бы странным, если бы вызов конструкторов класса зависел от порядка объявления полей. Такой подход чреват ошибками.

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

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

Для изучения — пример, который содержит все элементы, описанные в статье. В нём есть:

  • применение конструктора без аргументов;
  • перегрузка конструкторов;
  • вызов одного конструктора из другого;
  • инициализация полей явным способом;
  • инициализация полей с использованием блока инициализации.
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; < //блок инициализации //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; >>

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *