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

Для Андроид 05.05.2019

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

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

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

Рассмотрим два примера. 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 . Туда же можно поместить всю логику, и заодно - обработку возможных ошибок, наподобие нашей с нулем лет. Каждому свое. Конструкторы нужны для корректного задания состояния объекта. Для бизнес-логики у нас есть методы. Не стоит смешивать одно с другим. Вот несколько полезных ссылок, где ты можешь дополнительно почитать о конструкторах:

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

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

public long idNUm;

public String name = "<Без имени>";

public Body orbits = null;

private static long nextID = 0;

idNUm = nextID++;

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

Конструктор класса Body объявлен без параметров. Его назначение довольно важно – он обеспечивает уникальность значения поля idNum вновь создаваемого объекта класса. В исходном варианте класса небольшая ошибка программиста-пользователя, связанная, например, с неаккуратно выполненной операцией присваивания значения полю idNum либо с отсутствием инструкций приращения содержимого поля nextID, могла бы привести к тому, что несколько объектов Body получили бы один и тот же порядковый номер. Подобный результат явно Ошибочен, поскольку нарушает ту часть контракта класса, которая гласит:

"Значения idNum различных объектов класса должны быть уникальными".

Передав ответственность за выбор верных значений idNum самому классу, мы раз и навсегда избавимся от подобных ошибок. Теперь конструктор класса Body – это единственный субъект, который изменяет содержимое поля nextID и нуждается в доступе к нему. Поэтому мы можем и должны обозначить переменную nextID модификатором private, чтобы предотвратить возможность обращения к ней за пределами класса. Сделав это, мы исключим один из потенциальных источников ошибок, грозящих будущим пользователям нашего класса.

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

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

Body sun = new Body(); // idNUm = 0

sun. name = "Солнце";

Body earth = new Body(); // idNum = 1

earth.name = "земля";

earth.orbits = sun;

В Процессе создания объекта с помощью оператора new конструктор класса Body Вызывается после присваивания полям name и огbits предусмотренных нами Выражений инициализации.

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

