Encapsulation is one of the four fundamental Object-Oriented Programming (OOP) concepts in Java. It refers to the bundling of data (variables) and methods (functions) that operate on that data into a single unit, usually a class. By doing this, it restricts direct access to some of the class's components, which is often called data hiding. Encapsulation ensures that the internal workings of an object are hidden from the outside world.

Key Points of Encapsulation:

  1. Private Variables: Variables are kept private to prevent direct access from outside the class.
  2. Public Methods: Methods are provided to access or modify the values of the private variables (Getters and Setters).
  3. Controlled Access: Only those methods that are marked public can be accessed from other classes.

Benefits of Encapsulation:

  • Data hiding: Prevents external classes from accessing sensitive data.
  • Better control over data: Using getters and setters allows validation and control over how data is modified.
  • Improved maintainability: As implementation details are hidden, the internal structure of the class can be changed without affecting other parts of the code.
  • Increased flexibility: The internal representation can be changed without modifying the external interface.

Example 1: Basic Encapsulation Example

class Person {
    // Private data members
    private String name;
    private int age;
 
    // Getter for name
    public String getName() {
        return name;
    }
 
    // Setter for name
    public void setName(String name) {
        this.name = name;
    }
 
    // Getter for age
    public int getAge() {
        return age;
    }
 
    // Setter for age
    public void setAge(int age) {
        if (age > 0) {  // Simple validation
            this.age = age;
        } else {
            System.out.println("Age must be positive.");
        }
    }
}
 
public class EncapsulationExample1 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("John");
        person.setAge(25);
        
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
    }
}

Example 2: Encapsulation with Validation

class BankAccount {
    private double balance;
 
    // Getter for balance
    public double getBalance() {
        return balance;
    }
 
    // Setter for balance with validation
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        } else {
            System.out.println("Deposit amount must be positive.");
        }
    }
 
    // Withdraw with validation
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        } else {
            System.out.println("Invalid withdrawal amount.");
        }
    }
}
 
public class EncapsulationExample2 {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(100);
        account.withdraw(30);
        
        System.out.println("Remaining Balance: " + account.getBalance());
    }
}

Example 3: Read-Only Class (Encapsulation with only Getters)

class ReadOnly {
    private String data;
 
    public ReadOnly(String data) {
        this.data = data;
    }
 
    // Only getter, no setter
    public String getData() {
        return data;
    }
}
 
public class EncapsulationExample3 {
    public static void main(String[] args) {
        ReadOnly obj = new ReadOnly("Read-Only Data");
        System.out.println("Data: " + obj.getData());
    }
}

Example 4: Write-Only Class (Encapsulation with only Setters)

class WriteOnly {
    private String secretData;
 
    // Setter only, no getter
    public void setSecretData(String secretData) {
        this.secretData = secretData;
    }
}
 
public class EncapsulationExample4 {
    public static void main(String[] args) {
        WriteOnly obj = new WriteOnly();
        obj.setSecretData("Top Secret");
        // No way to access the data
    }
}

Example 5: Encapsulation with Array Example

class Student {
    private String[] subjects;
 
    // Getter
    public String[] getSubjects() {
        return subjects.clone();  // Return a copy of the array to avoid modification
    }
 
    // Setter
    public void setSubjects(String[] subjects) {
        this.subjects = subjects.clone();  // Store a copy to prevent external modification
    }
}
 
public class EncapsulationExample5 {
    public static void main(String[] args) {
        Student student = new Student();
        student.setSubjects(new String[]{"Math", "Science", "English"});
        
        for (String subject : student.getSubjects()) {
            System.out.println(subject);
        }
    }
}

Example 6: Encapsulation with Collection Example

import java.util.ArrayList;
import java.util.List;
 
class Department {
    private List<String> employees = new ArrayList<>();
 
    // Getter
    public List<String> getEmployees() {
        return new ArrayList<>(employees);  // Return a copy of the list
    }
 
    // Setter
    public void addEmployee(String employee) {
        employees.add(employee);
    }
}
 
public class EncapsulationExample6 {
    public static void main(String[] args) {
        Department department = new Department();
        department.addEmployee("Alice");
        department.addEmployee("Bob");
 
        for (String employee : department.getEmployees()) {
            System.out.println(employee);
        }
    }
}

Example 7: Encapsulation with Calculated Fields

class Rectangle {
    private double width;
    private double height;
 
