What is encapsulation and how is it achieved?
Encapsulation is one of the four fundamental pillars of Object-Oriented Programming (OOP), alongside inheritance, polymorphism, and abstraction. It plays a crucial role in improving code maintainability, security, and reusability.
What is Encapsulation?
Encapsulation is the mechanism of wrapping the data (variables) and code acting on the data (methods) together as a single unit. It involves bundling the data with the methods that operate on that data, and restricting direct access to some of an object's components. This means that the internal representation of an object is hidden from the outside. Only the object itself can access and modify its internal state through a public interface (methods).
How is Encapsulation Achieved in Java?
In Java, encapsulation is primarily achieved through the use of access modifiers and getter/setter methods. By making the instance variables private, their direct access from outside the class is prevented. Public getter methods are then provided to read the values of these variables, and public setter methods are provided to modify them. This approach allows controlled access to the internal state of an object.
- Declare the instance variables of a class as private. This makes them inaccessible directly from outside the class.
- Provide public 'getter' methods (also known as accessors) to allow read-only access to the private variables.
- Provide public 'setter' methods (also known as mutators) to allow controlled modification of the private variables. These methods can include validation logic to ensure data integrity.
Example
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
setBalance(initialBalance); // Use setter for initial validation
}
// Getter for accountNumber
public String getAccountNumber() {
return accountNumber;
}
// Getter for balance
public double getBalance() {
return balance;
}
// Setter for balance with validation
public void setBalance(double newBalance) {
if (newBalance >= 0) {
this.balance = newBalance;
} else {
System.out.println("Balance cannot be negative.");
}
}
public void deposit(double amount) {
if (amount > 0) {
this.balance += amount;
} else {
System.out.println("Deposit amount must be positive.");
}
}
public void withdraw(double amount) {
if (amount > 0 && this.balance >= amount) {
this.balance -= amount;
} else if (amount <= 0) {
System.out.println("Withdrawal amount must be positive.");
} else {
System.out.println("Insufficient balance.");
}
}
}
public class BankApp {
public static void main(String[] args) {
BankAccount myAccount = new BankAccount("12345", 1000.0);
System.out.println("Account Number: " + myAccount.getAccountNumber());
System.out.println("Initial Balance: " + myAccount.getBalance());
myAccount.deposit(200.0);
System.out.println("Balance after deposit: " + myAccount.getBalance());
myAccount.withdraw(300.0);
System.out.println("Balance after withdrawal: " + myAccount.getBalance());
myAccount.setBalance(-50.0); // Attempt to set invalid balance
System.out.println("Balance after invalid set: " + myAccount.getBalance());
}
}
Benefits of Encapsulation
- Data Hiding: It restricts direct access to data, preventing unauthorized or incorrect modifications.
- Flexibility and Maintainability: The internal implementation of a class can be changed without affecting the code that uses the class, as long as the public interface (getter/setter methods) remains consistent.
- Modularity: Encapsulated classes are easier to understand, test, and debug as they have well-defined interfaces and responsibilities.
- Increased Security: By controlling how data is accessed and modified, encapsulation helps in preventing data corruption and ensuring data integrity through validation logic in setter methods.
- Reusability: Well-encapsulated components are easier to reuse in different parts of an application or in other projects.