🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье разберемся, что такое классы в Java. По сути, классы — это фундамент программирования на Java. Работа разработчика, в значительной степени, сводится к созданию собственных классов с определённой функциональностью. Давайте разберёмся, что это означает на практике.
Java — объектно-ориентированный язык программирования. Любое приложение на Java состоит из объектов, которые взаимодействуют между собой.
Класс в Java — это шаблон (или описание), на основе которого создаются объекты. Он определяет структуру объекта и его поведение. Каждый объект обязательно принадлежит какому-либо классу.
Вот простой пример:
public class Cat {
String name;
int age;
}
Run CodeДопустим, мы разрабатываем приложение, связанное с кошками (например, у нас есть ветклиника с доступом к личному кабинету).
Мы создаём класс Cat
и добавляем в него две переменные: String name
и int age
. Эти переменные называются поля (fields) — они принадлежат объекту.
По сути, это шаблон для всех котов, которых мы будем создавать в будущем. Каждый объект Cat
будет содержать два поля: имя (name) и возраст (age).
public class Cat {
String name;
int age;
public static void main(String[] args) {
Cat smudge = new Cat();
smudge.age = 3;
smudge.name = "Smudge";
System.out.println("We created a cat named " + smudge.name + ". His age is " + smudge.age);
}
}
Run CodeВот как это работает! Мы создаём кота, задаём ему имя и возраст — и выводим всё это в консоль. Проще некуда. 🙂
Чаще всего классы описывают реальные вещи и явления.
Кошка, стол, человек, молния, книжная страница, колесо — все эти сущности представляются в программе как отдельные классы.
Теперь рассмотрим подробнее переменные, объявленные в классе Cat
. Они называются полями или переменными экземпляра (instance variables). Это означает, что каждый объект, созданный на основе класса Cat
, будет иметь собственные копии этих переменных. У каждого кота будет своё значение имени и свой возраст — всё как в жизни. 🙂
Кроме переменных экземпляра, бывают ещё и переменные класса (они же static-переменные). Дополним наш пример:
public class Cat {
String name;
int age;
static int count = 0;
public static void main(String[] args) {
Cat smudge = new Cat();
smudge.age = 3;
smudge.name = "Smudge";
count++;
Cat fluffy = new Cat();
fluffy.age = 5;
fluffy.name = "Fluffy";
count++;
System.out.println("We created a cat named " + smudge.name + ". His age is " + smudge.age);
System.out.println("We created a cat named " + fluffy.name + ". His age is " + fluffy.age);
System.out.println("Total number of cats = " + count);
}
}
Run CodeВот что отобразится в консоли:
We created a cat named Smudge. His age is 3
We created a cat named Fluffy. His age is 5
Total number of cats = 2
Теперь в нашем классе появилась новая переменная — count
. Она отвечает за подсчёт созданных котов. Каждый раз, когда в методе main
создаётся новый объект Cat
, мы увеличиваем значение этой переменной на единицу.
Эта переменная объявлена с ключевым словом static
. Это значит, что она принадлежит самому классу, а не конкретному объекту. И это логично: имя — это индивидуальное свойство каждого кота, а счётчик нужен общий — один на всех. Именно благодаря static
переменная count
становится единой для всех объектов, независимо от того, сколько котов мы создадим.
Важно: когда мы выводим значение переменной count
, мы не пишем smudge.count
или fluffy.count
. Потому что она — не часть конкретного кота. Она принадлежит всему классу Cat
. Поэтому просто count
— и всё.
Можно также использовать Cat.count
— это тоже будет правильно.
Однако для нестатических переменных, таких как name
, такой подход не работает. Мы не можем обращаться к ним вот так:
public class Cat {
String name;
int age;
static int count = 0;
public static void main(String[] args) {
Cat smudge = new Cat();
smudge.age = 3;
smudge.name = "Smudge";
count++;
System.out.println("We created a cat named " + name + ". His age is " + smudge.age);
System.out.println("Total number of cats = " + count);
}
}
Run CodeЭто ошибка! У каждого кота — своё имя. Компилятор просто не понимает, что вы от него хотите.
“Вывести имя в консоль? А чьё именно?”
Методы
Помимо переменных, у каждого класса есть методы.
Методы определяют функциональность класса — другими словами, то, что объекты этого класса умеют делать. С одним из таких методов вы уже знакомы — это метод main()
. Но main
— это статический метод, то есть он принадлежит всему классу, а не конкретному объекту (логика здесь такая же, как и у переменных).
Следует помнить, что нестатические (или обычные) методы можно вызывать только у определенных объектов, которых мы создали.
К примеру, если мы хотим написать класс Cat
, нам нужно заранее определить, какие действия должен выполнять кот в нашей программе. Исходя из этого, мы и добавим несколько методов:
public class Cat {
String name;
int age;
public void sayMeow() {
System.out.println("Meow!");
}
public void jump() {
System.out.println("Pounce!");
}
public static void main(String[] args) {
Cat smudge = new Cat();
smudge.age = 3;
smudge.name = "Smudge";
smudge.sayMeow();
smudge.jump();
}
}
Run CodeГотово! Теперь наш класс Cat
стал ближе к реальному объекту. Помимо имени (Smudge) и возраста (3), у кота появились базовые поведенческие методы: sayMeow()
и jump()
. Какой бы это был кот без этой «функциональности»? 🙂
Мы берём объект smudge
и вызываем у него методы sayMeow()
и jump()
. Посмотрим, что появится в консоли:
Meow!
Pounce!
Настоящий кот! 🙂
Создание собственных классов. Абстракция
В будущем вам предстоит создавать свои собственные классы. На что стоит обратить внимание при их написании?
Если мы говорим о переменных, то тут вам пригодится такой принцип, как абстракция.
Абстракция — это один из четырёх основных принципов объектно-ориентированного программирования. Она заключается в выделении ключевых и значимых характеристик объекта, исключая те, которые не имеют существенного значения.
Например, давайте создадим картотеку для сотрудников компании. Чтобы представлять сотрудников как объекты, мы написали класс Employee
. Какие характеристики важны для картотеки сотрудников нашей компании? Имя, дата рождения, номер социального страхования и ID сотрудника. Но вряд ли нам понадобятся такие данные, как рост сотрудника, цвет глаз или цвет волос для учёта в компании. Эта информация для компании не имеет значения.
Итак, в классе Employee
мы объявляем такие переменные, как String name
, int age
, int socialSecurityNumber
и int employeeId
, а всё лишнее (например, цвет глаз) мы опускаем. То есть мы создаём абстракцию.
Если же речь идет о картотеке для модельных агентств, то ситуация меняется. Для модели критичными характеристиками будут её рост, цвет глаз и волосы, тогда как SSN нам совершенно не нужен.
Поэтому в классе Model
мы создадим переменные: String height
, String hair
, String eyes
.
Вот и вся суть абстракции — ничего сложного! 🙂
Конструкторы
Давайте вернёмся к нашему примеру с котом.
public class Cat {
String name;
int age;
public static void main(String[] args) {
Cat smudge = new Cat();
System.out.println("Here the program does something for 2 hours...");
smudge.age = 3;
smudge.name = "Smudge";
}
}
Run CodeПосмотрите на этот код и попробуйте понять, что не так с нашей программой.
У нас в программе два часа существовал кот без имени и возраста!
Это очевидное нарушение логики. База данных ветеринарной клиники не должна содержать неполные записи.
Сейчас наш кот полностью зависит от программиста. Мы просто надеемся, что он не забудет указать имя и возраст, и всё будет нормально. Но если он забудет — в базе появится ошибка: кот без имени.
Как устранить данную проблему? Необходимо запретить создание котов без указания имени и возраста. И здесь нам на помощь приходят конструкторы.
Вот пример:
public class Cat {
String name;
int age;
// Constructor for the Cat class
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Cat smudge = new Cat("Smudge", 5);
}
}
Run CodeПо сути, конструктор — это шаблон для создания объектов класса.
В нашем примере конструктор требует два параметра: String
и int
. Это означает, что при создании объекта 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 smudge = new Cat(); // Error!
}
}
Run CodeТеперь, когда в классе есть конструктор, компилятор Java точно знает, как должны выглядеть объекты, и не позволит создать объект без указания аргументов.
Теперь рассмотрим ключевое слово this
, которое используется внутри конструктора. Оно указывает на конкретный объект, с которым мы работаем.
Код конструктора
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
Run Codeможно почти дословно перевести так:
“Имя этого кота (того, которого мы сейчас создаём) = то, что мы передали в name
.
Возраст этого кота = то, что мы передали в 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 smudge = new Cat("Smudge", 5);
System.out.println(smudge.name);
System.out.println(smudge.age);
}
}
Run CodeВывод:
Smudge
5
При вызове конструктора:
Cat smudge = new Cat("Smudge", 5);
Конструктор класса сработал и присвоил значения полям экземпляра:
this.name = "Smudge";
this.age = 5;
Run CodeАргументы, которые мы передали в конструктор, были присвоены объекту smudge
— именно на него ссылается this
.
На самом деле, даже если вы не объявите в классе ни одного конструктора, он всё равно будет вызываться!
Но как такое возможно? О_О
Всё просто: у любого класса есть “конструктор по умолчанию”. Он не требует параметров и автоматически срабатывает при создании объекта.
public class Cat {
public static void main(String[] args) {
Cat smudge = new Cat(); // The default constructor is invoked here
}
}
Run CodeСначала может показаться, что ничего особенного не происходит. Ну подумаешь, объект создали — а где же тут вообще конструктор?
Чтобы это проверить, добавим в класс Cat
явный конструктор без параметров и в его теле выведем строку в консоль. Если при создании объекта сообщение появится — значит, вызов конструктора состоялся.
public class Cat {
public Cat() {
System.out.println("A cat has been created!");
}
public static void main(String[] args) {
Cat smudge = new Cat(); // The default constructor is invoked here
}
}
Run CodeВывод:
A cat has been created!
Вот и подтверждение: конструктор по умолчанию действительно существует в каждом классе, просто его не видно.
Но есть тонкость. Если в класс добавлен хотя бы один конструктор с параметрами, Java удаляет конструктор по умолчанию.
На практике мы уже столкнулись с этим в примере выше:
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 smudge = new Cat(); // Error!
}
}
Run CodeСоздать кота без имени и возраста не получилось, потому что мы объявили конструктор с аргументами (String и int).
Это автоматически удалило конструктор по умолчанию из класса.
Важно помнить: если вам нужен и конструктор с параметрами, и без, — нужно все варианты описывать вручную.
Наша клиника хочет делать добрые дела и помогать бездомным котятам, чьи имя и возраст неизвестны.
Тогда наш код должен выглядеть так:
public class Cat {
String name;
int age;
// For cats with owners
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
// For street cats
public Cat() {
}
public static void main(String[] args) {
Cat smudge = new Cat("Smudge", 5);
Cat streetCat = new Cat();
}
}
Run CodeС явным конструктором по умолчанию мы можем создавать и обычных, и бездомных котов. В конструкторе можно присваивать значения напрямую — необязательно всегда передавать их через аргументы. Например, называть всех уличных котов по шаблону: "Street cat No. <count>"
:
public class Cat {
String name;
int age;
static int count = 0;
public Cat() {
count++;
this.name = "Street cat No. " + 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);
}
}
Run CodeУ нас есть переменная count
, которая считает количество уличных котов. Каждый раз при вызове конструктора по умолчанию мы увеличиваем count
на 1 и добавляем это число к имени кота.
Очень важно соблюдать порядок аргументов в конструкторах. Попробуем поменять местами аргументы name
и age
в нашем конструкторе — и посмотрим, что получится.
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 smudge = new Cat("Smudge", 10); // Error!
}
}
Run CodeОшибка! Конструктор четко указывает, что при создании объекта 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;
}
Run CodeЭто два совершенно разных конструктора!
А теперь — немного практики, чтобы закрепить материал. 🙂
1. Музей древностей.
Ваша задача — разработать класс Artifact
.
В музее хранятся три типа артефактов:
1. О первом мы знаем только номер, который присвоил музей (например: 212121
).
2. О втором — порядковый номер и культуру, создавшую артефакт (например: 212121
, "Aztecs"
).
3. О третьем — порядковый номер, культуру и век, в котором артефакт был создан (например: 212121
, "
, Aztecs
"12
).
Создайте класс Artifact
, описывающий эти древности, и напишите необходимый набор конструкторов.
Затем в методе main()
создайте по одному артефакту каждого типа.
public class Artifact {
// Напишите свой код здесь
public static void main(String[] args) {
// Напишите свой код здесь
}
}
Run Code2. Сайт знакомств
Нужно создать базу пользователей для сайта знакомств.
Но возникла проблема: вы забыли правильный порядок аргументов, а техническая документация отсутствует.
Создайте класс User
с полями: name
(тип String
), age
(тип short
) и height
(тип int
).
Добавьте необходимое количество конструкторов, чтобы можно было задавать name
, age
и height
в любом порядке.
public class User {
String name;
short age;
int height;
// Напишите свой код здесь
public static void main(String[] args) {
}
}
Run CodeЧтобы закрепить полученные знания, предлагаем вам посмотреть видеоурок (на английском языке):
Перевод статьи «Java Classes and Objects».