Java for Beginner
743 subscribers
707 photos
196 videos
12 files
1.14K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Основы ООП в Java

Глава 3. Наследование

Расширение классов с extends

Наследование — это механизм ООП, который позволяет создавать новый класс (подкласс или дочерний класс) на основе существующего (суперкласс или родительский класс). Подкласс "наследует" поля, методы и поведение суперкласса, добавляя или расширяя их. Это воплощает принцип "is-a" (является): например, "Собака является Животным".

Преимущества наследования:
Переиспользование кода: Не нужно дублировать общий код — подклассы используют то, что уже есть в суперклассе.
Иерархия: Позволяет строить иерархии классов, моделируя реальный мир (например, Животное → Млекопитающее → Собака).
Расширяемость: Подкласс может добавлять новые поля/методы или изменять поведение, не трогая суперкласс.
Полиморфизм: Наследование закладывает основу для полиморфизма (об этом в следующей главе).


В Java наследование реализуется с помощью ключевого слова extends. Java поддерживает только одиночное наследование (один суперкласс), но множественное — через интерфейсы.


Расширение классов с
extends: Основы

Чтобы создать подкласс, используйте extends после имени класса, указав суперкласс.

Синтаксис:

public class Подкласс extends Суперкласс {
// Дополнительные поля и методы
}


Подкласс автоматически наследует все non-private поля и методы суперкласса.
Конструкторы не наследуются — их нужно определять заново, но можно вызывать конструктор суперкласса с super().


Пример базового суперкласса Animal:

public class Animal {
protected String name; // Protected: доступно в подклассах
private int age; // Private: не видно в подклассах напрямую

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

public void eat() {
System.out.println(name + " ест.");
}

// Геттер для age (чтобы подклассы могли читать)
public int getAge() {
return age;
}
}


Теперь подкласс Dog, расширяющий Animal:
public class Dog extends Animal {
private String breed; // Новое поле

// Конструктор: Вызывает суперкласс с super()
public Dog(String name, int age, String breed) {
super(name, age); // Вызов конструктора Animal
this.breed = breed;
}

// Новый метод
public void bark() {
System.out.println(name + " лает: Гав!");
}

// Наследованный метод eat() доступен автоматически
}


Dog наследует name, eat() и getAge() от Animal.
super(name, age): Обязательно вызывает конструктор суперкласса (если он не по умолчанию). Должен быть первой строкой в конструкторе подкласса.


Новый метод bark(): Расширение поведения.
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Шарик", 5, "Лабрадор");
dog.eat(); // Наследованный: Шарик ест.
dog.bark(); // Новый: Шарик лает: Гав!
System.out.println("Возраст: " + dog.getAge()); // Возраст: 5
}
}



#Java #для_новичков #beginner #extends
👍4
Все нюансы расширения классов

Модификаторы доступа и наследование:
public: Наследуется и доступно везде.
protected: Наследуется и доступно в подклассах (даже в других пакетах) и в пакете суперкласса.
default (без модификатора): Наследуется только в том же пакете.
private: Не наследуется напрямую — подкласс не видит private-поля/методы. Используйте геттеры/сеттеры для доступа.
В примере name protected, так что Dog может использовать
this.name.

Конструкторы в наследовании:
Конструкторы не наследуются.
Если суперкласс имеет конструктор с параметрами, подкласс должен вызвать его с super(параметры).
Если суперкласс имеет только дефолтный конструктор, super() вызывается автоматически.
Нюанс: Если подкласс не вызывает super(), Java вставит super() без параметров. Если такого нет — ошибка компиляции.
Перегрузка: Подкласс может иметь несколько конструкторов, каждый вызывающий super().


Иерархия и класс Object:

Все классы в Java implicitly наследуют от java.lang.Object (если не указано
extends).
Методы Object, такие как toString(), equals(), hashCode(), наследуются всеми классами.
Нюанс: Если вы
extends другой класс, он уже наследует Object косвенно.

Одиночное наследование:
Java не поддерживает множественное наследование классов (extends A, B — ошибка). Это избегает "проблемы ромба" (конфликты при наследовании от двух классов с общим предком).
Для множественности используйте интерфейсы (об этом в главе об абстракции).


Final классы и методы:

Если суперкласс помечен final (final class Super {}), его нельзя extends — ошибка. (Например, String final.)
Нюанс: Это для immutable или secure классов.


Пакеты и видимость:
Если суперкласс в другом пакете, подкласс должен импортировать его (import package.Super;) или использовать полное имя.
Protected члены видимы в подклассах, даже в разных пакетах.


