JavaRush /Java блог /Java Developer /Знакомство с классами: написание собственных классов, кон...
Автор
Aditi Nawghare
Инженер-программист в Siemens

Знакомство с классами: написание собственных классов, конструкторы

Статья из группы Java Developer
Привет! Сегодня мы поговорим о классах в Java. Классы — это, можно сказать, основа основ программирования на Java. Когда ты станешь программистом, твоей основной задачей будет написание собственных классов с разным функционалом. Знакомство с классами: написание собственных классов, конструкторы - 1Давай разберемся, что же это за штука такая и как она работает :) Как ты уже знаешь, Java — это объектно-ориентированный язык программирования. Все программы состоят из объектов, которые как-то связываются между собой. Класс — это, по сути, шаблон для объекта. Он определяет, как объект будет выглядеть и какими функциями обладать. Каждый объект является объектом какого-то класса. Рассмотрим самый простой пример:

public class Cat {

    String name;
    int age;

}
Допустим, мы пишем программу, и в этой программе нам для чего-то нужны кошки (например, у нас ветеринарная клиника с возможностью онлайн-записи). Мы создали класс Cat, и указали для него две переменные — строку name, и число age. Такие переменные классов называются полями. По сути — это шаблон для всех кошек, которых мы создадим в будущем. У каждой кошки (объекта класса Cat) будет две переменные — имя и возраст.

public class Cat {

    String name;
    int age;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";

        System.out.println("Мы создали кота по имени " + barsik.name + ", его возраст - " + barsik.age);
    }

}
Вот так это работает! Мы создали кота, присвоили ему имя и возраст и вывели все это в консоль. Ничего сложного :) Классы чаще всего описывают предметы и явления окружающего мира. Кошка, стол, человек, молния, страница книги, колесо — все это в твоей программе будет создаваться с помощью отдельных классов. Теперь давай обратим внимание на переменные, которые мы создали в классе Cat. Они называются полями, или переменными экземпляров. В названии, собственно, раскрыта вся их суть. Эти переменные будут у каждого экземпляра (объекта) класса Cat. У каждого кота, которого мы создадим, будет своя переменная name и своя переменная age. Логично, в общем-то: с настоящими котами все так и есть :) Кроме переменных экземпляров существуют и другие — переменные классов, или статические. Дополним наш пример:

public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";
        count++;

        Cat vasia = new Cat();
        vasia.age = 5;
        vasia.name = "Вася";
        count++;

        System.out.println("Мы создали кота по имени " + barsik.name + ", его возраст - " + barsik.age);
        System.out.println("Мы создали кота по имени " + vasia.name + ", его возраст - " + vasia.age);

        System.out.println("Общее количество котов = " + count);
    }
}
Вывод в консоль:

Мы создали кота по имени Барсик, его возраст - 3
Мы создали кота по имени Вася, его возраст - 5
Общее количество котов = 2
Теперь у нас в классе появилась новая переменная — count (количество). Она отвечает за подсчет созданных котов. Каждый раз, когда в методе main мы создаем кота, мы увеличиваем эту переменную на 1. Эта переменная обозначена ключевым словом static. Это значит, что она принадлежит классу, а не конкретному объекту класса. Что, конечно, логично: если имя у каждого кота должно быть свое, то счетчик котов нам нужен один на всех. Именно этого позволяет добиться слово static — переменная count одна для всех котов. Обрати внимание: когда мы выводим ее в консоль, мы не пишем barsik.count или vasia.count. Она не принадлежит ни Барсику, ни Васе — она принадлежит всему классу Cat. Поэтому — просто count. Можно также написать Cat.count — это тоже будет правильно. С выводом в консоль переменной name у нас бы такое не прошло:

public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";
        count++;

        System.out.println("Мы создали кота по имени " + name + ", его возраст - " + barsik.age);

        System.out.println("Общее количество котов = " + count);
    }
}
Ошибка! name у каждого кота свое. В этом месте компилятор сбивается с толку. "Вывести в консоль имя? А чье имя-то? :/"

Методы

Кроме переменных, у каждого класса есть методы. О них мы поговорим в отдельной лекции более подробно, но общие моменты довольно просты. Методы — это функционал твоего класса; то, что объекты этого класса умеют делать. С одним из методов ты уже знаком — это метод main(). Но метод main, как ты помнишь, является статическим — то есть он принадлежит всему классу (логика такая же, как с переменными). А обычные, нестатические методы, можно вызывать только на конкретных объектах, которые мы создали. Например, если мы хотим написать класс для кошки, нам надо понять, какими функциями кошка должна обладать в нашей программе. Исходя из этого, напишем для нее пару методов:

