Java Encapsulation - Data Hiding and Controlled Access
⏱️ 22 min read • Beginner Level • Lesson 55
Lesson 55 of 124 of Java Tutorial
You have completed this lesson
Completed on .
You can revise this lesson or continue to the next topic.
Encapsulation in Java combines data and behavior inside a class while protecting internal state from uncontrolled outside access.
A well-encapsulated class exposes a small, meaningful API and keeps implementation details restricted. This allows the class to validate data, protect its rules, and change its internal representation without forcing every caller to change.
What is Encapsulation in Java?
Encapsulation is an object-oriented programming principle in which a class keeps related state and behavior together and controls how outside code interacts with that state.
Simple Meaning:
Keep data protected inside a class and allow access only through operations designed by that class.
Encapsulation usually involves:
Declaring implementation fields private.
Creating constructors that establish valid initial state.
Providing meaningful methods for allowed operations.
Validating data before changing internal state.
Avoiding unnecessary getters, setters, and public mutable fields.
Real-World Analogy
A bank account is a useful analogy. Customers cannot directly edit the balance stored in the bank's system. Instead, they use controlled operations such as deposit, withdraw, and check balance.
Protected Data
The account balance is internal state.
Controlled Operations
Deposit and withdrawal methods control changes.
Business Rules
Invalid transactions can be rejected before state changes.
Reduce coupling: outside code depends on behavior rather than fields.
Improve maintainability: internal representation can change behind a stable API.
Communicate intent: methods such as deposit() clearly describe allowed actions.
Improve testing: business rules are concentrated in predictable operations.
How Encapsulation Works
Outside Code Requests an operation
↓
Public or Accessible Method Checks input and business rules
↓
Private State Changes only when rules are satisfied
↓
Consistent Object The object remains valid
Basic Encapsulation Example
Student.java
CopyTryDownload
class Student {
private String name;
private int marks;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setMarks(int marks) {
if (marks >= 0 && marks <= 100) {
this.marks = marks;
}
}
public int getMarks() {
return marks;
}
public static void main(String[] args) {
Student student = new Student();
student.setName("Ayan");
student.setMarks(88);
System.out.println(student.getName());
System.out.println(student.getMarks());
}
}
Output:
Ayan
88
Explanation
The fields are private, so ordinary outside code cannot change them directly.
The setter controls how values enter the object.
setMarks() accepts only values from 0 to 100.
The getters provide controlled read access.
Encapsulation with Validation
Silently ignoring invalid data can hide programming mistakes. A stronger design often reports invalid input immediately.
BankAccount.java
CopyTryDownload
class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException(
"Deposit must be greater than zero");
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0 || amount > balance) {
throw new IllegalArgumentException(
"Invalid withdrawal amount");
}
balance -= amount;
}
public double getBalance() {
return balance;
}
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(2000);
account.withdraw(500);
System.out.println(account.getBalance());
}
}
Output:
1500.0
Explanation
The deposit() and withdraw() methods encapsulate the business rules.
Invalid data (like a negative deposit or withdrawing more than the balance) is rejected immediately with an exception.
The balance field remains completely hidden and safe from bad data.
Key Point:deposit() and withdraw() describe business actions more clearly than a general setBalance() method.
Getters and Setters
Getters and setters are common tools, but they are not the definition of encapsulation. A method should be added only when that operation belongs in the class's public or internal API.
Method Type
Purpose
Example
Getter
Provides controlled read access.
getBalance()
Setter
Provides controlled write access.
setEmail(value)
Boolean getter
Reports a boolean state.
isActive()
Domain method
Performs a meaningful action.
withdraw(amount)
Avoid automatic exposure: If a value should not be changed independently, do not add a public setter merely because the field is private.
Read-only and Write-only Designs
Read-only API
Provides a getter or observation method but no public operation for changing the value.
class Product {
private final String code;
Product(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
Write-only API
Accepts a value through an operation but does not expose the stored value directly.
A domain-specific method communicates purpose and keeps related validation together. For example, changePassword(), enroll(), and deposit() are often better than generic setters.
Weak API
Stronger API
Why?
setBalance(5000)
deposit(5000)
Clearly represents a transaction.
setStatus("PAID")
markAsPaid()
The transition can enforce rules.
setPassword(value)
changePassword(old, next)
Can verify the old password and validate the new one.
Encapsulation and Defensive Copying
A private field is not fully protected if a getter returns the same mutable object reference. Outside code could modify the internal object without going through class rules.
Course.java
CopyTryDownload
import java.util.ArrayList;
import java.util.List;
class Course {
private final List<String> students = new ArrayList<>();
public void enroll(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Name is required");
}
students.add(name);
}
public List<String> getStudents() {
return List.copyOf(students);
}
public static void main(String[] args) {
Course course = new Course();
course.enroll("Ayan");
course.enroll("Sarah");
System.out.println(course.getStudents());
}
}
Output:
[Ayan, Sarah]
Why List.copyOf()?
It returns an unmodifiable snapshot, so callers cannot use the returned list to directly add or remove elements from the course's internal list.
Encapsulation vs Immutability
Feature
Encapsulation
Immutability
Main goal
Control access to state and behavior.
Prevent observable state changes after creation.
Can state change?
Yes, through controlled methods.
No, after construction.
Typical tools
Private fields, validation, focused methods.
Final fields, constructor initialization, no mutators, defensive copies.
Relationship
Can be mutable or immutable.
Uses strong encapsulation to protect state.
Interview Point: Every well-designed immutable class is strongly encapsulated, but not every encapsulated class is immutable.
Encapsulation vs Abstraction
Feature
Encapsulation
Abstraction
Focus
Protecting state and implementation boundaries.
Showing essential capabilities while hiding unnecessary detail.
Encapsulation does not automatically mean immutability.
A small, meaningful API reduces coupling and improves maintainability.
Interview Questions ⭐
Encapsulation is the practice of combining data and the methods that operate on that data inside a class while restricting direct access to internal state.
It is commonly achieved by declaring fields private and exposing carefully designed public, protected, or package-private methods for controlled interaction.
Data hiding means preventing outside code from directly accessing or modifying a class's internal data.
Private fields prevent uncontrolled access, let the class validate changes, and allow the internal representation to change without breaking callers.
A getter is a method that returns a field value or a derived value when read access is appropriate.
A setter is a method that updates a field, often after validating the supplied value.
No. Encapsulation requires controlled access, not automatic getters and setters for every field. A class should expose only the operations its users need.
Encapsulation improves data protection, validation, maintainability, flexibility, testability, and separation between a class's public API and internal implementation.
A read-only design exposes data for reading but does not provide an outside operation that changes it after initialization.
A write-only design permits controlled updates but does not expose a getter for reading the stored value directly.
Yes. A setter can reject invalid values, normalize input, enforce business rules, or throw an exception.
No. Encapsulation controls access, while immutability means an object's observable state cannot change after construction. An encapsulated class may still be mutable.
Encapsulation protects state and controls access to implementation details. Abstraction presents essential behavior while hiding unnecessary implementation complexity.
Data hiding is one result of restricting direct access. Encapsulation is broader: it packages state and behavior together and defines controlled operations around that state.
Yes. Public methods form the class's usable API while private members keep internal details protected.
Yes. The appropriate access level depends on whether the operation is intended for the same package, subclasses, or all callers.
They let callers bypass validation, create tight coupling to internal representation, and make future changes harder.
Yes. Constructors can validate initial values and ensure that every object begins in a valid state.
Callers depend on stable methods instead of internal fields, so implementation details can often change without forcing changes in calling code.
Returning a mutable internal object directly may expose internal state. Consider an immutable view, defensive copy, or a more focused operation.
Defensive copying creates a separate copy of mutable data when receiving or returning it, preventing outside code from changing internal state through a shared reference.
No. Add a setter only when callers should be allowed to change that value. Some fields should be initialized once or changed only through domain-specific methods.
It is an operation that expresses intent and protects rules, such as deposit(amount) or changePassword(value), instead of exposing unrestricted field assignment.
Records provide concise data carriers with private final component fields and public accessors, but their components may still refer to mutable objects that require careful handling.
Keep implementation details as restricted as practical, establish valid state in constructors, expose meaningful operations, validate changes, and avoid unnecessary getters and setters.
Reflection and other privileged mechanisms may access implementation details in some environments, but ordinary application design should still enforce encapsulation through access control and APIs.
The Java compiler reports an access error because private members are not directly accessible outside their declaring class.
Access modifiers define visibility boundaries. Encapsulation uses those boundaries, especially private access, to protect internal state and expose a controlled API.
Yes. A method can return a calculated result without revealing or exposing the fields used to produce it.
No. It also improves design clarity, validation, maintainability, reuse, testing, and the ability to change implementation details.
Q1. Which is the best general encapsulation guideline?
Q2. Can an encapsulated class have public methods?
Q3. What does encapsulation improve by separating API from implementation?
Q4. Can a method return a calculated value without exposing internal fields?
Q5. Which method name best expresses domain intent?
Q6. What is data hiding?
Q7. Which pair best demonstrates controlled access?
Q8. How can a constructor support encapsulation?
Q9. What is the output of the validated BankAccount example?
class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public double getBalance() {
return balance;
}
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(-100);
account.deposit(500);
System.out.println(account.getBalance());
}
}
Q10. What is the primary purpose of a getter method?
Q11. What is the difference between encapsulation and abstraction?
Q12. Should a salary field always have a public setter?
Q13. Which approach protects a mutable list most effectively when returning it?
Q14. Which statement about public mutable fields is correct?
Q15. Which design is more strongly encapsulated?
Q16. What is the primary purpose of a setter method?
Q17. Does encapsulation automatically make an object immutable?
Q18. What happens when another class directly accesses a private field?
Q19. Are getters and setters required for every private field?
Q20. What is encapsulation in Java?
Q21. What is a read-only design?
Q22. Which is a benefit of encapsulation?
Q23. Which access modifier is most commonly used for encapsulated fields?
Q24. What happens when this code is compiled?
class Student {
private int marks = 90;
}
class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.marks);
}
}
Q25. What is defensive copying used for?
Great job! Continue learning Java OOP step by step.
Discussion
Ask questions, share suggestions, or discuss this lesson.
Discussion
Ask questions, share suggestions, or discuss this lesson.