Ошибки и исключения:
Если подкласс пытается extends несуществующий класс — ошибка компиляции.
Циклическое наследование (A
extends B, B extends A) — запрещено.
Подкласс может быть abstract, даже если суперкласс не abstract.


Производительность и дизайн:
Наследование — мощный инструмент, но не злоупотребляйте: предпочитайте композицию (has-a) над наследованием (is-a), если возможно, чтобы избежать жесткой связи.
Нюанс: Глубокие иерархии (много уровней) могут усложнить код — старайтесь держать 2-3 уровня.



Как создать это в IntelliJ IDEA


Создайте суперкласс:
New → Java Class → Animal.

Создайте подкласс:

New → Java Class → Dog.
IntelliJ предложит
extends: В коде напишите extends Animal — IDE подскажет импорт.

Генерация конструктора:
В Dog: Generate → Constructor → Выберите поля, и укажите super.

Запустите: В Main создайте объект Dog и протестируйте.


Полезные советы для новичков


Используйте protected: Для полей/методов, которые нужны подклассам, но не внешнему коду.
Вызывайте super() первым: Всегда в начале конструктора — иначе ошибка.
Тестируйте наследование: Создавайте объекты подкласса и вызывайте наследованные методы.
Избегайте глубоких иерархий: Лучше плоская структура для простоты.



#Java #для_новичков #beginner #extends
👍4
Основы ООП в Java

Переопределение и ключевое слово super

Что такое переопределение методов в ООП?

Переопределение (method overriding) — это возможность подкласса предоставить свою реализацию метода, унаследованного от суперкласса. Это воплощает полиморфизм: объект подкласса может вести себя иначе, чем суперкласс, но сохранять ту же сигнатуру метода.

Когда использовать: Если поведение суперкласса не подходит для подкласса, но имя и параметры метода должны остаться теми же (например, все животные едят, но собака ест по-своему).

Правила переопределения:
Сигнатура метода (имя, параметры) должна быть идентичной суперклассу.
Тип возвращаемого значения может быть подтипом (covariant return type) в Java 5+.
Модификатор доступа не может быть строже (например, если в супер protected, в подклассе нельзя private, но можно public).
Метод в суперкласса не должен быть final или static (static не переопределяются, а скрываются).
Аннотация
@Override: Рекомендуется добавлять для проверки компилятором — если метод не переопределяет, ошибка.

Нюанс: Переопределение отличается от перегрузки (overloading) — перегрузка: разные сигнатуры в одном классе; переопределение: одинаковые сигнатуры в иерархии.


Ключевое слово super: Доступ к суперклассу

super — это ссылка на суперкласс, аналог this для родителя.


Оно используется для:
Вызова методов суперкласса (super.method()).
Доступа к полям суперкласса (super.field), если они скрыты.
Вызова конструктора суперкласса (super(params)).


super полезно, когда в подклассе переопределен метод, но нужно вызвать оригинальную версию из суперкласса.


Подробный пример

Возьмем класс: Animal (суперкласс) и Dog (подкласс).

Суперкласс Animal:
public class Animal {
protected String name;
private int age;

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

public void eat() {
System.out.println(name + " ест пищу.");
}

public int getAge() {
return age;
}
}


Подкласс Dog с переопределением:

public class Dog extends Animal {
private String breed;

public Dog(String name, int age, String breed) {
super(name, age); // Вызов конструктора суперкласса
this.breed = breed;
}

// Переопределение метода eat()
@Override // Аннотация для проверки
public void eat() {
super.eat(); // Вызов версии из суперкласса
System.out.println("Но предпочитает кости!"); // Расширение поведения
}

public void bark() {
System.out.println(name + " лает: Гав!");
}
}

Аннотация @Override: Указывает, что метод переопределен. Если ошибка (например, сигнатура не совпадает) — компилятор предупредит.
super.eat(): Вызывает оригинальный eat() из Animal, затем добавляет свое.
Если убрать super.eat(), метод полностью заменит поведение суперкласса.


Нюанс: Если поле в подклассе скрывает поле суперкласса (field hiding, редко рекомендуется), super.field дает доступ к скрытому.


#Java #для_новичков #beginner #extends #super
👍3
Все нюансы переопределения и super

Сигнатура и совместимость:
Имя, количество/типы параметров должны совпадать точно.
Возврат: Может быть подтипом (например, супер возвращает Animal, под — Dog).
Исключения: Подкласс может бросать меньше или подтипы исключений, но не больше (checked exceptions).


Модификаторы:
Доступ: Может быть шире (protected → public), но не уже (public → protected — ошибка).
Final: Final-методы нельзя переопределять.
Static: Static-методы не переопределяются — это method hiding. Вызов зависит от типа ссылки, не объекта.
Private: Private-методы не видны, так что не переопределяются.