public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Мяу!");
    }

    public void jump() {
        System.out.println("Прыг-скок!");
    }

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";

        barsik.sayMeow();
        barsik.jump();

    }
}
Ну вот, теперь наш класс гораздо больше похож на описание настоящей кошки! У нас теперь не просто кот Барсик с именем и возрастом. Он еще умеет мяукать и прыгать! Какой же кот без такого "функционала" :) Берем конкретный объект — barsik, и вызываем у него методы sayMeow() и jump(). Смотрим в консоль:

Мяу!
Прыг-скок!
Настойщий кот! :)

Создание собственных классов. Абстракция

В будущем тебе придется писать собственные классы. На что нужно обратить внимание при их написании? Если мы говорим о переменных, тебе нужно пользоваться такой вещью как абстракция. Абстракция — один из четырех основных принципов объектно-ориентированного программирования. Он подразумевает выделение главных, наиболее значимых характеристик предмета, и наоборот — отбрасывание второстепенных, незначительных. Например, мы создаем картотеку работников компании. Для создания объектов "работник" мы написали класс Employee. Какие характеристики важны для описания работника в картотеке компании? ФИО, дата рождения, номер социального страхования, ИНН. Но вряд ли в карточке работника компании нам нужны его рост, цвет глаз и волос. Компании эта информация ни к чему. Поэтому для класса Employee мы зададим переменные String name, int age, int socialInsuranceNumber и int taxNumber, а от лишней для нас информации (типа цвета глаз) откажемся, абстрагируемся. А вот если мы создаем картотеку фотомоделей для модельного агентства, ситуация резко меняется. Для описания фотомодели нам как раз очень важны рост, цвет глаз и цвет волос, а вот номер ИНН для нее нам абсолютно не важен. Поэтому в классе Model нам нужно создать переменные int height, String hair, String eyes. Вот так и работает абстракция, все просто! :)

Конструкторы

Давай вернемся к нашему примеру с кошками.

public class Cat {

    String name;
    int age;

    public static void main(String[] args) {
        Cat barsik = new Cat();

        System.out.println("Тут в программе в течение 2-х часов что-то происходит...");

        barsik.age = 3;
        barsik.name = "Барсик";

    }
}
Посмотри на этот код и попробуй догадаться, что с нашей программой не так. На протяжении двух часов в нашей программе существовал кот без имени и возраста! Конечно, это в корне неверно. В базе данных ветеринарной клиники не должно быть котов без информации о них. Сейчас мы отдаем это на откуп программиста. Не забудет он указать имя и возраст — все будет ок. Забудет — в базе будет ошибка, неизвестные коты. Как нам решить эту проблему? Надо каким-то образом запретить создавать котов без имени и возраста. Здесь нам на помощь приходят функции-конструкторы. Приведем пример:

public class Cat {

    String name;
    int age;

    //конструктор для класса Cat
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat barsik = new Cat("Barsik", 5);
    }
}
Конструктор — это, по сути, шаблон для объектов класса. В данном случае мы указывем, что для каждого объекта 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(); //ошибка!
    }
}
Теперь, когда в классе есть конструктор, компилятор Java знает, как должны выглядеть объекты, и не позволяет создавать объекты без указанных в нем аргументов. Теперь давай разберемся с ключевым словом this, которое ты видишь внутри конструктора. С ним тоже все просто. "this" по-английски — "этот, этого". То есть это слово указывает на конкретный предмет. Код в конструкторе

public Cat(String name, int age) {
    this.name = name;
    this.age = age;
}
можно перевести почти дословно: "name для этого кота (которого мы создаем) = аргументу name, который указан в конструкторе. age для этого кота (которого мы создаем) = аргументу age, который указан в конструкторе." После срабатывания конструктора можешь проверить, что нашему коту присвоились все нужные значения:

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("Барсик", 5);
        System.out.println(barsik.name);
        System.out.println(barsik.age);
    }
}
Вывод в консоль:

Барсик
5
Когда конструктор отработал:

Cat barsik = new Cat("Барсик", 5);
Внутри него по факту произошло следующее:

this.name = "Барсик";
this.age = 5;
И объекту barsik (он и является this) присвоились значения из аргументов конструктора. На самом деле, если не указать в классе конструкторы у него все равно будет срабатывать конструктор! Но как такое возможно? О_О Дело в том, что в 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;

    static int count = 0;

    public Cat() {
        count++;
        this.name = "Уличный кот номер " + count;
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat streetCat1 = new Cat();
        Cat streetCat2 = new Cat();
        System.out.println(streetCat1.name);
        System.out.println(streetCat2.name);
    }
}
У нас есть переменная count, которая является счетчиком уличных котов. Каждый раз при выполнении конструктора по умолчанию мы увеличиваем ее на 1 и присваем этот номер в качестве имени кота. Для конструктора очень важен порядок следования аргументов. Поменяем в нашем конструкторе аргументы имени и возраста местами.

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;
}
Это два абсолютно разных конструктора! Теперь реши несколько задач на закрепление материала :)
  • Музей древностей.
