Master Core Java Programming From Scratch

Clear, interactive, and structured coding lessons designed for absolute beginners.

Encapsulation in Java

Learn how encapsulation protects object state by controlling access to fields and exposing behavior through methods.

What is Encapsulation?

Encapsulation is an object-oriented programming principle that combines data and the methods that operate on that data inside a class while controlling how the data is accessed.

In Java, encapsulation is commonly implemented by declaring fields as private and providing controlled access through methods such as getters and setters.

Basic Example

Java
class Student {

    private String name;
    private int age;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

}

Why Use private Fields?

A private field cannot be accessed directly from unrelated classes.

Java
class BankAccount {

    private double balance;

}

Code outside BankAccount cannot directly modify balance. The class can instead expose controlled operations for reading or updating it.

Getter Method

A getter returns the current value of a private field.

Java
public double getBalance() {

    return balance;

}

Setter Method

A setter changes the value of a private field. It can also validate the supplied value before modifying the object.

Java
public void setBalance(double balance) {

    if (balance >= 0) {

        this.balance = balance;

    }

}

Controlled Access

Encapsulation allows a class to decide exactly how its state can be accessed or modified.

Java
class BankAccount {

    private double balance;

    public double getBalance() {

        return balance;

    }

    public void deposit(double amount) {

        if (amount > 0) {

            balance += amount;

        }

    }

}

Instead of exposing balance directly, the class provides a meaningful deposit() operation.

Using an Encapsulated Object

Java
BankAccount account = new BankAccount();

account.deposit(5000);

System.out.println(
    account.getBalance()
);
Output
5000.0

Encapsulation with Validation

One of the major benefits of encapsulation is that validation rules can be placed inside the class.

Java
class Employee {

    private double salary;

    public void setSalary(double salary) {

        if (salary >= 0) {

            this.salary = salary;

        } else {

            throw new IllegalArgumentException(
                "Salary cannot be negative"
            );

        }

    }

    public double getSalary() {

        return salary;

    }

}

Encapsulation and Immutability

Encapsulation can also be used to create immutable objects. An immutable object cannot change its state after creation.

Java
final class User {

    private final String username;

    public User(String username) {

        this.username = username;

    }

    public String getUsername() {

        return username;

    }

}

The field is private and final, and there is no setter that can change it after construction.

Read-Only Properties

A class can expose a getter without providing a setter when callers should be able to read a value but not directly modify it.

Java
class Product {

    private final int productId;

    Product(int productId) {

        this.productId = productId;

    }

    public int getProductId() {

        return productId;

    }

}

Write-Only Style Access

In unusual cases, a class can expose an operation that accepts a value without providing a getter for the underlying state.

Java
class PasswordManager {

    private String password;

    public void setPassword(String password) {

        this.password = password;

    }

}

In real applications, sensitive data such as passwords should generally be handled using secure credential-management practices rather than exposing or storing plaintext values unnecessarily.

Direct Access vs Encapsulation

Direct Access Encapsulation
Fields may be publicly accessible. Fields are commonly private.
Little or no validation. Validation can be centralized.
Internal representation is exposed. Implementation details can be hidden.
Harder to change internal design safely. Public API can remain stable while internals change.

Data Hiding

Data hiding means restricting direct access to internal state so that external code interacts with an object through a controlled public interface.

Java
class Temperature {

    private double celsius;

    public double getCelsius() {

        return celsius;

    }

    public void setCelsius(double celsius) {

        if (celsius >= -273.15) {

            this.celsius = celsius;

        }

    }

}

Access Modifiers and Encapsulation

Modifier Access Level
private Accessible only within the declaring class.
No modifier Accessible within the same package.
protected Accessible within the same package and through inheritance under Java's protected-access rules.
public Accessible wherever the class/member is accessible.

Encapsulation vs Abstraction

Encapsulation Abstraction
Controls access to internal state and implementation. Focuses on exposing essential behavior while hiding unnecessary implementation details.
Commonly implemented using access modifiers. Commonly implemented using interfaces and abstract classes.
Protects and manages object state. Defines what an object can do.

Example: CIIT Student

Java
class CIITStudent {

    private String studentId;
    private double feesPaid;

    public CIITStudent(
        String studentId,
        double initialPayment
    ) {

        this.studentId = studentId;

        if (initialPayment >= 0) {

            this.feesPaid = initialPayment;

        }

    }

    public String getStudentId() {

        return studentId;

    }

    public double getFeesPaid() {

        return feesPaid;

    }

    public void payFees(double amount) {

        if (amount > 0) {

            feesPaid += amount;

        }

    }

    public boolean refundFees(double amount) {

        if (amount > 0 && amount <= feesPaid) {

            feesPaid -= amount;

            return true;

        }

        return false;

    }

}

public class Main {

    public static void main(String[] args) {

        CIITStudent student =
            new CIITStudent("CIIT001", 10000.0);

        System.out.println(
            "Student ID : " + student.getStudentId()
        );

        System.out.println(
            "Initial Fees Paid : Rs. " + student.getFeesPaid()
        );

        student.payFees(5000.0);

        System.out.println(
            "After Fee Payment : Rs. " + student.getFeesPaid()
        );

        boolean refundSuccessful =
            student.refundFees(2000.0);

        System.out.println(
            "Refund Successful : " + refundSuccessful
        );

        System.out.println(
            "Final Fees Paid : Rs. " + student.getFeesPaid()
        );

    }
}

Output

Output
Student ID : CIIT001
Initial Fees Paid : Rs. 10000.0
After Fee Payment : Rs. 15000.0
Refund Successful : true
Final Fees Paid : Rs. 13000.0

Explanation

  • studentId and feesPaid are private fields, so they cannot be accessed directly from outside the class.
  • The constructor initializes the student's ID and the initial fee payment.
  • getStudentId() and getFeesPaid() provide controlled read access to private data.
  • payFees() adds a valid payment to the student's total fees paid.
  • refundFees() validates the refund amount before reducing the fees paid and returns true when the refund succeeds.

Benefits of Encapsulation

  • Protects internal object state.
  • Allows validation of incoming data.
  • Reduces coupling between classes.
  • Makes implementation changes easier.
  • Improves maintainability.
  • Provides a clear public API for interacting with objects.

Encapsulation Best Practices

  • Prefer private fields for mutable object state.
  • Expose only the operations that callers actually need.
  • Validate state changes at the appropriate boundary.
  • Avoid blindly generating getters and setters for every field when a domain operation is more meaningful.
  • Keep object invariants inside the class whenever practical.

Interview Questions

Encapsulation combines data and behavior inside a class while controlling access to the object's internal state.

Private fields prevent unrelated code from directly modifying internal state and allow the class to control access.

A getter is a method used to retrieve the value of an encapsulated field.

A setter is a method that changes the value of an encapsulated field and can apply validation.
Summary

Encapsulation protects an object's internal state by controlling access through a well-defined interface. Private fields, getters, setters, validation, and domain-specific methods are common techniques for implementing encapsulation in Java.