super в конструкторах:
Должен быть первой строкой.
Если не указан, Java вставит super() без параметров.
Нюанс: В цепочке иерархий (A
extends B extends C) конструкторы вызываются сверху вниз: C() → super(B) → super(A).

super для методов и полей:

super.method(): Вызывает версию суперкласса, даже если переопределен.
Полезно для расширения, а не замены поведения.
Нюанс: super не работает для static — используйте SuperClass.method().
Field hiding: Если подкласс имеет поле с тем же именем, super.field дает доступ к суперклассу.


Ошибки компиляции и runtime:
Без @Override: Если сигнатура не совпадает, создастся новый метод (overloading вместо overriding) — неожиданное поведение.
Runtime: Если метод не переопределен правильно, вызовется версия суперкласса.
Abstract методы: Должны быть переопределены в non-abstract подклассах.


Полиморфизм и overriding:
Вызов метода зависит от типа объекта, не ссылки: Animal a = new Dog(); a.eat() — вызовет Dog.eat().
Нюанс: Для полей — наоборот, зависит от типа ссылки (field hiding, не overriding).


Дизайн и лучшие практики:
Переопределяйте только когда нужно изменить поведение.
Используйте super для композиции поведения.
Избегайте переопределения для радикальных изменений — лучше новый метод.
В больших иерархиях: Документируйте, что можно переопределять.



Как создать это в IntelliJ IDEA

Переопределение метода:
В подклассе Dog: Ctrl+O (Override Methods) → Выберите eat() — IDE добавит @Override и скелет.

Добавьте super:
В сгенерированном методе вставьте super.eat().
Проверка: IDE подскажет ошибки в сигнатуре или доступе.


Полезные советы для новичков

Всегда используйте @Override: Избегайте ошибок.
Тестируйте полиморфно: Создавайте ссылки суперкласса на объекты подкласса и проверяйте вызовы.
super в конструкторах: Не забывайте, если суперкласс требует параметров.
Избегайте field hiding: Лучше разные имена для полей.


#Java #для_новичков #beginner #extends #super
👍5
Основы ООП в Java

Глава 7. Принципы проектирования и хорошего кода

Композиция vs Наследование

В объектно-ориентированном программировании композиция и наследование — это два способа организации кода для повторного использования и связи между классами. Они решают разные задачи и подходят для разных ситуаций.

Наследование:
Механизм, при котором класс (подкласс) наследует поля, методы и поведение от другого класса (суперкласса) с помощью ключевого слова extends. Это воплощает отношение "является" (is-a). Например, Dog является Animal.

Композиция:
Механизм, при котором класс содержит объекты других классов как поля, используя их функциональность. Это воплощает отношение "имеет" (has-a). Например, Car имеет Engine.

Оба подхода позволяют повторно использовать код, но их применение влияет на гибкость, читаемость и масштабируемость программы.



Наследование: Когда использовать и проблемы

Наследование удобно, когда классы имеют четкое отношение "является", и подкласс естественным образом расширяет поведение суперкласса.

Пример наследования:
public class Animal {
protected String name;

public Animal(String name) {
this.name = name;
}

public void makeSound() {
System.out.println(name + " издает звук.");
}
}

public class Dog extends Animal {
public Dog(String name) {
super(name);
}

@Override
public void makeSound() {
System.out.println(name + " лает: Гав!");
}
}


Когда использовать:
Есть четкое is-a отношение: Dog — это Animal.
Нужно переопределить или расширить поведение (через override).
Полиморфизм: Работа с подклассами через ссылку на суперкласс (Animal[] animals).


Преимущества:
Простота: Код автоматически наследуется.
Полиморфизм: Легко реализовать через override.
Иерархия: Удобно для моделирования реальных сущностей (например, Животное → Млекопитающее → Собака).


Недостатки:
Жесткая связь: Подкласс привязан к суперклассу — изменения в суперклассе могут сломать подклассы.
Хрупкий базовый класс: Изменение метода в суперклассе может неожиданно повлиять на подклассы.
Ограничение Java: Только одиночное наследование (один
extends).
Сложность в глубоких иерархиях: Много уровней наследования трудно поддерживать.
Нарушение инкапсуляции: Подклассы могут зависеть от внутренней реализации.

Пример проблемы: Если добавить метод в Animal, который не подходит для всех подклассов (например, fly()), подклассы вроде Dog окажутся в неудобном положении.


Композиция: Когда использовать и преимущества

