Java почему конструктор заменяют методом. Конструкторы. Конструкторы по умолчанию. Вызов конструкторов класса из других конструкторов. Вызов перегруженных конструкторов через this()

Конструктор – это схожая c методом структура, назначение которой состоит в создании экземпляра класса. Характеристики конструктора:
  • Имя конструктора должно совпадать с именем класса (по договоренности, первая буква - заглавная, обычно имя существительное);
  • Конструктор имеется в любом классе. Даже если вы его не написали, компилятор Java сам создаст конструктор по умолчанию (default constructor), который будет пустым и не делает ничего, кроме вызова конструктора суперкласса.
  • Конструктор похож на метод, но не является методом, он даже не считается членом класса. Поэтому его нельзя наследовать или переопределить в подклассе;
  • Конструкторы не наследуются;
  • Конструкторов может быть несколько в классе. В этом случае конструкторы называют перегруженными;
  • Если в классе не описан конструктор, компилятор автоматически добавляет в код конструктор без параметров;
  • Конструктор не имеет возвращаемого типа, им не может быть даже тип void , если возвращается тип void , то это уже не конструктор а метод, несмотря на совпадение с именем класса.
  • В конструкторе допускается оператор return , но только пустой, без всякого возвращаемого значения;
  • В конструкторе допускается применение модификаторов доступа, можно задать один из модификаторов: public , protected , private или без модификатора.
  • Конструктор не может иметь модификаторов abstract , final , native , static или synchronized ;
  • Ключевое слово this cсылается на другой конструктор в этом же классе. Если используется, то обращение должно к нему быть первой строкой конструктора;
  • Ключевое слово super вызывает конструктор родительского класса. Если используется, должно обращение к нему быть первой строкой конструктора;
  • Если конструктор не делает вызов конструктора super класса-предка (с аргументами или без аргументов), компилятор автоматически добавляет код вызова конструктора класса-предка без аргументов;

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

Конструктор имеется в любом классе. Даже если вы его не написали, компилятор Java сам создаст конструктор по умолчанию (default constructor). Этот конструктор пустой и не делает ничего, кроме вызова конструктора суперкласса. Т.е. если написать: public class Example { } то это эквивалентно написанию: public class Example { Example () { super ; } } В данном случае явно класса предка не указано, а по умолчанию все классы Java наследуют класс Object поэтому вызывается конструктор класса Object . Если в классе определен конструктор с параметрами, а перегруженного конструктора без параметров нет, то вызов конструктора без параметров является ошибкой. Тем не менее, в Java, начиная с версии 1.5, можно использовать конструкторы с аргументами переменной длины. И если есть конструктор, имеющий аргумент переменной длины, то вызов конструктора по умолчанию ошибкой не будет. Не будет потому, что аргумент переменной длины может быть пустым. Например, следующий пример не будет компилироваться, однако если раскомментарить конструктор с аргументом переменной длины, то компиляция и запуск пройдут успешно и в результате работы строки кода DefaultDemo dd = new DefaultDemo() ; вызовется конструктор DefaultDemo(int ... v) . Естественно, что в данном случае необходимо пользоваться JSDK 1.5. Файл DefaultDemo.java class DefaultDemo { DefaultDemo (String s) { System. out. print ("DefaultDemo(String)" ) ; } /* DefaultDemo(int ... v) { System.out.println("DefaultDemo(int ...)"); } */ public static void main (String args ) { DefaultDemo dd = new DefaultDemo () ; } } Результат вывода программы при раскомментаренном конструкторе: DefaultDemo (int . . . ) Однако, в распространенном случае, когда в классе вообще не определено ни одного конструктора, вызов конструктора по умолчанию (без параметров) будет обязательным явлением, поскольку подстановка конструктора по умолчанию происходит автоматически.

При создании объекта последовательно выполняются следующие действия:
  • Ищется класс объекта среди уже используемых в программе классов. Если его нет, то он ищется во всех доступных программе каталогах и библиотеках. После обнаружения класса в каталоге или библиотеке выполняется создание, и инициализация статических полей класса. Т.е. для каждого класса статические поля инициализируются только один раз.
  • Выделяется память под объект.
  • Выполняется инициализация полей класса.
  • Отрабатывает конструктор класса.
  • Формируется ссылка на созданный и инициализированный объект. Эта ссылка и является значением выражения, создающего объект. Объект может быть создан и с помощью вызова метода newInstance() класса java.lang.Class . В этом случае используется конструктор без списка параметров.

Перегрузка конструкторов

Конструкторы одного класса могут иметь одинаковое имя и различную сигнатуру. Такое свойство называется совмещением или перегрузкой(overloading). Если класс имеет несколько конструкторов, то присутствует перегрузка конструкторов.

Параметризированные конструкторы

