Appearance
Введение в ООП
Что такое ООП?
ООП (Объектно-ориентированное программирование), как ни странно, ориентировано на объекты. Срыв покровов, не правда ли?
ООП - это подход к разработке программ, в котором программа представляется как совокупность взаимодействующих объектов. Начнём с примеров. Нам нужно хранить всякую информацию о студентах. Например имя, возраст и оценки. Самый тупой вариант:
java
String student1Name = "Ivan";
int student1Age = 99;
List<Integer> student1Grades = new ArrayList<>();
student1Grades.add(5);
student1Grades.add(4);
String student2Name = "Aleg";
int student2Age = 12;
List<Integer> student2Grades = new ArrayList<>();
student2Grades.add(3);
student2Grades.add(3);
String student3Name = "Arkadiy";
int student3Age = 1234;
List<Integer> student3Grades = new ArrayList<>();
student3Grades.add(1);
student3Grades.add(1);
// ...
Что тут плохо? Данные одного студента разбросаны по отдельным переменным, приходится следить, какие переменные относятся к кому. А если студентов 10? 100? 1000? В таком случае нужно использовать класс.
java
class Student {
String name;
int age;
List<Integer> grades = new ArrayList<>();
}
В этом примере мы не создали студента. Мы описали, что такое студент. Какие данные будет храниться о каждом студенте.
Класс позволяет нам описать собственный тип данных, который описывает состояние объектов (поля) и их поведение (методы)
Класс и объект
Важно запомнить:
- Класс - описание того, какими будут объекты: какие у них есть данные и методы;
- Объект - конкретный экземпляр класса.
Простыми словами: класс - это чертёж, а объект - то, что по чертежу было создано. Другая аналогия: у нас у всех есть некоторое представление о человеке - наличие двух рук, двух ног, головы, туловища и т.д. Есть некоторый шаблон - этот шаблон можно назвать классом. Реально же существующий человек (фактически экземпляр данного класса) является объектом этого класса.
Объект (экземпляр) объединяет:
- состояние - данные, которые он хранит;
- поведение - действия, которые он умеет выполнять.
Создание объектов, оператор new
Собственно, а как создать конкретного студента? Создание объектов класса мало чем отличается от создания переменных. Указываем название класса (по сути тип данных) и потом название объекта.
java
Student student;
Обратите внимание. Мы ещё не создали сам объект! Мы создали переменную student, которая может хранить ссылку на объект типа Student в памяти (объявление переменной). Но самого объекта в памяти ещё нет!
А теперь создадим сам объект с помощью оператора new. С ним мы сталкивались ранее и теперь разберём подробнее:
java
student = new Student();
new создаёт новый объект класса и возвращает ссылку на него. Если прям простыми словами, то код:
java
Student student = new Student();
буквально означает
Создай объект
Studentи сохрани ссылку на него в переменнойstudent.
То есть student - объект (экземпляр) класса Student? Ну... Нет.
Самое время вспомнить про ссылочный тип данных. Переменная ссылочного типа хранит ссылку на объект либо специальное значение null, означающее отсутствие ссылки на объект. Не сам объект и не его содержимое! Ссылку на объект в памяти! То есть student - не объект класса! Это ссылка на объект, а сам объект - то, что находится в памяти и то, на что ссылается переменная student.
java
Student a = new Student();
Student b = a;
Здесь объект один! И есть две ссылки на него - a и b.
Так в итоге экземпляр или объект?
Никакой разницы. Экземпляр класса и объект класса - синонимы. И то, и то означает конкретную реализацию класса, созданную с помощью оператора new.
Поля
Переменные, объявленные на уровне класса называются полями. Они предназначены для хранения данных об объекте класса. Опять пример со студентами:
java
class Student {
String name; // поле
int age; // поле
List<Integer> grades = new ArrayList<>(); // не поверите. Поле
}
Теперь создадим объект класса и обратимся к его полю:
java
void main() {
Student std = new Student();
std.name = "Ivan";
IO.println(std.name);
}
Обращение к членам объекта (полям и методам) осуществляется с помощью точки. Так мы можем читать и записывать значения полей.
ТОЧКА = ЗЛО
Забегая немного вперёд: почти никогда мы не обращаемся к полям объекта через точку. Просто примите к сведению.
Методы
Как мы уже выяснили, класс описывает информацию об объектах с помощью полей. А работает он с этой информацией посредством методов. С методами мы уже сталкивались. Например main(). Или методы из четвёртой практической работы первого блока. До изучения классов методы были просто отдельным именованным блоком кода:
java
public static int add(int a, int b) {
return a + b;
}
Теперь методы становятся частью класса и описывают поведение объектов этого класса. Опять вернёмся к бедным студентам и создадим пару методов:
java
class Student {
String name;
int age;
List<Integer> grades = new ArrayList<>();
void sayHello() {
IO.println("Hello! My name is " + name);
}
}
В примере выше мы создали простейший метод. Теперь вызовем его:
java
void main() {
Student std = new Student();
std.name = "Aleg";
std.sayHello(); // Hello! My name is Aleg
}
Обратите внимание на две вещи:
- Теперь у методов необязательно писать
publicиstatic. Достаточно возвращаемого типа данных и названия. - Метод принадлежит экземпляру класса. Мы пишем не
sayHello(), неStudent.sayHello(), аstd.sayHello().
Как обсуждалось выше, к методам мы обращаемся через точку. И вот тут вспомните про то, что я говорил ранее - скобки при вызове метода и почему это важно:
java
class Test {
String text;
void text() {
//...
}
}
void main() {
Test t = new Test();
t.text; // обращение без скобок - обращение к полю
t.text(); // обращение со скобками - вызов метода
}
this, ссылка на текущий объект
Для ссылки на текущий объект используется ключевое слово this.
java
class Student {
String name;
void sayHello() {
IO.println(this.name);
}
}
// ...
void main() {
Student s1 = new Student();
Student s2 = new Student();
s1.sayHello();
s2.sayHello();
}
При s1.sayHello(); this будет ссылаться на s1, при s2.sayHello(); - на s2. А зачем вообще это надо? Ведь всё прекрасно работает и без this. Вопрос хороший. В простых случаях this в принципе не нужен:
java
class Student {
String name;
void sayHello() {
IO.println(name); // всё прекрасно работает
}
}
Тут всё работает, потому что в этом методе имя name однозначно обозначает поле объекта, поэтому this можно опустить. А теперь другой пример:
java
class Student {
String name;
void setName(String name) {
// name = name а что из этого ссылается на поле, а что на параметр?
this.name = name;
}
}
В примере выше this.name означает поле объекта, а name - параметр метода.
Конструкторы
До сих пор объекты классов мы создавали "пустыми", а потом ручками заполняли поля:
java
Student student = new Student();
student.name = "Ivan";
student.age = 20;
Удобно? Ну, не очень, но в целом приемлемо. А если так?
java
public class User {
Integer id;
String email;
String phone;
String passwordHash;
Instant createdAt;
Pharmacy pharmacy;
String firstName;
String lastName;
Set<Role> userRoles = new HashSet<>();
Boolean isDeleted;
Instant deletedAt;
Boolean emailVerified;
Instant emailVerifiedAt;
}
А теперь давайте создадим 3 объекта классы из примера выше. Уже не так весело, правда? А если мы случайно пропустим одно из полей? Объект вроде создан, но находится в не совсем правильном (консистентном) состоянии.
Собственно, для этого существуют конструкторы - специальные методы в классе, которые автоматически вызываются при создании объекта и управляют "первоначальной настройкой" (инициализацией) этого объекта.
Например:
java
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
}
То есть конструктор - это некоторое нечто, похожее на метод, но в терминологии Java это специальный член класса, предназначенный для инициализации объекта. Он обязательно должен называться так же, как и класс (учитывайте и регистр).
Теперь при создании объекта:
java
Student student = new Student("Ivan", 20);
Java создаёт объект и вызывает его конструктор, передавая ему "Ivan" и 20.
Внутри конструктора:
java
this.name = name;
this.age = age;
this.name и this.age - поля создаваемого объекта, а name и age - параметры конструктора.
Например:
java
Student s1 = new Student("Ivan", 20);
Student s2 = new Student("Petr", 21);
Получаются два разных объекта с разным начальным состоянием.
АХТУНГ
Важно: конструктор называется так же, как класс, и не имеет возвращаемого типа. Даже void писать нельзя. Правильно:
java
Student(String name, int age) {
this.name = name;
this.age = age;
}
Неправильно:
java
void Student(String name, int age) {
// ...
}
Конструктор по умолчанию
Ранее мы спокойно писали:
java
Student student = new Student();
Тут при создании мы используем конструктор, просто не передаём ему аргументов. Но мы вроде никакого конструктора не создавали?..
Если в классе вообще нет ни одного конструктора, Java автоматически предоставляет конструктор без параметров вида:
java
Student() { }
Поэтому такой код работает нормально:
java
class Student {
String name;
int age;
}
Student student = new Student();
Но! Конструктор по умолчанию создаётся только в том случае, если мы не создали других конструкторов в классе. Например:
java
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
}
Student student = new Student(); // Ошибка
В примере выше пустой конструктор уже не создаётся автоматически, поэтому попытка его использовать приведёт к ошибке. Если нужно, то вы можете сами создать такой конструктор:
java
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
Student() { }
}
В таком случае вы можете писать:
java
Student student1 = new Student("Beatrice", 42);
Student student2 = new Student();
То есть, вы можете объявить в классе столько конструкторов, сколько хотите. На любые случаи жизни.
Статические методы и поля
ЗАПОМНИТЕ, РАДИ БОГА
СТАТИЧЕСКИЕ! НЕ СТАТИЧНЫЕ! УПАСИ БОЖЕ ХОТЬ ОДНА ЖИВАЯ ДУША ПЕРЕПУТАЕТ
Собственно, вернёмся к модификатору static, который видим ещё с самой первой лекции. В примерах выше все методы и поля относились к экземпляру класса. Но иногда нужно, чтобы метод относился к самому классу. Для этого и используется модификатор static. Например:
java
class MathUtils {
static int sum(int a, int b) {
return a + b;
}
}
Для использования метода sum нам не нужно создавать экземпляр класса:
java
void main() {
int result = MathUtils.sum(5, 3); // 8
}
Мы обращаемся к нему по имени класса, без создания объекта класса.
То же самое относится и к полям. Поле, объявленное как статическое, не относится к объекту класса.
java
class Test {
static int counter = 0;
}
// ...
void main() {
Test.counter++;
}
Нюансики
Первое: статический метод не может обратиться к полю объекта, если поле является нестатическим.
java
class Student {
String name;
static void printName() {
IO.println(name); // non-static чётатам can't be referenced from чотатам. Ошибка короче
}
}
Такое поведение может показаться нелогичным и неоправданным на первый взгляд. Но на самом деле, всё просто. Проблема в том, что name принадлежит конкретному объекту.
Например:
java
Student s1 = new Student();
Student s2 = new Student();
Student.printName(); // а чьё имя-то вызывать? От s1 или s2?
Но можно сделать по-тупому, если прям сильно хочется:
java
class Student {
String name;
static void printName(Student s) { // явно передаём студента в метод
IO.println(s.name);
}
}
Второе: статический метод не обладает this, потому что this ссылается на текущий объект, а статические методы в принципе не подразумевают наличие объекта. Ссылаться не на что.
main()
Небольшое отступление по поводу метода main().
java
class Main {
public static void main(String[] args) {
// ...
}
}
Почему main() статический? Потому что JVM должна иметь возможность вызвать его через класс Main, не создавая объект класса Main.
Контрольные вопросы
- Что такое класс, а что такое объект?
- Что произойдёт при выполнении
Student student = new Student();? - Чем переменная
studentотличается от объекта, на который она ссылается? - Что такое поле класса и что оно хранит?
- Для чего нужны методы внутри класса?
- Что означает запись
student.sayHello()? Что здесь такоеstudent, а чтоsayHello()? - Для чего используется
this? На какой объект оно ссылается? - Чем отличается обычный метод от статического метода?
- Почему статический метод не может напрямую обратиться к нестатическому полю объекта?
- Представьте, что есть два объекта
Student:s1иs2. У обоих есть полеname, ноs1.name = "Иван", аs2.name = "Пётр". Если вызватьs1.sayHello()иs2.sayHello(), почему один и тот же метод может вывести разные имена?
Задачи
Это не практическая работа
Отныне и далее практические работы будут не после каждой лекции. Обращайте внимание на название раздела. Задачи - делаете по желанию после лекции на доп. оценку. Практическая работа - обязательна к выполнению.
1. Студент
Создайте класс Student, содержащий поля: Ну и зачем ты это копируешь? Чтоб в ЖПТ вставить, да?
name- имя;age- возраст;averageGrade- средняя оценка.
Создайте два объекта этого класса с помощью конструктора с разными данными и выведите информацию о каждом студенте.
2. Прямоугольник
Создайте класс, описывающий прямоугольник. Класс должен хранить информацию о высоте и ширине прямоугольника и иметь метод, который возвращает площадь прямоугольника. В main() создайте два прямоугольника и выведите их площадь.
3. Банковский счёт
Создайте класс BankAccount с полями:
owner- имя владельца;balance- баланс.
Добавьте методы:
deposit(double amount)- пополнение счёта;withdraw(double amount)- снятие денег.
При снятии денег не допускайте, чтобы баланс становился отрицательным.
Создайте счёт и выполните несколько операций с ним.
4. this
Создайте класс Book с полями title и author.
Добавьте метод:
void setInfo(String title, String author)
который устанавливает значения обоих полей.
Используйте this, чтобы отличить поля класса от параметров метода.
После вызова метода выведите информацию о книге.
5. Статический счётчик
Создайте класс Student, содержащий имя студента и статическое поле count, хранящее количество созданных объектов Student.
При создании каждого нового студента увеличивайте count.
В main() создайте несколько студентов и выведите количество созданных объектов.