RU | EN | DE

Encapsulation

We hide fields, providing access only through methods.

class BankAccount {
    private double balance;
    public void deposit(double amount) { balance += amount; }
    public void withdraw(double amount) {
        if (amount <= balance) balance -= amount;
        else throw new IllegalArgumentException("Недостаточно средств");
    }
    public double getBalance() { return balance; }
}
class Main {
    public static void main(String[] args) {
        BankAccount acc = new BankAccount();
        acc.deposit(100);
        acc.withdraw(40);
        System.out.println(acc.getBalance()); // 60.0
    }
}

Inheritance

class Animal {
    void sound() { System.out.println("Some sound"); }
}
class Dog extends Animal {
    @Override
    void sound() { System.out.println("Woof!"); }
}
class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.sound(); // Woof!
    }
}

Abstraction

abstract class Shape {
    abstract double area();
}
class Circle extends Shape {
    double r;
    Circle(double r) { this.r = r; }
    @Override double area() { return Math.PI * r * r; }
}
class Main {
    public static void main(String[] args) {
        Shape s = new Circle(3);
        System.out.println(s.area()); // 28.27
    }
}

Polymorphism

interface Payment {
    void pay(double amount);
}
class CardPayment implements Payment {
    public void pay(double amount) {
        System.out.println("Pay by card: " + amount);
    }
}
class CashPayment implements Payment {
    public void pay(double amount) {
        System.out.println("Pay by cash: " + amount);
    }
}
class Main {
    public static void main(String[] args) {
        Payment p = new CardPayment();
        p.pay(100); // Pay by card: 100
    }
}

Difference between abstract class and interface

📘 Definitions

abstract class is a class that cannot be created directly (using new), but it can contain both implemented methods and abstract (without body). It is used for common logic and state that subclasses should inherit.

interface is a contract (set of methods) that guarantees behavior, but does not contain state (before Java 8).

Modern interfaces (from Java 8+) can contain:

  • default methods (with implementation),
  • static methods,
  • private methods (from Java 9).

📗 Key Differences

Criterionabstract classinterface
InheritanceOnly one (Java supports single inheritance)Can implement multiple interfaces
State (fields)Can have regular fields (including protected/private)Only public static final (constants)
MethodsCan contain implemented and abstract methodsUntil Java 8 — only abstract; after — also default and static
ConstructorCan have a constructor (for subclasses)Cannot have a constructor
When to useWhen you need common state and partial implementationWhen you need to define behavior (contract) for different classes
Example applicationBase class Animal, from which Dog, Cat inheritInterface Comparable<T> or Serializable
// Abstract class — has common state
public abstract class Animal {
  protected String name;
  public Animal(String name) {
  this.name = name;
  }
  public abstract void makeSound();
  public void sleep() {
  System.out.println(name + " is sleeping...");
  }
}
// Interface — defines behavior
public interface Flyable {
  void fly();
}
// Class inheriting from abstract and implementing interface
public class Bird extends Animal implements Flyable {
  public Bird(String name) {
  super(name);
  }
  @Override
  public void makeSound() {
  System.out.println(name + " chirps");
  }
  @Override
  public void fly() {
  System.out.println(name + " flies away!");
  }
}

💡 Conclusion:

  • An abstract class describes what an object is.
  • An interface describes what an object can do.

SOLID Principles

SOLID is a set of 5 design principles that help create flexible, readable, and maintainable code. They are used in OOP and application architecture, especially in Spring, DDD, and clean architecture.


S — Single Responsibility Principle

Essence: A class should have only one reason to change, that is, it should perform only one task.

Bad:

class UserService {
  void registerUser(User user) { ... }  // registration
  void sendEmail(User user) { ... }  // and immediately send email
}

Good:

class UserService {
  void registerUser(User user) { ... }
}
class EmailService {
  void sendEmail(User user) { ... }
}

📍 Applied in Spring: Controller, Service, Repository — each is responsible for its layer.

O — Open/Closed Principle

Essence: Classes should be open for extension, but closed for modification. → It is better to add new classes than to change old ones.

Example:

interface Payment {
  void pay();
}
class CardPayment implements Payment { ... }
class PaypalPayment implements Payment { ... }
class ApplePay implements Payment { ... }
// Class uses an interface, not a specific implementation
class PaymentProcessor {
  void process(Payment payment) {
  payment.pay();
  }
}

📍 Applied when implementing dependencies (Dependency Injection).

L — Liskov Substitution Principle

Essence: Subclass objects should be replaceable with parent class objects without changing the program’s behavior.

Incorrect:

class Bird { void fly() { ... } }
class Penguin extends Bird {
  void fly() { throw new UnsupportedOperationException(); }
}

A penguin is a bird, but it doesn’t fly → violates the principle.

Solution: Extract the Flyable interface and use composition, not inheritance.

I — Interface Segregation Principle

Essence: Clients should not depend on methods they do not use. It is better to have many small interfaces than one large one.

Bad:

interface Worker {
  void work();
  void eat();
}
class Robot implements Worker {
  public void work() {}
  public void eat() {} // cannot eat
}

Good:

interface Workable { void work(); }
interface Eatable { void eat(); }
class Robot implements Workable { public void work() {} }
class Human implements Workable, Eatable { ... }

📍 Applied when designing APIs and microservices.

D — Dependency Inversion Principle

Essence: High-level modules should not depend on low-level modules — both should depend on abstractions.

Bad:

class MySQLDatabase { void connect() { ... } }
class UserRepository {
  private MySQLDatabase db = new MySQLDatabase(); // tight coupling
}

Good (through an interface):

interface Database { void connect(); }
class MySQLDatabase implements Database { ... }
class PostgreSQLDatabase implements Database { ... }
class UserRepository {
  private final Database db;
  public UserRepository(Database db) { this.db = db; }
}

📍 In Spring, this is implemented through Dependency Injection (@Autowired, @Service, @Repository).

🧩 Applying SOLID in a Real Java Architecture

PrincipleWhere it is found in Spring/Java
SLayer separation: Controller / Service / Repository
OExtensible interfaces, Strategy pattern
LCorrect hierarchy of DTO and Entity
ISmall interfaces: CrudRepository, JpaRepository
DDependency Injection (Spring IoC Container)