Сигнатура конструктора – это количество и типы параметров, а также последовательность их типов в списке параметров конструктора. Тип возвращаемого результата не учитывается. Конструктор не возвращает никаких параметров. Это положение объясняет в некотором смысле, как Java различает перегруженные конструкторы или методы. Java различает перегруженные методы не по возвращаемому типу, а по числу, типам и последовательности типов входных параметров. Конструктор не может возвращать даже тип void , иначе он превратится в обычный метод, даже не смотря на сходство с именем класса. Следующий пример демонстрирует это. Файл VoidDemo.java class VoidDemo { /** * Это конструктор */ VoidDemo () { System. out. println ("Constructor" ) ; } void VoidDemo () { System. out. println ("Method" ) ; } public static void main (String s ) { VoidDemo m = new VoidDemo () ; } } В результате программа выведет: Constructor Это лишний раз доказывает, что конструктором является метод без возвращаемых параметров. Тем не менее, для конструктора можно задать один из трех модификаторов public , private или protected . И пример теперь будет выглядеть следующим образом: Файл VoidDemo2.java class VoidDemo2 { /** * Это конструктор */ public VoidDemo2 () { System. out. println ("Constructor" ) ; } /** * А это уже обычный метод, даже не смотря на сходство с * именем класса, поскольку имеется возвращаемый тип void */ private void VoidDemo2 () { System. out. println ("Method" ) ; } public static void main (String s ) { VoidDemo2 m = new VoidDemo2 () ; } } В конструкторе разрешается записывать оператор return , но только пустой, без всякого возвращаемого значения. Файл ReturnDemo.java class ReturnDemo { /** * В конструкторе допускается использование оператора * return без параметров. */ public ReturnDemo () { System. out. println ("Constructor" ) ; return ; } public static void main (String s ) { ReturnDemo r = new ReturnDemo () ; } }

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

