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
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.
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.
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.
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.
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
BankAccount account = new BankAccount();
account.deposit(5000);
System.out.println(
account.getBalance()
);
5000.0
Encapsulation with Validation
One of the major benefits of encapsulation is that validation rules can be placed inside the class.
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.
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.
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.
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.
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
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
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
-
studentIdandfeesPaidare 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()andgetFeesPaid()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 returnstruewhen 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
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.