Body(String bodyName, Body orbitsAround) {

name = bodyName; orbits = orbitsAround;

Как нетрудно заметить, один конструктор класса обращается к другому посредством выражения this – первой исполняемой инструкции в теле конструктора-инициатора. Подобное предложение называют явным вызовом конструктора. Если конструктор, к которому вы намереваетесь обратиться явно, предполагает задание аргументов, при вызове они должны быть переданы. Какой из конструкторов будет вызван – это обусловливается количеством аргументов и набором их типов. В данном случае выражение this означает вызов конструктора без параметров, позволяющего установить значение idNum объекта и увеличить текущее содержимое статического поля nextID на единицу. Обращение к this дает возможность избежать повторения кода инициализации idNum и изменения nextID. Теперь код, предусматривающий создание объектов, становится существенно более простым:

Body sun = new Bоdу("солнце", null);

Body earth = new Воdу("земля", sun);

Версия конструктора, вызываемого в процессе выполнения оператора new, определяется структурой списка передаваемых аргументов.

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

Руководствуясь соображениями логической полноты, мы могли бы ввести в состав класса Body еще один конструктор, предусматривающий задание единственного аргумента и используемый для создания объектов, которые представляют небесные тела, не являющиеся спутниками более крупных светил. К этому конструктору удобно обращаться, когда заранее известно, что значение второго аргумента равно null:

Body(String bodyName) {

this (bodyName, null);

Здесь вновь используется прием явного вызова конструктора.

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

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

· Без помощи конструкторов с параметрами некоторые классы не в состоянии обеспечить свои объекты приемлемыми исходными значениями.

· При использовании дополнительных конструкторов задача определения начальных свойств объектов упрощается (наглядный пример – конструктор класса Body с двумя параметрами).

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

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

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

будет помечен как publiс.

Существует еще одна специальная форма конструктора – конструктор копии, который в качестве аргумента принимает объект текущего типа и создает его копию. Обычно такой конструктор сохраняет в полях нового объекта значения исходного объекта, но подчас семантика класса вынуждает автора предусматривать в теле конструктора копии более изощренные операции. Ниже приведен простой конструктор копии объектов типа Body.

Body(Body other) {

idNum = other.idNum;

name = other.name;

orbits = other.orbits;

Подход к решению задачи копирования объектов, связанный с применением специальных конструкторов, в классах стандартных пакетов Java широкого распространения не нашел, поскольку для достижения подобной цели более предпочтительным считается использование метода Сlone (обратитесь к разделу 3.9 на странице 111). Впрочем, простой конструктор копии предусмотрен, например, в составе класса String, а в классах коллекций (они рассматриваются в главе 16)

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

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

Предложение throws помещается после списка параметров непосредственно Перед открывающей фигурной скобкой, которая отмечает начало тела конструктора. Если в объявлении конструктора присутствует предложение throws, любой метод, который косвенным образом обращается к конструктору при выполнении оператора new, обязан либо обеспечить "отлов" исключений упомянутых типов с помощью соответствующих предложений catch, либо перечислить эти типы в разделе throws собственного объявления.

Как известно, объект является экземпляром определенного класса. Для его создания используется ключевое слово 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 , т.к. уже есть другие конструкторы.

Метод в Java - это комплекс выражений, совокупность которых позволяет выполнить определенную операцию. Так, например, при вызове метода System.out.println(), система выполняет ряд команд для выведения сообщения на консоль.

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

Создание метода

Ниже рассмотрен пример, иллюстрирующий синтаксис метода, как в Java создать метод.

Синтаксис

public static int methodName(int a, int b) { // тело }
  • public static - модификатор;
  • int - возвращаемый тип;
  • methodName - имя метода;
  • a, b - формальные параметры;
  • int a, int b - перечень параметров.

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

Синтаксис

modifier returnType nameOfMethod (Parameter List) { // тело метода }

Приведенный выше синтаксис включает:

  • modifier – определяет тип доступа для метода и возможность его использования.
  • returnType – метод может возвратить значение.
  • nameOfMethod – указывает имя метода. Сигнатура метода включает имя метода и перечень параметров.
  • Parameter List – перечень параметров представлен типом, порядком и количеством параметров метода. Данная опция задается произвольно, в методе может присутствовать нулевой параметр.
  • method body – тело метода определяет метод работы с командами.

Пример

/* фрагмент кода возвращает минимальное между двумя числами */ public static int minFunction(int n1, int n2) { int min; if (n1 > n2) min = n2; else min = n1; return min; }

Вызов метода

Перед использованием метода его необходимо вызвать. Существует два способа для вызова метода в Java, т.е. метод производит возврат значения либо не производит (отсутствует возвращающее значение).

Алгоритм вызова метода достаточно прост. Когда программа производит в Java вызов метода, программное управление передается вызванному методу. Данный вызванный метод затем возвращает управление вызывающему клиенту в двух случаях, если:

  • выполняется оператор возврата;
  • достигнута закрывающая фигурная скобка окончания метода.

Метод возврата типа void производит вызов команды. Рассмотрим пример:

System.out.!");

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

Int result = sum(6, 9);

Пример ниже демонстрирует способ определения и вызова метода в Java.

Пример

public class ExampleMinNumber { public static void main(String args) { int a = 11; int b = 6; int c = minFunction(a, b); System.out.println("Минимальное значение = " + c); } /* Возвращает минимум из двух чисел */ public static int minFunction(int n1, int n2) { int min; if (n1 >

Минимальное значение = 6

Ключевое слово void

Ключевое слово void в Java позволяет нам создать методы, не производящие возврат значения. В примере, расположенном далее, нами был рассмотрен метод типа void – methodRankPoints. Методы типа void в Java не производят возврат каких-либо значений. Вызов метода типа void выполняется командой, т.е. methodRankPoints(255.7);. Это java-выражение, которое оканчивается точкой с запятой, как показано в примере ниже:

Пример

public class ExampleVoid { public static void main(String args) { methodRankPoints(255.7); } public static void methodRankPoints(double points) { if (points >= 202.5) { System.out.println("Ранг A1"); }else if (points >= 122.4) { System.out.println("Ранг A2"); }else { System.out.println("Ранг A3"); } } }

В итоге будет получен следующий результат:

Ранг A1

Передача параметров по значению в Java

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

В Java передача параметров по значению обозначает вызов метода с параметром. За счет этого производится передача значения аргумента параметру.

Пример

Следующая программа демонстрирует пример передачи параметра по значению. Значения аргументов остаются неизменными даже после вызова метода.

Public class swappingExample { public static void main(String args) { int a = 30; int b = 45; System.out.println("Перед тем как передать, значения аргументов a = " + a + " и b = " + b); // Вызов метода передачи swapFunction(a, b); System.out.println("\nСейчас, до и после передачи значения аргументов "); System.out.println("остались неизменными, a = " + a + " и b = " + b); } public static void swapFunction(int a, int b) { System.out.println("До замены: a = " + a + " b = " + b); // Передача параметров int c = a; a = b; b = c; System.out.println("После замены: a = " + a + " b = " + b); } }

Получим следующий результат:

Перед тем как передать, значения аргументов a = 30 и b = 45 До замены: a = 30 b = 45 После замены: a = 45 b = 30 Сейчас, до и после передачи значения аргументов остались неизменными, a = 30 и b = 45

Перегрузка методов

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

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

Рассмотренный пример поясняет вышесказанное.

Пример

public class ExampleOverloading { public static void main(String args) { int a = 7; int b = 3; double c = 5.1; double d = 7.2; int result1 = minFunction(a, b); // такая же функция с другими параметрами double result2 = minFunction(c, d); System.out.println("Минимальное значение = " + result1); System.out.println("Минимальное значение = " + result2); } // для integer public static int minFunction(int n1, int n2) { int min; if (n1 > n2) min = n2; else min = n1; return min; } // для double public static double minFunction(double n1, double n2) { double min; if (n1 > n2) min = n2; else min = n1; return min; } }

В итоге будет получен следующий результат:

Минимальное значение = 3 Минимальное значение = 5.1

Методы перегрузки делают программу читаемой. Таким образом, представлены два метода с одинаковым именем, но различными параметрами. В результате чего мы получили минимальные int число и число double типа.

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

В ходе работы программы вам может понадобиться произвести передачу определенной информации. Это может быть сделано в Java за счет передачи аргументов командной строки в main().

В Java аргумент командной строки представляет информацию, которая напрямую следует за именем программы в командной строке при ее выполнении. Получение доступа к аргументам командной строки в java-программе не представляет сложности. Они хранятся в виде строки в массиве строк, переданном в main().

Пример

Программа ниже отображает все вызванные аргументы командной строки.

Public class CommandLine { public static void main(String args) { for(int i = 0; i

Попробуйте выполнить данную программу, как показано далее:

$java CommandLine это командная строка 300 -200

В итоге будет получен следующий результат:

Args: это args: командная args: строка args: 300 args: -200

Конструктор в Java

В Java конструктор инициализирует объект при его создании. Его имя аналогично имени класса, а синтаксис сходен с синтаксисом метода. Однако, в отличие от последнего, в конструкторе отсутствует возвращаемое значение.

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

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

Пример

В примере ниже рассмотрено использование конструктора класса без параметров.

// Простой конструктор. class MyClass { int x; // Далее следует конструктор MyClass() { x = 10; } }

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

Public class ConsDemo { public static void main(String args) { MyClass t1 = new MyClass(); MyClass t2 = new MyClass(); System.out.println(t1.x + " " + t2.x); } }

Получим результат:

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

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

Пример

// Простой конструктор. class MyClass { int x; // Ниже конструктор MyClass(int i) { x = i; } }

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

Public class ConsDemo { public static void main(String args) { MyClass t1 = new MyClass(10); MyClass t2 = new MyClass(20); System.out.println(t1.x + " " + t2.x); } }

Получим следующий результат:

Ключевое слово this

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

Примечание: ключевое слово this используется только в составе методов либо конструкторов экземпляра.

Как правило, ключевое слово this в Java используется для:

  • дифференцирования между переменными экземпляра и локальными переменными в случае, если у них одинаковые имена, в составе конструктора или метода.
class Student { int age; Student(int age) { this.age = age; } }
  • вызова конструктора одного типа (параметризованного конструктора либо конструктора по умолчанию) из другого в составе класса. Данный процесс также носит название явного вызова конструктора.
class Student { int age Student() { this(20); } Student(int age) { this.age = age; } }

Пример

Public class This_Example { // Инициализация переменной num int num = 11; This_Example() { System.out.println("Это пример программы с ключевым словом this"); } This_Example(int num) { // Вызов конструктора по умолчанию this(); // Присвоение локальной переменной num переменной экземпляра num this.num = num; } public void greet() { System.out.println("Привет! Добро пожаловать на ProgLang!"); } public void print() { // Локальная переменная num int num = 20; // Вызов метода класса greet this.greet(); // Вывод локальной переменной. System.out.println("Значение локальной переменной num: " + num); // Вывод переменной экземпляра. System.out.println("Значение переменной экземпляра num: " + this.num); } public static void main(String args) { // Инициализация класса This_Example obj1 = new This_Example(); // Вызов метода print obj1.print(); // Передача нового значения переменной num через параметризованный конструктор This_Example obj2 = new This_Example(30); // Вызов снова метода print obj2.print(); } }

В итоге будет получен следующий результат:

Это пример программы с ключевым словом this Привет! Добро пожаловать на ProgLang! Значение локальной переменной num: 22 Значение переменной экземпляра num: 11 Это пример программы с ключевым словом this Привет! Добро пожаловать на ProgLang! Значение локальной переменной num: 22 Значение переменной экземпляра num: 30

Аргументы переменной (var-args)

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

TypeName... parameterName

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

Пример

public class VarargsDemo { public static void main(String args) { // Вызов метода с переменной args printMax(27, 11, 11, 5, 77.1); printMax(new double{10, 11, 12, 77, 71}); } public static void printMax(double... numbers) { if (numbers.length == 0) { System.out.println("Ни один аргумент не передается"); return; } double result = numbers; for (int i = 1; i result) result = numbers[i]; System.out.println("Максимальное значение " + result); } }

В итоге будет получен следующий результат:

Максимальное значение 77.1 Максимальное значение 77.0

Метод finalize()

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

К примеру, мы можете использовать finalize() чтобы удостовериться в том, что открытый файл, принадлежащий данному объекту, был закрыт.

Для добавления финализатора в класс, вам просто следует определить метод finalize() в Java. Среда выполнения Java производит вызов данного метода непосредственно перед обработкой объекта данного класса.

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

В общем виде метод finalize() выглядит следующим образом:

Protected void finalize() { // здесь финализация кода }

Здесь ключевое слово protected представляет спецификатор, предотвращающий доступ к finalize() посредством кода, определяемого вне его класса.

Это свидетельствует о том, что вы не можете знать как или даже когда будет производиться выполнение finalize(). К примеру, если ваша программа будет окончена до «сборки мусора», finalize() не будет выполняться.

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. Какие ограничения (требования) накладываются на вызов других конструкторов из конструктора класса?

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

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


Рекомендуем почитать

Наверх