Конструктор в Java и ключевое слово this- введение в Java-OOP 003 #
Создание объектов в Java часто сравнивает с обычной стройкой в жизни. И английский constructor и русский конструктор связаны со строительством напрямую.
Принципиально, конструктор — это просто метод. Просто метод который создаёт объекты. В связи с тем, что это очень важный метод и является краеугольным камнем всей парадигмы ООП, то у этого метода есть привилегия.
Конструкторы (методы по которым создаются объекты) — пишутся в java всегда с большой буквы.
Это помогает понять, что сейчас создаётся объект.
Несмотря на его маленькие отличия это всего лишь метод. Метод с помощью которого мы можем создать объект.
В некоторых ситуациях нам достаточно создать пустой объект, без всяких полей, в других нам нужны объекты с параметрами.
Например, мы не можем общаться с пользователем сайта, не зная его емайла и ника. Если мы говорим о базе данных школы, то мы не можем “создать ученика” без имени, фамилии, года рождения и возможно без документа(номера документа) подтверждающего личность это тоже не всегда возможно.
Ситуации и в жизни и в коде могут встретиться разные и потому мы можем встретить в ином классе целое семейство конструктов. На сайте существуют различные страницы. На некоторых есть фотография, на некоторых нет. Для создания различных объектов одного типа используют разные конструкторы.
Иногда в классе нет вообще ни одного конструктора. Именно с такого примера мы и начнём.
Объект есть, конструктора нет #
На нашем (условном) сайте есть статьи и мы создаём Article класс, который поможет нам с ними работать.
public class Article > class Test public static void main(String[] args) Article art = new Article(); > >
Мы можем создать тестовый класс в том же файле. Мы должны знать, что модификатор доступа Public может применятся только к тому классу, который назван так же как и файл. Дополнительный класс в нашем файле скорее вспомогательный. Этим правилом мы и воспользовались для простоты.
При создании объекта мы использовали пустой конструктор, конструктор без параметров. Но мы его не создавали в классе Article. Так откуда же он взялся?
Java создаёт один стандартный пустой конструктор сразу по умолчанию в каждом классе. Нам не обязательно прописывать пустой конструктор. Но как только мы прописываем хоть одно правило создания экземпляра объекта, то мы переписываем стандартное поведение Java. И если нам всё ещё нужен пустой конструктор, то мы должны его прописать самостоятельно.
Конструктор — это специальный метод, не имеющий возвратного значения. Этот метод называется так же как и класс экземпляр которого он должен создать.
В конструкторе мы можем указать начальные или стандартные значения будущих объектов.
public class Article String title; String announce; String fullText; public Article() title = "unknown"; announce = "unknown"; fullText = "unknown"; > >
Для чего нужно создавать много разных конструкторов? #
Много конструкторов обычно и не нужно. В рамках обучения можно создать несколько разных вариантов, в обычной жизни делают ровно столько, сколько используется другими частями программы. Чаще всего ровно один.
public class Book private String title; private String author; private String isbn; private int page; private int year; public Book() > public Book(String bookTitle, String bookAuthor, int bookYear) title = bookTitle; author = bookAuthor; year = bookYear; > public Book(String bookTitle, String bookAuthor, String bookIsbn, int bookPage) title = bookTitle; author = bookAuthor; isbn = bookIsbn; page = bookPage; > >
this #
This это ссылка на самого себя, на класс, по которому создаются объекты.
Простейшее применение this уместно в классе выше. Что бы не придумывать новые переменные для конструкторов, отличающиеся от переменных класса, но созвучные им. Мы используем this и как бы говорим: » ЭТА переменная из ПОЛЯ КЛАССА будет равна тому значению, которое мы получим из переменной в сигнатуре конструктора.»
public Book(String title, String author, int year) this.title = title; this.author = author; this.year = year; > public Book(String title, String author, String isbn, int page) this.title = title; this.author = author; this.isbn = isbn; this.page = page; >
Другой пример использования this указан в официальной документации и я просто попытаюсь прочитать его для вам.
public class Rectangle private int x, y; private int width, height; public Rectangle() this(0, 0, 1, 1); > public Rectangle(int width, int height) this(0, 0, width, height); > public Rectangle(int x, int y, int width, int height) this.x = x; this.y = y; this.width = width; this.height = height; > . >
Строчка “this(0, 0, 1, 1);” указывает на конструктор в этом же методе. И такой конструктор, которые принимает 4 параметра есть — “public Rectangle(int x, int y, int width, int height)”. Вот этот this и вызывает СОБСТВЕННЫЙ конструктор.
Дополнительные ссылки #
- Providing Constructors for Your Classes — официальная документация
- A Guide to Constructors in Java — www.baeldung.com/java-constructors
- Guide to the this Java Keyword this от baeldung
- Using the this Keyword — от Oracle
Домашнее задание #
- Сколько различных конструкторов можно создать, если у нас девять различных полей класса? Допустим int, double, String, char, long, float, byte, short, boolean.
Конструкторы в Java
Конструкторы — это специальные методы, которые вызывается при создании объекта . Они «конструируют» новый объект определенного класса.
Шаг за шагом
Итак, чтобы объяснить нагляднее, представим, как работает программа.
1. Вы создаете основное «тело» программы, прописывая метод main:
public class Test < public static void main ( String [ ] args ) <
2. Допустим, Вам нужен объект класса Cat. Класс Cat у вас уже есть, и выглядит он так:
private String name ;
private String color ;
public String getName ( ) <
return name ;
public void setName ( String a ) <
public String getColor ( ) <
return color ;
public void setColor ( String color ) <
this . color = color ;
Вы пишете строку, которая должна создать объект класса Cat:
public class Test < public static void main ( String [ ] args ) < Cat cat1 = new Cat ( ) ;
3. В тот момент, когда программа приступает к созданию объекта cat1, она идет в class Cat:
Тут-то и появляется необходимость в конструкторах. Ведь в первую очередь Java ищет именно конструкторы, которые укажут, как именно создавать объект.
Но в нашем классе есть только геттеры и сеттеры — никаких конструкторов! Что же делать? Теперь объект не создастся?
Создастся, конечно. А все потому, что по-настоящему конструктор все равно присутствует — просто он явно не указан. Теперь, давайте посмотрим как создавать конструкторы явно, и какими они вообще бывают.
Явные и неявные конструкторы
Существуют два вида конструкторов — явные и неявные. Вы уже знаете, что, даже если ничего не прописать в коде класса, Вы все равно сможете «сконструировать» объект этого класса. Но, если все и так работает, зачем их вообще писать? Какие преимущества это дает?
Преимущество 1. Контроль над вводом данных.
Сначала, дайте посмотрим на изображение. Какие отличия Вы видите?
Если Вы заметили, что у всех трех классов разное количество параметров в конструкторе — Вы были правы:
Явно прописывая конструктор, Вы получаете возможность регулировать, какие параметры и в каком количестве нужно задать для создания объекта определенного класса.
Код№1 — класс Cat — скорее всего, был создан с использованием неявного конструктора. Он не просит никаких параметров.
Код№2 — класс Scanner — уже использует явно описанный конструктор. Он требует один параметр — и без него создать объект невозможно.
Код№3 — класс Dog — тоже использует явно описанный конструктор. Но тут, как мы видим, требуется уже три параметра — имя («Шарик»), порода («мопс») и возраст собаки (2 года).
Преимущество 2. Меньше строчек кода.
Вы заметили, как конструктор уменьшает количество строк в коде? Сравните:
Конструкторы
Основная задача конструкторов заключается в том, чтобы установить объект в первоначальное состояние, когда он создается при помощи оператора new . Конструктор имеет следующий обобщенный синтаксис:
() // заголовок конструктора < // тело конструктора >
Объявление конструктора очень похоже на объявление метода. Однако следует обратить внимание на следующие ограничения.
- Модификаторы, кроме модификаторов доступа, недопустимы в заголовке конструктора.
- Конструктор не может возвращать значение, поэтому не может и задавать в заголовке возвращаемый тип, даже void , но простая форма оператора return может содержаться в теле конструктора.
- Имя конструктора должно быть таким же, как и имя класса.
Имена классов и методов находятся в разных пространствах имен. Поэтому в примере 4.5 не будет конфликта имен, где метод, объявленный в строке (2), имеет такое же имя, как и конструктор, объявленный в строке (1). Однако использование подобной схемы именования строго не рекомендуется.
Пример 4.5. Пространство имен
public class Name < Name() < // (1) System.out.println("Constructor"); >void Name() < // (2) System.out.println("Method"); >public static void main(String[] args) < new Name().Name(); // (3) за вызовом конструктора следует вызов метода >>
Constructor Method
Конструктор по умолчанию
Конструктор по умолчанию — это конструктор без каких-либо параметров. Другими словами, он имеет следующую сигнатуру:
Если в классе не задано ни одного конструктора, то предоставляется неявный конструктор по умолчанию. Неявный конструктор по умолчанию эквивалентен следующей реализации:
() < super(); >// нет параметров, вызывает конструктор суперкласса.
Конструктор по умолчанию выполняет единственное действие — вызов конструктора суперкласса. Это гарантирует, что все унаследованное состояние объекта будет проинициализировано должным образом. Кроме того, все переменные экземпляра в объекте устанавливаются в значения по умолчанию своего типа.
В следующем коде класс Light не определяет никаких конструкторов.
class Light < // поля int noOfWatts; // Мощность boolean indicator; // Включено или выключено String location; // Размещение // нет конструкторов //. >class Greenhouse < // . Light oneLight = new Light(); // (1) вызов неявного конструктора по умолчанию >
В предыдущем коде во время создания объекта Light в строке (1) используется неявный конструктор по умолчанию.
Light()
При создании объекта при помощи оператора new с использованием конструктора по умолчанию, как в строке (1), поля объекта будут проинициализированы соответствующими значениями по умолчанию (т.е. поля noOfWatts , indicator и location в объекте Light будут проинициализированы 0 , false и null соответственно).
Класс может самостоятельно предоставить реализацию конструктора по умолчанию. В следующем примере класс Light явно определяет в строке (1) конструктор по умолчанию. Обратите внимание, что он имеет такое же имя, как и класс, и не получает никаких параметров.
class Light < // . // Explicit Default Constructor Light() < // (1) noOfWatts = 50; indicator = true; location = "X"; >//. > class Greenhouse < // . Light extraLight = new Light(); // (2) Вызов явного конструктора по умолчанию >
Наличие явного конструктора по умолчанию гарантирует, что в любом объекте, созданном с помошью выражения new Light() , как в строке (2), значения полей noOfWatts , indicator и location будут установлены в 50 , true и «X» соответственно.
Если в классе определены какие-либо явные конструкторы, то он не может больше полагаться на неявный конструктор по умолчанию, для установки состояния объектов. Если в таком классе требуется конструктор по умолчанию, то должна быть его реализация. В примере ниже в классе Light задан только конструктор не по умолчанию (1). Он вызывается в строке (2), когда с помощью оператора new создается объект класса light . Любая попытка вызова конструктора по умолчанию будет обозначена как ошибка на этапе компиляции, как показано в строке (3).
class Light < // . // Только конструктор не по умолчанию Light(int noOfWatts, boolean indicator, String location) < // (1) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; >//. > class Greenhouse < // . Light moreLight = new Light(100, true, "Greenhouse"); // (2) OK. // Light firstLight = new Light(); // (3) Ошибка на этапе компиляции >
Перегруженные конструкторы
Как и методы, конструкторы также могут быть перегружены. Так как все конструкторы в классе имеют такое же имя, как и имя класса, то их сигнатуры различаются по их спискам параметров. В следующем примере класс Light поддерживает как явную реализацию конструктора по умолчанию в строке (1), так и конструктор не по умолчанию (2). Конструкторы перегружены, как явно следует из их сигнатур. Конструктор не по умолчанию вызывается, когда создается объект класса Light в строке (3), подобным же образом вызывается и конструктор по умолчанию в строке (4).
Перегрузка конструкторов позволяет обеспечить подходящую инициализацию объектов при создании, в зависимости от вызванного конструктора.
class Light < // . // Явный конструктор по умолчанию Light() < // (1) noOfWatts = 50; indicator = true; location = "X"; >// Конструктор не по умолчанию Light(int noOfWatts, boolean indicator, String location) < // (2) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; >//. > class Greenhouse < // . Light moreLight = new Light(100, true, "Greenhouse"); // (3) OK. Light firstLight = new Light(); // (4) OK. >
Что такое Конструктор в Java?
Конструктор класса очень удобен и по своему виду схож с обыкновенными функциями. За счёт конструктора мы можем установить значения для объекта сразу при его создании.
Конструктор должен иметь одинаковое название с классом. Пример создания конструктора показан ниже:
class Book < private int pages; public String name; private float weight; public void getInfoBook () < System.out.print("В книге " + name + " находиться " + pages + " страниц. "); System.out.print("При этом она весит " + weight + "кг!"); System.out.println(""); >Book (int pages, float weight) < this.pages = pages; this.weight = weight; >Book (int pages, float weight, String name) < this.pages = pages; this.weight = weight; this.name = name; >>
В одном классе может быть сразу несколько конструкторов. Создав несколько конструкторов мы можем передавать разное количество параметров при создании объекта.
Компилятор сам определяет какой конструктор необходимо использовать в зависимости от передаваемых параметров и их типов данных.
В коде выше переменные идут с модификатором доступа private . Дело в том, что все переменные лучше делать либо private, либо protected. Доступ к переменным должен осуществляться только за счёт методов и конструкторов. Доступ к полям напрямую должен быть закрыт.
Дополнительно создадим два объекта и выведем информацию:
public class Main < public static void main(String[] args) < Book sherlock_holms = new Book(460, 1.7f); // Используем 1 конструктор sherlock_holms.name = "Шерлок Холмс"; // Устанавливаем вручную значение для name sherlock_holms.getInfoBook (); Book green_mile = new Book(750, 2.8f, "Зеленая миля"); // Используем второй конструктор green_mile.getInfoBook (); >>
Поскольку переменная name имеет public модификатор доступа, то мы можем ссылаться к ней отдельно. Лучше так не делать и для неё тоже установить модификатор private.
Также конструктор может не принимать параметров. В таком случае при создании объекта ничего не надо дополнительно передавать.