Твоя задача — спроектировать класс Artifact. Артефакты, которые хранятся в музее, бывают трех видов. Первый — о которых неизвестно ничего, кроме порядкового номера, присвоенному музеем (например: 212121). Второй — о которых известен порядковый номер и культура, которой он был создан (например: 212121, "Ацтеки"). Третий вид — о которых известен порядковый номер, культура, которой он был создан, и точный век его создания (например: 212121, "Ацтеки", 12). Создай класс Artifact, описывающий хранящиеся в музее древности, и напиши необходимое количество конструкторов для него. В методе main() создай по одному артефакту каждого вида.

public class Artifact {

    public static void main(String[] args) {
    }
}
  • Сайт знакомств
Вы создаете базу данных пользователей для сайта знакомств. Да вот беда — вы подзабыли в каком порядке их нужно указывать, а технического задания под рукой нет. Спроектируйте класс User, у которого будут поля — имя (String) возраст (short) и рост (int). Создайте для него необходимое количество конструкторов, чтобы имя, возраст и рост можно было указывать в любом порядке.

public class User {

    String name;
    short age;
    int height;

    public static void main(String[] args) {
        
    }
}
Комментарии (2419)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
{Java_Shark} Уровень 14
1 марта 2024
Прекрасная информация!!! Автору огромное спасибо!!!:)
vlafriday Уровень 3
20 февраля 2024
20.02.2024 check, мой уровень 3
7 февраля 2024
Мне кажется для лучшего понимания, для новичка, переменные класса и присваивание им значений через конструктор лучше написать так. public class Cat { String catname; int catage; //конструктор для класса Cat public Cat(String name, int age) { this.catname = name; this.catage = age; }
Ivan Tanashev Уровень 13
31 января 2024
Я думаю, abstraction не совсем верно локализировано на русский. Тут скорее подходит "абстрагирование", судя по описанию
AkkelaForest #3399225 Уровень 16
22 января 2024
Большое спасибо за эту статью! Наконец-то стало понятно значение static
Anastasia Уровень 20
18 декабря 2023
Напишите пожалуйста, правильно ли я поняла, если упростить еще сильнее, до примитива: Класс - это место(файл). Типа оранжерея Объект - это предмет. Типа цветок в горшке Переменная - это деталь, она может быть деталью объекта (это поле). Типа цвет горшка - красный. Либо деталью класса(это переменная со словом static). Типа цвет оранжереи - зеленый. Массив - это много деталей в одном. Типа комода с деталями. Метод - это действия. Он может быть для предметов(т.е метод внутри которого будет работа с объектами), либо для деталей предмета(метод, внутри которого будет работа только с переменными) А абстракция - это место, где есть хранение, но нет действия. Типа комнаты хранилища, где хранятся семена нужных растений из оранжереи. Конструктор - это получается стикер на горшке, где написано что это за растение. Если нужно просто чтобы в оранжерее были растения в горшках - просто создаем обьект. Получается мы не должны ему сами прописывать конструктор, но он скрыто создастся по дефолту. А если мы хотим поместить напр в оранжерею 2 обьекта: красный апельсин и каенский перец - мы сначала создаем 2 обьекта: оба обьекты, и создаем им конструктор, в котором указываем что это красный апельсин и перец. Типа клеем стикер на каждое растение. А что написано на стикере - наши параметры, которые мы хотим написать. Правильно ли я все усвоила, отпишитесь плс, я совсем новичок
Ваня Корниенко Уровень 43
17 декабря 2023
Ставлю дизлайк, так как данная статья (иногда прямо целыми кусками с кодом и объяснениями) скопипасчена с другой, написанной ранее.
7 декабря 2023
Отличная статья. Спасибо за разъяснения в ней.
spancky Уровень 9
4 декабря 2023
А как правильно делать в такой ситуации, например: у нас есть вышеописанный класс User из сайта знакомств и при создании нового пользователя мы же должны создать новый объект класса User? User user1 = new user();, а следующий будет User user2 = new user(); Как подставлять в имя обьекта класса (user1, user2,..userN) переменные? Или это делается вообще другой логикой?
Evgeny Gubanov Уровень 16
1 декабря 2023
А почему статья не удалена. Она же позаимствована у другого автора? (Оригинал ниже, сверьте даты) https://javarush.com/groups/posts/konstruktory-v-java-1