    // Setters
    public void setWidth(double width) {
        if (width > 0) {
            this.width = width;
        }
    }
 
    public void setHeight(double height) {
        if (height > 0) {
            this.height = height;
        }
    }
 
    // Getters for calculated fields
    public double getArea() {
        return width * height;
    }
 
    public double getPerimeter() {
        return 2 * (width + height);
    }
}
 
public class EncapsulationExample7 {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setWidth(5);
        rectangle.setHeight(10);
 
        System.out.println("Area: " + rectangle.getArea());
        System.out.println("Perimeter: " + rectangle.getPerimeter());
    }
}

Example 8: Encapsulation with Enum

class Employee {
    private String name;
    private Role role;
 
    public enum Role { MANAGER, DEVELOPER, TESTER }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Role getRole() {
        return role;
    }
 
    public void setRole(Role role) {
        this.role = role;
    }
}
 
public class EncapsulationExample8 {
    public static void main(String[] args) {
        Employee emp = new Employee();
        emp.setName("John");
        emp.setRole(Employee.Role.DEVELOPER);
 
        System.out.println("Name: " + emp.getName());
        System.out.println("Role: " + emp.getRole());
    }
}

Example 9: Encapsulation with Static Fields

class Counter {
    private static int count = 0;
 
    public static void increment() {
        count++;
    }
 
    public static int getCount() {
        return count;
    }
}
 
public class EncapsulationExample9 {
    public static void main(String[] args) {
        Counter.increment();
        Counter.increment();
        
        System.out.println("Count: " + Counter.getCount());
    }
}

Example 10: Encapsulation with Singleton Pattern

class Singleton {
    private static Singleton instance;
    private int value;
 
    private Singleton() { }
 
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
 
    public int getValue() {
        return value;
    }
 
    public void setValue(int value) {
        this.value = value;
    }
}
 
public class EncapsulationExample10 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.setValue(100);
        
        System.out.println("Singleton Value: " + singleton.getValue());
    }
}

Example 11: Encapsulation with Static Block

class Config {
    private static String url;
 
    static {
        url = "https://example.com";
    }
 
    public static String getUrl() {
        return url;
    }
}
 
public class EncapsulationExample11 {
    public static void main(String[] args) {
        System.out.println("Config URL: " + Config.getUrl());
    }
}

Example 12: Encapsulation with Multiple Classes

class Address {
    private String city;
    private String country;
 
    public String getCity() {
        return city;
    }
 
    public void setCity(String city) {
        this.city = city;
    }
 
    public String getCountry() {
        return country;
    }
 
    public void setCountry(String country) {
        this.country = country;
    }
}
 
class Person {
    private String name;
    private Address address;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name =
 
 name;
    }
 
    public Address getAddress() {
        return address;
    }
 
    public void setAddress(Address address) {
        this.address = address;
    }
}
 
public class EncapsulationExample12 {
    public static void main(String[] args) {
        Address address = new Address();
        address.setCity("New York");
        address.setCountry("USA");
 
        Person person = new Person();
        person.setName("John");
        person.setAddress(address);
 
        System.out.println(person.getName() + " lives in " + person.getAddress().getCity() + ", " + person.getAddress().getCountry());
    }
}

Example 13: Encapsulation with Interface

interface Shape {
    double getArea();
}
 
class Circle implements Shape {
    private double radius;
 
    public void setRadius(double radius) {
        this.radius = radius;
    }
 
    public double getArea() {
        return Math.PI * radius * radius;
    }
}
 
public class EncapsulationExample13 {
    public static void main(String[] args) {
        Circle circle = new Circle();
        circle.setRadius(5);
        
        System.out.println("Circle Area: " + circle.getArea());
    }
}

Example 14: Encapsulation with Method Overloading

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
 
    public double add(double a, double b) {
        return a + b;
    }
}
 
public class EncapsulationExample14 {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("Sum (int): " + calc.add(5, 10));
        System.out.println("Sum (double): " + calc.add(5.5, 10.5));
    }
}

Example 15: Encapsulation with Immutable Class

final class ImmutableClass {
    private final String name;
    private final int age;
 
    public ImmutableClass(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public int getAge() {
        return age;
    }
}
 
public class EncapsulationExample15 {
    public static void main(String[] args) {
        ImmutableClass obj = new ImmutableClass("John", 30);
        System.out.println("Name: " + obj.getName());
        System.out.println("Age: " + obj.getAge());
    }
}

Happy coding...!