Композиция — это включение объектов других классов как полей, чтобы использовать их функциональность. Это отношения, где класс владеет компонентами.


Пример композиции:
public class Engine {
private int horsepower;

public Engine(int horsepower) {
this.horsepower = horsepower;
}

public void start() {
System.out.println("Двигатель с " + horsepower + " л.с. запущен.");
}
}

public class Car {
private String model;
private Engine engine; // Композиция: Car "имеет" Engine

public Car(String model, Engine engine) {
this.model = model;
this.engine = engine;
}

public void drive() {
engine.start();
System.out.println(model + " едет.");
}
}


Использование:
public class Main {
public static void main(String[] args) {
Engine engine = new Engine(200);
Car car = new Car("Toyota", engine);
car.drive(); // Двигатель с 200 л.с. запущен. Toyota едет.
}
}


Когда использовать:
Отношения: Car имеет Engine, а не является им.
Нужно гибкое сочетание функциональности без жесткой связи.
Требуется заменяемость компонентов (например, разные двигатели).


Преимущества:
Гибкость: Легко заменить компонент (например, Engine на ElectricEngine).
Слабая связь: Изменения в Engine не ломают Car, если интерфейс сохранен.
Инкапсуляция: Car скрывает детали Engine, управляя доступом.
Модульность: Компоненты можно использовать в других классах.
Тестирование: Легче подменять компоненты (mocking) для тестов.


#Java #для_новичков #beginner #OOP #Composition #Extends
👍4
Недостатки:
Больше кода: Нужно явно создавать и передавать компоненты.
Сложнее полиморфизм: Требуются интерфейсы для единообразия.



Композиция vs Наследование: Как выбирать

Выбирайте композицию, если:
Нет четкого is-a отношения, а скорее has-a.
Нужна гибкость: Компоненты могут меняться (например, разные реализации Engine).
Хотите избежать жесткой связи или хрупкого базового класса.
Нужно реализовать поведение, которое не должно быть частью иерархии.

Выбирайте наследование, если:
Есть четкое is-a отношение, и подкласс логично расширяет суперкласс.
Нужен полиморфизм через ссылки на суперкласс.
Иерархия простая (1-2 уровня).


Пример: Композиция вместо наследования:

Наследование (проблема):
public class Vehicle {
public void move() {
System.out.println("Движется с помощью двигателя.");
}
}

public class Car extends Vehicle {
// Проблема: А если электромобиль или велосипед?
}

Композиция (решение):
public interface Movable {
void move();
}

public class EngineMovable implements Movable {
@Override
public void move() {
System.out.println("Движется с помощью двигателя.");
}
}

public class Car {
private Movable movement;

public Car(Movable movement) {
this.movement = movement;
}

public void drive() {
movement.move();
}
}

Теперь Car может использовать любой Movable (двигатель, электромотор), без привязки к иерархии.


Все нюансы

Наследование:
Жесткая связь: Подкласс зависит от реализации суперкласса.
Проблема ромба: В Java избежана (одиночное наследование), но множественное через интерфейсы.
Нюанс: Переопределение может нарушить контракт (Liskov Substitution Principle).


Композиция:
Требует интерфейсов для полиморфизма (например, Movable).
Нюанс: Больше кода для передачи компонентов (конструктор, сеттеры).
Делегирование: Car вызывает методы Engine — это нормально.


Когда комбинировать:
Используйте наследование для базовой структуры, композицию для поведения.
Пример:
extends AbstractVehicle, но Engine как поле.

Ошибки:
Наследование вместо композиции: Глубокие иерархии или неподходящие is-a.
Композиция без интерфейсов: Теряется полиморфизм.


Дизайн:
Композиция соответствует принципу KISS (меньше связей).
Наследование — YAGNI: Не создавайте иерархии "на будущее".
DRY: Оба подхода помогают повторно использовать код.


Java-специфика:
Интерфейсы + default методы часто заменяют наследование.
Dependency Injection (Spring) — пример композиции.



Как создать это в IntelliJ IDEA

Композиция: Создайте интерфейс (New → Interface), класс с полем и методами.
Наследование: New → Class, укажите
extends, IDE поможет override.
Рефакторинг: Extract Interface для перехода от наследования к композиции.



Полезные советы для новичков

Композиция по умолчанию: Если сомневаетесь, начните с has-a.
Проверяйте is-a: Dog is-an Animal? Да. Car is-an Engine? Нет.
Интерфейсы для гибкости: Комбинируйте композицию с интерфейсами.
Избегайте глубоких иерархий: Не больше 2-3 уровней.



#Java #для_новичков #beginner #OOP #Composition #Extends
👍4