В Java SDK 1.5 появился долгожданный инструмент – аргументы переменной длины для конструкторов и методов(variable-length arguments). До этого переменное количество документов обрабатывалось двумя неудобными способами. Первый из них был рассчитан на то, что максимальное число аргументов ограничено небольшим количеством и заранее известно. В таком случае можно было создавать перегружаемые версии метода, по одной на каждый вариант списка передаваемых в метод аргументов. Второй способ рассчитан на неизвестное заранее и большое количество аргументов. В этом случае аргументы помещались в массив, и этот массив передавался методу. Аргументы переменной длины чаще всего задействованы в последующих манипуляциях с инициализациями переменных. Отсутствие некоторых из ожидаемых аргументов конструктора или метода удобно заменять значениями по умолчанию. Аргумент переменной длины есть массив, и обрабатывается как массив. Например, конструктор для класса Checking с переменным числом аргументов будет выглядеть так: class Checking { public Checking (int . . . n) { } } Символьная комбинация... сообщает компилятору о том, что будет использоваться переменное число аргументов, и что эти аргументы будут храниться в массиве, значение ссылки на который содержится в переменной n. Конструктор может вызываться с разным числом аргументов, включая их полное отсутствие. Аргументы автоматически помещаются в массив и передаются через n. В случае отсутствия аргументов длина массива равна 0. В список параметров наряду с аргументами переменной длины могут быть включены и обязательные параметры. В этом случае параметр, содержащий переменное число аргументов должен обязательно быть последним в списке параметров. Например: class Checking { public Checking (String s, int . . . n) { } } Вполне очевидное ограничение касается количества параметров с переменной длиной. В списке параметров должен быть только один параметр переменной длины. При наличии двух параметров переменной длины компилятору невозможно определить, где заканчивается один параметр и начинается другой. Например: class Checking { public Checking (String s, int . . . n, double . . . d) //ОШИБКА! { } } Файл Checking.java Например, есть аппаратура, способная распознавать номера автомобилей и запоминать номера квадратов местности, где побывал каждый из автомобилей за день. Необходимо из общей массы зафиксированных автомобилей отобрать те, которые в течение дня побывали в двух заданных квадратах, скажем 22 и 15, согласно карте местности. Вполне естественно, что автомобиль может в течение дня побывать во многих квадратах, а может только в одном. Очевидно, что количество посещенных квадратов ограничено физической скоростью автомобиля. Составим небольшую программу, где конструктор класса будет принимать в качестве аргументов номер автомобиля как обязательный параметр и номера посещенных квадратов местности, число которых может быть переменным. Конструктор будет проверять, не появился ли автомобиль в двух квадратах, если появился, то вывести его номер на экран.

Передача параметров в конструктор

В языках программирования существует в основном два вида параметров:
  • основные типы (примитивы);
  • ссылки на объекты.
Термин вызов по значению (call by value) означает, что конструктор получает значение, переданное ему вызывающим модулем. В противоположность этому, вызов по ссылке (call by reference) означает, что конструктор получает от вызывающего модуля адрес переменной. В языке Java используется только вызов по значению. По значению параметра и по значению ссылки параметра. Java не использует вызов по ссылке для объектов (хотя многие программисты и авторы некоторых книг это утверждают). Параметры при передаче объектов в Java осуществляются не по ссылке , а по значению ссылки на объекты ! В любом случае конструктор получает копии значений всех параметров. Конструктор не может делать со своими входными параметрами:
  • конструктор не может менять значения входных параметров основных (примитивных) типов;
  • конструктор не может изменять ссылки входных параметров;
  • конструктор не может переназначать ссылки входных параметров на новые объекты.
Конструктор может делать со своими входными параметрами:
  • изменять состояние объекта, передаваемого в качестве входного параметра.
Следующий пример доказывает, что в Java входные параметры для конструктора передаются по значению ссылки на объект. Так же в этом примере отражено то, что конструктор не может изменять ссылки входных параметров, а фактически изменяет ссылки копий входных параметров. Файл Empoyee.java class Employee { Employee (String x, String y) { String temp = x; x = y; y = temp; } public static void main (String args ) { String name1 = new String ("Alice" ) ; String name2 = new String ("Mary" ) ; Employee a = new Employee (name1, name2) ; System. out. println ("name1=" + name1) ; System. out. println ("name2=" + name2) ; } } Результат вывода программы: name1= Alice name2= Mary Если бы в языке Java для передачи объектов в качестве параметров использовался вызов по ссылке, то конструктор поменял бы в этом примере местами name1 и name2 . На самом деле конструктор не поменяет местами объектные ссылки, хранящиеся в переменных name1 и name2 . Это говорит о том, что параметры конструктора инициализируются копиями этих ссылок. Затем конструктор меняет местами уже копии. По завершении работы конструктора переменные x и y уничтожаются, а исходные переменные name1 и name2 продолжают ссылаться на прежние объекты.

Изменение параметров, передаваемых конструктору.

Конструктор не может модифицировать передаваемые параметры основных типов. Однако, конструктор может модифицировать состояние объекта, передаваемого как параметр. Например, рассмотрим следующую программу: Файл Salary1.java class Salary1 { Salary1 (int x) { x = x * 3 ; System. out. println ("x=" + x) ; } public static void main (String args ) { int value = 1000 ; Salary1 s1 = new Salary1 (value) ; System. out. println ("value=" + value) ; } } Результат вывода программы: x= 3000 value= 1000 Очевидно, что такой способ не изменит параметр основного типа. Поэтому после вызова конструктора значение переменной value остается равным 1000 . По сути, происходит три действия:
  1. Переменная x инициализируется копией значения параметра value (т.е. числом 1000).
  2. Значение переменной x утраивается – теперь оно равно 3000 . Однако значение переменной value остается равным 1000 .
  3. Конструктор завершает свою работу, и переменная x больше не используется.
В следующем примере зарплата сотрудника успешно утраивается, так как в качестве параметра методу передается значение ссылки объекта. Файл Salary2.java class Salary2 { int value = 1000 ; Salary2 () { } Salary2 (Salary2 x) { x. value = x. value * 3 ; } public static void main (String args ) { Salary2 s1 = new Salary2 () ; Salary2 s2 = new Salary2 (s1) ; System. out. println ("s1.value=" + s1. value) ; System. out. println ("s2.value=" + s2. value) ; } } Результат вывода программы: s1. value= 3000 s2. value= 1000 В качестве параметра используется значение ссылки на объект. При выполнении строки Salary2 s2 = new Salary2(s1) ; конструктору Salary2(Salary x) передастся значение ссылки на объект переменной s1 , и конструктор фактически утроит зарплату для s1.value , поскольку даже копия (Salary x) , создаваемая внутри конструктора указывает на объект переменной s1 .

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

В случае, если в параметрах перегруженного конструктора используется примитив, который может быть сужен (например int <- double), то вызов метода со суженным значением возможен, несмотря на то, что метода, перегруженного с таким параметром нет. Например: Файл Primitive.java class Primitive { Primitive (double d) { d = d + 10 ; System. out. println ("d=" + d) ; } public static void main (String args ) { int i = 20 ; Primitive s1 = new Primitive (i) ; } } Результат вывода программы: d= 30.0 Несмотря на то, что в классе Primitive отсутствует конструктор, у которого есть параметр типа int , отработает конструктор с входным параметром double . Перед вызовом конструктора переменная i будет расширена от типа int до типа double . Обратный вариант, когда переменная i была бы типа double , а конструктор был бы только с параметром int , в данной ситуации привел бы к ошибке компиляции.

Вызов конструктора и оператор new

Конструктор всегда вызывается оператором new . При вызове конструктора оператором new , конструктор всегда формирует ссылку на новый объект. Заставить конструктор сформировать вместо ссылки на новый объект ссылку на уже существующий объект нельзя, кроме подстановки десериализируемого объекта. А с оператором new сформировать вместо ссылки на новый объект ссылку на уже существующий объект нельзя. Например: Файл Salary3.java class Salary3 { int value = 1000 ; Salary3 () { } Salary3 (Salary3 x) { x. value = x. value * 3 ; } public static void main (String args ) { Salary3 s1 = new Salary3 () ; System. out. println ("First object creation: " + s1. value) ; Salary3 s2 = new Salary3 (s1) ; System. out. println ("Second object creation: " + s2. value) ; System. out. println (+ s1. value) ; Salary3 s3 = new Salary3 (s1) ; System. out. println ("Third object creation: " + s3. value) ; System. out. println ("What"s happend with first object?:" + s1. value) ; } } Результат вывода программы: First object creation: 1000 Second object creation: 1000 What"s happend with first object? : 3000 Third object creation: 1000 What"s happend with first object? : 9000 Сначала с помощью строчки Salary3 s1 = new Salary3() ; создается новый объект. Далее, если бы с помощью строки Salary3 s2 = new Salary3(s1) ; или строки Salary3 s3 = new Salary3(s1) ; можно было бы создать ссылку на уже существующий объект, то s1.value s2.value и s3.value хранили бы одинаковое значение 1000 . На самом деле в строке Salary3 s2 = new Salary3(s1) ; создастся новый объект для переменной s2 и изменится состояние объекта для переменной s1 , через передачу своего значения ссылки на объект, в параметре конструктора. В этом можно убедиться по результатам вывода. А при выполнении строки Salary3 s3 = new Salary3(s1) ; создастся НОВЫЙ объект для переменной s3 и снова изменится состояние объекта для переменной s1 .

Конструкторы и блоки инициализации, последовательность действий при вызове конструктора

В разделе Создание объекта и конструкторы перечислены действия общего характера, которые производятся при создании объекта. Среди них сопрягаются процессы инициализация полей класса и отработка конструктора класса, которые в свою очередь тоже имеют внутренний порядок:
  1. Все поля данных инициализируются своими значениями, предусмотренными по умолчанию (0, false или null).
  2. Инициализаторы всех полей и блоки инициализации выполняются в порядке их перечисления в объявлении класса.
  3. Если в первой строке конструктора вызывается другой конструктор, то выполняется вызванный конструктор.
  4. Выполняется тело конструктора.
Конструктор имеет отношение к инициализации, поскольку в Java существует три способа инициализации поля в классе:
  • присвоить значение в объявлении;
  • присвоить значения в блоке инициализации;
  • задать его значение в конструкторе.
Естественно, нужно организовать код инициализации так, чтобы в нем было легко разобраться. В качестве примера приведен следующий класс: class Initialization { int i; short z = 10 ; static int x; static float y; static { x = 2000 ; y = 3.141 ; } Initialization () { System. out. println ("i=" + i) ; System. out. println ("z=" + z) ; z = 20 ; System. out. println ("z=" + z) ; } } В приведенном примере, переменные инициализируются в следующем порядке: сначала инициализируются статические переменные x и y значениями по умолчанию. Далее выполняется статический блок инициализации. Затем производится инициализация переменной i значением по умолчанию и инициализируется переменная z . Далее в работу вступает конструктор. Вызов конструкторов класса не должен зависеть от порядка объявления полей. Это может привести к ошибкам.

Конструкторы и наследование

Конструкторы не наследуются. Например: public class Example { Example () { } public void sayHi () { system. out. println ("Hi" ) ; } } public class SubClass extends Example { } Класс SubClass автоматически наследует метод sayHi() определенный в родительском классе. В тоже время, конструктор Example() родительского класса не наследуется его потомком SubClass .

Ключевое слово this в конструкторах

Конструкторы используют this чтобы сослаться на другой конструктор в этом же классе, но с другим списком параметров. Если конструктор использует ключевое слово this , то оно должно быть в первой строке, игнорирование этого правила приведет к ошибке компилятора. Например: Файл ThisDemo.java public class ThisDemo { String name; ThisDemo (String s) { name = s; System. out. println (name) ; } ThisDemo () { this ("John" ) ; } public static void main (String args ) { ThisDemo td1 = new ThisDemo ("Mary" ) ; ThisDemo td2 = new ThisDemo () ; } } Результат вывода программы: Mary John В данном примере имеется два конструктора. Первый получает строку-аргумент. Второй не получает никаких аргументов, он просто вызывает первый конструктор используя имя "John" по-умолчанию. Таким образом, можно с помощью конструкторов инициализировать значения полей явно и по умолчанию, что часто необходимо в программах.

Ключевое слово super в конструкторах

Конструкторы используют super , чтобы вызвать конструктор суперкласса. Если конструктор использует super , то этот вызов должен быть в первой строке, иначе компилятор выдаст ошибку. Ниже приведен пример: Файл SuperClassDemo.java public class SuperClassDemo { SuperClassDemo () { } } class Child extends SuperClassDemo { Child () { super () ; } } В этом простом примере конструктор Child() содержит вызов super() , который создает экземпляр класса SuperClassDemo , в дополнение к классу Child . Так как super должен быть первым оператором, выполняемым в конструкторе подкласса, этот порядок всегда одинаков и не зависит от того, используется ли super() . Если он не используется, то сначала будет выполнен конструктор по умолчанию (без параметров) каждого суперкласса, начиная с базового класса. Следующая программа демонстрирует, когда выполняются конструкторы. Файл Call.java //Создать суперкласс A class A { A () { System. out. println ("Inside A constructor." ) ; } } //Создать подкласс B, расширяющий класс A class B extends A { B () { System. out. println ("Inside B constructor." ) ; } } //Создать класс (C), расширяющий класс В class C extends B { C () { System. out. println ("Inside C constructor." ) ; } } class Call { public static void main (String args ) { C c = new C () ; } } Вывод этой программы: Inside A constructor. Inside B constructor. Inside C constructor. Конструкторы вызываются в порядке подчиненности классов. В этом есть определенный смысл. Поскольку суперкласс не имеет никакого знания о каком-либо подклассе, то любая инициализация, которую ему нужно выполнить, является отдельной. По возможности она должна предшествовать любой инициализации, выполняемой подклассом. Поэтому-то она и должна выполняться первой.

Настраиваемые конструкторы

Механизм идентификации типа во время выполнения является одним из мощных базовых принципов языка Java, который реализует полиморфизм. Однако такой механизм не страхует разработчика от несовместимого приведения типов в ряде случаев. Самый частый случай – манипулирование группой объектов, различные типы которых заранее неизвестны и определяются во время выполнения. Поскольку ошибки, связанные с несовместимостью типов могут проявиться только на этапе выполнения, то это затрудняет их поиск и ликвидацию. Введение настраиваемых типов в Java 2 5.0 частично отодвигает возникновение подобных ошибок с этапа выполнения на этап компиляции и обеспечивает недостающую типовую безопасность. Отпадает необходимость в явном приведении типов при переходе от типа Object к конкретному типу. Следует иметь ввиду, что средства настройки типов работают только с объектами и не распространяются на примитивные типы данных, которые лежат вне дерева наследования классов. Благодаря настраиваемым типам все приведения выполняются автоматически и скрыто. Это позволяет обезопасить от несоответствия типов и гораздо чаще повторно использовать код. Настраиваемые типы можно использовать в конструкторах. Конструкторы могут быть настраиваемыми, даже если их класс не является настраиваемым типом. Например: class GenConstructor { private double val; < T extends Number > GenConstructor (T arg) { val = arg. doubleValue () ; } void printValue () { System. out. println ("val: " + val) ; } } class GenConstructorDemo { public static void main (String args ) { GenConstructor gc1 = new GenConstructor (100 ) ; GenConstructor gc2 = new GenConstructor (123.5F ) ; gc1. printValue () ; gc2. printValue () ; } } Поскольку конструктор GenConstructor задает параметр настраиваемого типа, который должен быть производным классом от класса Number , его можно вызвать с любы

Как известно, объект является экземпляром определенного класса. Для его создания используется ключевое слово new, например:
Person student = new Person(“Mike”)
Этим кодом создается новый объект Person и указывается его имя – Mike . Строка «Mike » передается аргументом соответствующему конструктору Person :

Person(String name) { this.name = name; }

Этот конструктор позволяет указать имя человека при создании объекта.

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

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

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

  1. Конструкторы можно перегружать :

Это означает, что класс может иметь множество различных конструкторов, если их списки параметров различны. Например:

Class Rectangle { int width; int height; Rectangle() { width = 1; height = 1; } Rectangle(int width) { this. width = width; this. height = width; } Rectangle(int width, int height) { this. width = width; this. height = height; } }

Имея три различных конструктора Rectangle можно создавать новый объект тремя различными способами:

Rectangle rect1 = new Rectangle(); Rectangle rect2 = new Rectangle(10); Rectangle rect3 = new Rectangle(10,20);

  1. Конструктор по умолчанию :

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

Например, если мы напишем класс Rectangle следующим образом:

Class Rectangle { int widh, height; int area() { } int perimeter() { } }

То компилятор автоматически вставляет конструктор по умолчанию: ectangle() { }

  1. Компилятор не будет генерировать конструктор по умолчанию, если в классе уже есть конструктор:

Рассмотрим следующий пример:

Class Rectangle { int width; int height; Rectangle(int width) { this. width = width; this. height = width; } Rectangle(int width, int height) { this. width = width; this. height = height; } }

При попытке создать новый объект: Rectangle rect1 = new Rectangle(); Компилятор выдаст ошибку, потому что не может найти конструктор без аргументов.

  1. Конструкторы не наследуются:

В отличие от методов, конструкторы не наследуются. Пример:

Class Rectangle { Rectangle(int width, int height) { } } class Square extends Rectangle { }

Нельзя сделать что-то вроде этого: Square box = new Square(10, 10);

  1. Конструкторы могут быть приватными!

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

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

  1. Конструктор по умолчанию имеет тот же модификатор доступа, что и класс:

При написании следующего класса: public class Person { }

Компилятор при вставке конструктора по умолчанию, укажет и необходимый модификатор доступа: public Preson();

  1. Первой строкой любого конструктора должен вызываться либо перегруженный конструктор того же класса или конструктор его суперкласса:

Если это не так, то компилятор автоматически допишет вызов конструктора суперкласса без аргументов super(); Это может привести к ошибке, поскольку такого конструктора может не быть в суперклассе. Пример:

Class Parent { Parent(int number) { } } class Child extends Parent { Child() { } }

Приведет к ошибке, потому что компилятор вставляет вызов super () в конструкторе класса Child :

Child() { super();// дописал компилятор }

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

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

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

Что такое конструкторы и зачем они нужны?

Рассмотрим два примера. public class Car { String model; int maxSpeed; public static void main (String args) { Car bugatti = new Car () ; bugatti. model = "Bugatti Veyron" ; bugatti. maxSpeed = 407 ; } } Мы создали наш автомобиль и установили для него модель и максимальную скорость. Однако в реальном проекте у объекта Car явно будет не 2 поля. А, например, 16 полей! public class Car { String model; //модель int maxSpeed; //максимальная скорость //объем двигателя //фамилия владельца //число мест в салоне String salonMaterial; //материал салона boolean insurance; //застрахована ли //страна-производитель int trunkVolume; //объем багажника int accelerationTo100km; public static void main (String args) { Car bugatti = new Car () ; bugatti. color = "blue" ; bugatti. accelerationTo100km = 3 ; bugatti. engineVolume = 6.3 ; bugatti. manufacturerCountry = "Italy" ; bugatti. ownerFirstName = "Amigo" ; bugatti. yearOfIssue = 2016 ; bugatti. insurance = true ; bugatti. price = 2000000 ; bugatti. isNew = false ; bugatti. placesInTheSalon = 2 ; bugatti. maxSpeed = 407 ; bugatti. model = "Bugatti Veyron" ; } } Мы создали новый объект Car . Одна проблема: полей-то у нас 16, а проинициализировали мы только 12 ! Попробуй сейчас по коду найти те, которые мы забыли! Не так-то просто, да? В такой ситуации программист может легко ошибиться и пропустить инициализацию какого-то поля. В итоге поведение программы станет ошибочным: public class Car { String model; //модель int maxSpeed; //максимальная скорость int wheels; //ширина дисков double engineVolume; //объем двигателя String color; //цвет int yearOfIssue; //год выпуска String ownerFirstName; //имя владельца String ownerLastName; //фамилия владельца long price; //цена boolean isNew; //новая или нет int placesInTheSalon; //число мест в салоне String salonMaterial; //материал салона boolean insurance; //застрахована ли String manufacturerCountry; //страна-производитель int trunkVolume; //объем багажника int accelerationTo100km; //разгон до 100 км/час в секундах public static void main (String args) { Car bugatti = new Car () ; bugatti. color = "blue" ; bugatti. accelerationTo100km = 3 ; bugatti. engineVolume = 6.3 ; bugatti. manufacturerCountry = "Italy" ; bugatti. ownerFirstName = "Amigo" ; bugatti. yearOfIssue = 2016 ; bugatti. insurance = true ; bugatti. price = 2000000 ; bugatti. isNew = false ; bugatti. placesInTheSalon = 2 ; bugatti. maxSpeed = 407 ; bugatti. model = "Bugatti Veyron" ; System. out. println ("Модель Bugatti Veyron. Объем двигателя - " + bugatti. engineVolume + ", багажника - " + bugatti. trunkVolume + ", салон сделан из " + bugatti. salonMaterial + ", ширина дисков - " + bugatti. wheels + ". Была приоберетена в 2018 году господином " + bugatti. ownerLastName) ; } } Вывод в консоль: Модель Bugatti Veyron. Объем двигателя - 6.3, багажника - 0, салон сделан из null, ширина дисков - 0. Была приобретена в 2018 году господином null Вашему покупателю, отдавшему 2 миллиона долларов за машину, явно не понравится, что его назвали “господином null ”! А если серьезно, в итоге в нашей программе оказался некорректно созданный объект - машина с шириной дисков 0 (то есть вообще без дисков), отсутствующим багажником, салоном, сделанным из неизвестного материала, да еще и принадлежащая непонятно кому. Можно только представить, как такая ошибка может “выстрелить” при работе программы! Нам нужно как-то избежать подобных ситуаций. Надо, чтобы в нашей программе было ограничение: при создании нового объекта машины для него всегда должны быть указаны, например, модель и максимальная скорость. Иначе - не позволять создание объекта. С этой задачей легко справляются функции-конструкторы . Они получили свое название не просто так. Конструктор создает своеобразный “каркас” класса, которому каждый новый объект класса должен соответствовать. Давай для удобства вернемся к более простому варианту класса Car с двумя полями. С учетом наших требований, конструктор для класса Car будет выглядеть так: public Car (String model, int maxSpeed) { this . model = model; this . maxSpeed = maxSpeed; } А создание объекта теперь выглядит так: public static void main (String args) { Car bugatti = new Car ("Bugatti Veyron" , 407 ) ; } Обрати внимание, как создается конструктор . Он похож на обычный метод, но у него нет типа возвращаемого значения. При этом в конструкторе указывается название класса, тоже с большой буквы. В нашем случае - Car . Кроме того, в конструкторе используется новое для тебя ключевое слово this . "this" по-английски - "этот, этого". Это слово указывает на конкретный предмет. Код в конструкторе: public Car (String model, int maxSpeed) { this . model = model; this . maxSpeed = maxSpeed; } можно перевести почти дословно: " model для этой машины (которую мы сейчас создаем) = аргументу model , который указан в конструкторе. maxSpeed для этой машины (которую мы создаем) = аргументу maxSpeed , который указан в конструкторе." Так и произошло: public class Car { String model; int maxSpeed; public Car (String model, int maxSpeed) { this . model = model; this . maxSpeed = maxSpeed; } public static void main (String args) { Car bugatti = new Car ("Bugatti Veyron" , 407 ) ; System. out. println (bugatti. model) ; System. out. println (bugatti. maxSpeed) ; } } Вывод в консоль: Bugatti Veyron 407 Конструктор успешно присвоил нужные значения. Ты, возможно, заметил, что конструктор очень похож на обычный метод! Так оно и есть: конструктор - это метод, только немного специфичный:) Так же как в метод, в наш конструктор мы передали параметры. И так же как вызов метода, вызов конструктора не сработает, если их не указать: public class Car { String model; int maxSpeed; public Car (String model, int maxSpeed) { this . model = model; this . maxSpeed = maxSpeed; } public static void main (String args) { Car bugatti = new Car () ; //ошибка! } } Видишь, конструктор сделал то, чего мы пытались добиться. Теперь нельзя создать машину без скорости или без модели! На этом сходство конструкторов и методов не заканчивается. Так же, как и методы, конструкторы можно перегружать . Представь, что у тебя дома живут 2 кота. Одного из них ты взял еще котенком, а второго ты принес домой с улицы уже взрослым и не знаешь точно, сколько ему лет. Значит, наша программа должна уметь создавать котов двух видов - с именем и возрастом для первого кота, и только с именем - для второго кота. Для этого мы перегрузим конструктор: public class Cat { String name; int age; //для первого кота //для второго кота public Cat (String name) { this . name = name; } public static void main (String args) { Cat barsik = new Cat ("Barsik" , 5 ) ; Cat streetCatNamedBob = new Cat ("Bob" ) ; } } К изначальному конструктору с параметрами “имя” и “возраст” мы добавили еще один, только с именем. Точно так же мы перегружали методы в прошлых уроках. Теперь мы успешно можем создать оба варианта котов:)

Помнишь, в начале лекции мы говорили, что ты уже пользовался конструкторами, только сам не замечал этого? Так и есть. Дело в том, что у каждого класса в Java есть так называемый конструктор по умолчанию . У него нет никаких аргументов, но он срабатывает каждый раз при создании любого объекта любого класса. public class Cat { public static void main (String args) { Cat barsik = new Cat () ; } } На первый взгляд это незаметно. Ну создали объект и создали, где тут работа конструктора? Чтобы это увидеть, давай прямо руками напишем для класса Cat пустой конструктор, а внутри него выведем какую-нибудь фразу в консоль. Если она выведется, значит конструктор отработал. public class Cat { public Cat () { System. out. println ("Создали кота!" ) ; } public static void main (String args) { Cat barsik = new Cat () ; //вот здесь сработал конструктор по умолчанию } } Вывод в консоль: Создали кота! Вот и подтверждение! Конструктор по умолчанию всегда незримо присутствует в твоих классах. Но тебе нужно знать еще одну его особенность. Дефолтный конструктор исчезает из класса, когда ты создаешь какой-то конструктор с аргументами. Доказательство этого, на самом деле, мы уже видели выше. Вот в этом коде: public class Cat { String name; int age; public Cat (String name, int age) { this . name = name; this . age = age; } public static void main (String args) { Cat barsik = new Cat () ; //ошибка! } } Мы не смогли создать кота без имени и возраста, потому что определили конструктор для Cat: строка + число . Дефолтный конструктор сразу после этого исчез из класса. Поэтому обязательно запомни: если тебе в твоем классе нужно несколько конструкторов, включая пустой, его нужно создать отдельно . Например, мы создаем программу для ветеринарной клиники. Наша клиника хочет делать добрые дела и помогать бездомным котикам, про которых мы не знаем ни имени, ни возраста. Тогда наш код должен выглядеть так: public class Cat { String name; int age; //для домашних котов public Cat (String name, int age) { this . name = name; this . age = age; } //для уличных котов public Cat () { } public static void main (String args) { Cat barsik = new Cat ("Barsik" , 5 ) ; Cat streetCat = new Cat () ; } } Теперь, когда мы явно прописали конструктор по умолчанию, мы можем создавать котов обоих типов:) Для конструктора (как и для любого метода) очень важен порядок следования аргументов. Поменяем в нашем конструкторе аргументы имени и возраста местами. public class Cat { String name; int age; public Cat (int age, String name) { this . name = name; this . age = age; } public static void main (String args) { Cat barsik = new Cat ("Барсик" , 10 ) ; //ошибка! } } Ошибка! Конструктор четко описывает: при создании объекта Cat ему должны быть переданы число и строка, именно в таком порядке . Поэтому наш код не срабатывает. Обязательно запомни это и учитывай при создании своих собственных классов: public Cat (String name, int age) { this . name = name; this . age = age; } public Cat (int age, String name) { this . age = age; this . name = name; } Это два абсолютно разных конструктора! Если выразить в одном предложении ответ на вопрос “Зачем нужен конструктор?” , можно сказать: для того, чтобы объекты всегда находились в правильном состоянии . Когда ты используешь конструкторы, все твои переменные будут корректно проинициализированы, и в программе не будет машин со скоростью 0 и прочих “неправильных” объектов. Их использование очень выгодно прежде всего для самого программиста. Если ты будешь инициализировать поля самостоятельно, велик риск что-нибудь пропустить и ошибиться. А с конструктором такого не будет: если ты передал в него не все требуемые аргументы или перепутал их типы, компилятор сразу же выдаст ошибку. Отдельно стоит сказать о том, что внутрь конструктора не стоит помещать логику твоей программы . Для этого в твоем распоряжении есть методы , в которых ты можешь описать весь нужный тебе функционал. Давай посмотрим, почему логика в конструкторе - это плохая идея: public class CarFactory { String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) { this . name = name; this . age = age; this . carsCount = carsCount; System. out. println ( "Она была основана " "В среднем она производит " + (this . carsCount/ this . age) + " машин в год" ) ; } public static void main (String args) { CarFactory ford = new CarFactory ("Ford" , 115 , 50000000 ) ; } } У нас есть класс CarFactory , описывающий фабрику по производству автомобилей. Внутри конструктора мы инициализируем все поля, и сюда же помещаем логику: выводим в консоль некоторую информацию о фабрике. Казалось бы - ничего плохого в этом нет, программа прекрасно отработала. Вывод в консоль: Наша автомобильная фабрика называется Ford Она была основана 115 лет назад За это время на ней было произведено 50000000 автомобилей В среднем она производит 434782 машин в год Но на самом деле мы заложили мину замедленного действия. И подобный код может очень легко привести к ошибкам. Представим себе, что теперь мы говорим что не о Ford, а о новой фабрике "Amigo Motors", которая существует меньше года и произвела 1000 машин: public class CarFactory { String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) { this . name = name; this . age = age; this . carsCount = carsCount; System. out. println ("Наша автомобильная фабрика называется " + this . name) ; System. out. println ("Она была основана " + this . age + " лет назад" ) ; System. out. println ("За это время на ней было произведено " + this . carsCount + " автомобилей" ) ; System. out. println ("В среднем она производит " + (this . carsCount/ this . age) + " машин в год" ) ; } public static void main (String args) { CarFactory ford = new CarFactory ("Amigo Motors" , 0 , 1000 ) ; } } Вывод в консоль: Наша автомобильная фабрика называется Amigo Motors Exception in thread "main" java.lang.ArithmeticException: / by zero Она была основана 0 лет назад За это время на ней было произведено 1000 автомобилей at CarFactory. (CarFactory.java:15) at CarFactory.main(CarFactory.java:23) Process finished with exit code 1 Приехали! Программа завершилась с какой-то непонятной ошибкой. Попробуешь догадаться, в чем причина? Причина - в логике, которую мы поместили в конструктор. А конкретно - вот в этой строке: System. out. println ("В среднем она производит " + (this . carsCount/ this . age) + " машин в год" ) ; Здесь мы выполняем вычисление и делим количество произведенных машин на возраст фабрики. А поскольку наша фабрика новая (то есть ей 0 лет) - в результате получается деление на 0, которое в математике запрещено. В результате программа завершается с ошибкой. Как же нам стоило поступить? Вынести всю логику в отдельный метод и назвать его, например, printFactoryInfo() . В качестве параметра ему можно передать объект CarFactory . Туда же можно поместить всю логику, и заодно - обработку возможных ошибок, наподобие нашей с нулем лет. Каждому свое. Конструкторы нужны для корректного задания состояния объекта. Для бизнес-логики у нас есть методы. Не стоит смешивать одно с другим. Вот несколько полезных ссылок, где ты можешь дополнительно почитать о конструкторах:

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

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

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) { width = w; height = h; depth = 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) { width = height = depth = len; }

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

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

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

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

// Используем объект типа Box Box(Box ob) { width = ob.width; height = ob.height; depth = ob.depth; }

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

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(); // программа закроется с ошибкой

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

1. Понятие конструктора по умолчанию

Конструктор по умолчанию (default constructor) – это конструктор, который не имеет параметров. Конструктор по умолчанию может объявляться в классе явным образом или генерироваться автоматически.

В наиболее общем случае, для класса ClassName , конструктор по умолчанию имеет следующий вид:

class ClassName { ... // объявление конструктора ClassName() { // тело конструктора // ... } ... }
2. В каких случаях конструктор по умолчанию генерируется в классе автоматически а в каких нет? Пример

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

Например. В следующем объявлении класса конструктор по умолчанию генерируется автоматически

class CMyClass { int d ; int GetD() { return d ; } void SetD(int nd) { d = nd; } }

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

// работает, так как в классе больше не реализовано ни одного конструктора CMyClass mc = new CMyClass();

Если в тело класса CMyClass добавить хотя бы один другой конструктор (например, конструктор с одним параметром), то конструктор по умолчанию автоматически генерироваться не будет

class CMyClass { int d ; // конструктор по умолчанию уже не генерируется автоматически CMyClass(int nd) { d = nd; } int GetD() { return d ; } void Set(int nd) { d = nd; } }

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

// ошибка компиляции, так как в классе уже объявлен другой конструктор // CMyClass mc = new CMyClass(); CMyClass mc2 = new CMyClass(7); // а этот код работает

В результате выполнения вышеприведенной строки будет выдана ошибка компиляции:

The constructor CMyClass() is undefined

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

class CMyClass { int d ; // явное объявление конструктора по умолчанию CMyClass() { d = 0; } // объявление конструктора с 1 параметром, CMyClass(int nd) { d = nd; } int GetD() { return d ; } void Set(int nd) { d = nd; } }

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

CMyClass mc = new CMyClass(); // вызывается конструктор по умолчанию mc.d = 25; CMyClass mc2 = new CMyClass(5); // вызывается конструктор с 1 параметром

3. Вызов конструкторов из других конструкторов. Пример

Язык программирования Java позволяет осуществлять вызов конструкторов класса из другого конструктора этого же класса. Для этого используется ключевое слово this , которое есть ссылкой на текущий класс.

Пример. В примере демонстрируется использование класса CPixel , который реализует пиксел на экране монитора.

// Класс, который реализует пиксел на экране монитора public class CPixel { // внутренние переменные класса private int x , y ; // координаты пиксела private int color ; // цвет пиксела // конструктор без параметров (конструктор по умолчанию) CPixel() { x = y = color = 0; } // конструктор с 2 параметрами, которые инициализируют только координаты CPixel(int _x, int _y) { x = _x; y = _y; color = 0; } // конструктор с 1 параметром, который инициализирует только цвет CPixel(int _color) { color = _color; x = y = 0; } // конструктор с 3 параметрами, который вызывает конструктор с 2 параметрами CPixel (int _x, int _y, int _color) { // вызов конструктора с 2 параметрами: обязательно первая операция и только один раз this (_x, _y); //this(_color); // повторный вызов конструктора запрещен this .color = _color; // так можно } // методы доступа int GetX() { return x ; } int GetY() { return y ; } int GetColor() { return color ; } }

Использование класса CPixel в другом программном коде (методе)

CPixel cp1 = new CPixel(2,8); // вызов конструктора с 2 параметрами CPixel cp2 = new CPixel(3,5,8); // вызов конструктора, который вызовет другой конструктор int d; d = cp1.GetX(); // d = 2 d = cp2.GetColor(); // d = 8 d = cp2.GetY(); // d = 5 ...

4. Какие ограничения (требования) накладываются на вызов других конструкторов из конструктора класса?

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

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