Die while- und do-while-Schleife in Java 17: Eine ausführliche Anleitung
ООП в Java: классы, объекты, наследование и полиморфизм
Объектно-ориентированное программирование (ООП) — это подход к написанию кода, при котором программа моделирует реальный мир через объекты. Вместо набора инструкций — объекты с данными и поведением. Java спроектирована вокруг ООП.
Класс и объект: чертёж и дом
Класс — архитектурный чертёж. Описывает как выглядит и ведёт себя объект.
Объект — конкретный дом, построенный по этому чертежу.
По одному чертежу строят много домов — каждый независимый. Один класс → много объектов.
// КЛАСС — шаблон
class Car {
// Поля (атрибуты, данные)
String brand;
String model;
int year;
double price;
boolean isRunning;
// Методы (поведение)
void start() {
if (!isRunning) {
isRunning = true;
System.out.println(brand + " " + model + " заведён!");
} else {
System.out.println("Двигатель уже работает.");
}
}
void stop() {
if (isRunning) {
isRunning = false;
System.out.println(brand + " " + model + " остановлен.");
}
}
void honk() { System.out.println(brand + ": Бип-бип!"); }
void describe() {
System.out.printf("%d %s %s, %.0f₽%n", year, brand, model, price);
}
}
public class ClassDemo {
public static void main(String[] args) {
// Создаём ОБЪЕКТЫ с помощью new
Car lada = new Car();
lada.brand = "ВАЗ";
lada.model = "Лада Гранта";
lada.year = 2022;
lada.price = 850_000;
Car bmw = new Car();
bmw.brand = "BMW";
bmw.model = "X5";
bmw.year = 2023;
bmw.price = 7_500_000;
// Каждый объект независим!
lada.describe();
bmw.describe();
lada.start(); // ВАЗ Лада Гранта заведён!
lada.honk();
lada.start(); // Двигатель уже работает.
lada.stop();
bmw.start();
bmw.honk(); // BMW: Бип-бип!
}
}
Конструкторы: правильная инициализация
class Point {
double x, y;
// Конструктор по умолчанию (начало координат)
Point() { x=0; y=0; }
// С параметрами
Point(double x, double y) {
this.x = x; // this.x — поле; x — параметр конструктора
this.y = y;
}
// Конструктор копирования
Point(Point other) { this.x=other.x; this.y=other.y; }
double distanceTo(Point other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
return Math.sqrt(dx*dx + dy*dy);
}
@Override
public String toString() {
return String.format("(%.1f, %.1f)", x, y);
}
}
Point origin = new Point(); // (0.0, 0.0)
Point p1 = new Point(3.0, 4.0); // (3.0, 4.0)
Point p2 = new Point(0.0, 0.0);
Point p1copy = new Point(p1); // Копия p1
System.out.println(p1); // (3.0, 4.0)
System.out.println(p1.distanceTo(p2)); // 5.0 (теорема Пифагора: 3²+4²=5²)
p1copy.x = 10.0; // Изменяем копию — оригинал не трогаем
System.out.println(p1); // (3.0, 4.0) — оригинал не изменился
System.out.println(p1copy); // (10.0, 4.0) — копия изменена
Инкапсуляция: скрываем детали
Аналогия: Телевизор. Вы нажимаете кнопки пульта (публичный интерфейс) — не зная что происходит внутри (скрытая реализация). Именно так работает инкапсуляция.
class Student {
private String name; // private — только внутри класса
private int grade; // оценка 1-5
private double gpa;
// Конструктор с валидацией
public Student(String name, int grade) {
this.name = name;
setGrade(grade); // используем setter с проверкой
}
// Getter — только читаем
public String getName() { return name; }
public int getGrade() { return grade; }
public double getGpa() { return gpa; }
// Setter с валидацией — защита от некорректных данных
public void setGrade(int grade) {
if (grade < 1 || grade > 5) {
System.out.println("Ошибка: оценка должна быть от 1 до 5!");
return;
}
this.grade = grade;
this.gpa = (grade - 1) * 1.25; // Вычисляем GPA автоматически
}
public void printInfo() {
System.out.printf("Студент: %s, оценка: %d, GPA: %.2f%n", name, grade, gpa);
}
}
Student s = new Student("Иван", 4);
s.printInfo(); // Студент: Иван, оценка: 4, GPA: 3.75
s.setGrade(5); // Обновляем через setter
s.printInfo(); // Студент: Иван, оценка: 5, GPA: 5.00
s.setGrade(7); // Ошибка: оценка должна быть от 1 до 5!
// s.grade = 10; // ОШИБКА КОМПИЛЯЦИИ: grade has private access
Наследование
// Базовый класс
class Animal {
protected String name; protected int age;
Animal(String name, int age) { this.name=name; this.age=age; }
void breathe() { System.out.println(name + " дышит"); }
void eat(String food) { System.out.println(name + " ест " + food); }
void sleep() { System.out.println(name + " спит"); }
String info() { return name + " (" + age + " лет)"; }
}
// Dog наследует Animal и добавляет своё
class Dog extends Animal {
private String breed;
private boolean vaccinated;
Dog(String name, int age, String breed) {
super(name, age); // ПЕРВЫМ вызываем конструктор родителя!
this.breed = breed;
this.vaccinated = false;
}
// Специфичные методы Dog
void bark() { System.out.println(name + " (" + breed + "): ГАВ-ГАВ!"); }
void fetch(String item) { System.out.println(name + " принёс " + item + "!"); }
void vaccinate() { vaccinated=true; System.out.println(name + " привит."); }
@Override // Переопределяем метод родителя
String info() { return super.info() + ", порода: " + breed + ", привит: " + vaccinated; }
}
class Cat extends Animal {
private boolean indoor;
Cat(String name, int age, boolean indoor) { super(name,age); this.indoor=indoor; }
void meow() { System.out.println(name + ": МЯУ~"); }
void purr() { System.out.println(name + " мурлычет ♪"); }
@Override String info() { return super.info()+(indoor?", домашняя":", уличная"); }
}
Dog rex = new Dog("Рекс", 3, "Немецкая овчарка");
Cat mur = new Cat("Мурка", 5, true);
// Методы унаследованные от Animal
rex.breathe(); rex.eat("мясо"); rex.sleep();
// Специфичные методы
rex.bark(); rex.fetch("мячик"); rex.vaccinate();
System.out.println(rex.info());
mur.meow(); mur.purr();
System.out.println(mur.info());
// instanceof — проверка типа
System.out.println(rex instanceof Animal); // true
System.out.println(rex instanceof Dog); // true
System.out.println(rex instanceof Cat); // false
Полиморфизм: один вызов — разное поведение
abstract class Shape {
protected String color;
Shape(String c) { color=c; }
abstract double area(); // Каждый подкласс ОБЯЗАН реализовать
abstract double perimeter();
void describe() { // Общий метод — вызывает переопределённые методы
System.out.printf("[%s] площадь=%.2f, периметр=%.2f%n",
color, area(), perimeter());
}
}
class Circle extends Shape {
double r;
Circle(String c, double r) { super(c); this.r=r; }
@Override double area() { return Math.PI*r*r; }
@Override double perimeter() { return 2*Math.PI*r; }
}
class Rectangle extends Shape {
double w,h;
Rectangle(String c, double w, double h) { super(c); this.w=w; this.h=h; }
@Override double area() { return w*h; }
@Override double perimeter() { return 2*(w+h); }
}
class Square extends Shape {
double side;
Square(String c, double s) { super(c); side=s; }
@Override double area() { return side*side; }
@Override double perimeter() { return 4*side; }
}
// ПОЛИМОРФИЗМ: один и тот же вызов — разное поведение у разных объектов
Shape[] shapes = {
new Circle("красный",5), new Rectangle("синий",4,6),
new Square("зелёный",3), new Circle("жёлтый",2)
};
double totalArea = 0;
for (Shape s : shapes) {
s.describe(); // У каждой фигуры своя реализация area() и perimeter()
totalArea += s.area();
}
System.out.printf("Суммарная площадь: %.2f%n", totalArea);
Интерфейсы
interface Drawable {
void draw(); // abstract по умолчанию
default void print() { // default — есть реализация
System.out.println("Рисую: " + getClass().getSimpleName());
}
static void info() { // static метод интерфейса
System.out.println("Drawable interface v1.0");
}
}
interface Saveable { void save(String file); }
interface Resizable { void resize(double factor); double getSize(); }
// Класс может реализовать несколько интерфейсов!
class SmartShape implements Drawable, Saveable, Resizable {
private double size; private String color;
SmartShape(double s, String c) { size=s; color=c; }
@Override public void draw() { System.out.printf("%s фигура размера %.1f%n",color,size); }
@Override public void save(String f) { System.out.println("Сохраняю в "+f); }
@Override public void resize(double f) { size*=f; System.out.printf("Новый размер: %.1f%n",size); }
@Override public double getSize() { return size; }
}
SmartShape ss = new SmartShape(5.0, "Синяя");
ss.draw();
ss.print(); // default метод
ss.resize(2.0);
ss.save("shape.dat");
Drawable.info(); // static метод
Итого: что мы узнали
- ✅ Класс = шаблон (чертёж), Объект = экземпляр (конкретный дом)
- ✅ Поля хранят данные, методы описывают поведение объекта
- ✅ Конструктор: инициализирует объект при
new;this— текущий объект - ✅ Инкапсуляция:
privateполя + публичные методы (геттеры/сеттеры) - ✅ Наследование (
extends): подкласс получает всё от родителя - ✅
super— обращение к родителю;@Override— переопределение метода - ✅ Полиморфизм: один вызов — разное поведение в разных классах
- ✅
abstractкласс: нельзя создать объект; содержит абстрактные методы - ✅ Интерфейс: контракт поведения; класс реализует через
implements - ✅ Класс наследует один класс (
extends), но реализует много интерфейсов (implements)