Hello! In this lesson, we're revisiting Encapsulation, Private Attributes, and Private Methods in Object-Oriented Programming (OOP). Think of encapsulation as an invisible fence that safeguards your garden (the class) from outside interference, keeping data and methods secure inside. Within this garden, certain elements (Private Attributes and Methods) are only accessible to the gardener, providing an extra layer of protection and ensuring data integrity in your classes!
Encapsulation is a fundamental concept in Object-Oriented Programming (OOP) that involves bundling data (attributes) and methods (functions) that operate on the data into a single unit, called a class. This concept also limits direct access to some of an object's components, preventing accidental interference and misuse of the data. For instance, in a multiplayer game, you might create a Player
class that encapsulates data (health
, armor
, stamina
) and methods (receiveDamage
, shieldHit
, restoreHealth
).
Java1class Player { 2 private int health; 3 private int armor; 4 private int stamina; 5 6 public Player(int health, int armor, int stamina) { 7 this.health = health; 8 this.armor = armor; 9 this.stamina = stamina; 10 } 11 12 public void receiveDamage(int damage) { 13 health -= damage; // Reduce health 14 } 15 16 public void shieldHit(int armorDamage) { 17 armor -= armorDamage; // Decrease armor 18 } 19 20 public void restoreHealth(int healthIncrease) { 21 health += healthIncrease; // Restore health 22 } 23 24 public void displayStatus() { 25 System.out.println("Health: " + health + ", Armor: " + armor + ", Stamina: " + stamina); 26 } 27} 28 29public class Solution { 30 public static void main(String[] args) { 31 Player player = new Player(100, 50, 77); 32 player.displayStatus(); // Outputs: Health: 100, Armor: 50, Stamina: 77 33 } 34}
In Java, player
is an instance of the Player
class on which you can call methods.
In Java, the private
keyword is used to designate attributes and methods as accessible only within their own class. This ensures the integrity of an object by restricting direct external access and interference.
Private Attributes, such as balance
in a BankAccount
class, ensure the integrity and security of data by restricting modifications to the methods within the class and disallowing direct external access.
Java1class BankAccount { 2 private double balance; // Declare a private field 3 4 // Constructor to initialize the balance 5 public BankAccount(double balance) { 6 this.balance = balance; // Initialize the private attribute 7 } 8 9 // Method to deposit money into the account 10 public void deposit(double amount) { 11 balance += amount; // Deposit money 12 } 13 14 // Getter for balance 15 public double getBalance() { 16 return balance; // A public method to access balance 17 } 18 19 // Optional setter for balance with validation 20 public void setBalance(double balance) { 21 if (balance >= 0) { 22 this.balance = balance; 23 } else { 24 System.out.println("Balance cannot be negative."); 25 } 26 } 27} 28 29public class Solution { 30 public static void main(String[] args) { 31 BankAccount bankAccount = new BankAccount(100); // Initialize account with balance of 100 32 bankAccount.setBalance(200); // Works: sets balance to 200 33 System.out.println(bankAccount.getBalance()); // Works: logs 200 34 bankAccount.setBalance(-50); // Logs: "Balance cannot be negative." 35 } 36}
Like private attributes, private methods are accessible only within their class. They are useful for hiding complex implementation details or helper functions from outside access. Here's an example:
Java1class BankAccount { 2 private double balance; // Declare a private field 3 4 public BankAccount(double balance) { 5 this.balance = balance; // Initialize the private attribute 6 } 7 8 // Private method to calculate interest 9 private void addInterest(double interestRate) { 10 balance += balance * interestRate; // Calculation of interest 11 } 12 13 // Public method calling the private method 14 public void addYearlyInterest() { 15 addInterest(0.02); // Adds 2% interest 16 } 17 18 // Getter for balance 19 public double getBalance() { 20 return balance; 21 } 22} 23 24public class Solution { 25 public static void main(String[] args) { 26 BankAccount bankAccount = new BankAccount(100); // Initialize account with balance of 100 27 bankAccount.addYearlyInterest(); // Works, calling a public method 28 System.out.println(bankAccount.getBalance()); // Logs the updated balance 29 // bankAccount.addInterest(0.1); // Error: can't call a private method from outside 30 } 31}
Here, addYearlyInterest()
is a public method that calls the private method addInterest()
.
In Java, getters provide a method to access private attributes indirectly while maintaining control over how values are retrieved, adhering to the encapsulation principle. Here's how you can define a getter method for a private attribute:
Java1class BankAccount { 2 private double balance; // Declare a private field 3 4 // Constructor to initialize the balance 5 public BankAccount(double balance) { 6 this.balance = balance; // Initialize the private attribute 7 } 8 9 // Getter for balance 10 public double getBalance() { 11 return balance; 12 } 13 14 // Method to deposit money into the account 15 public void deposit(double amount) { 16 balance += amount; // Deposit money 17 } 18} 19 20public class Solution { 21 public static void main(String[] args) { 22 BankAccount bankAccount = new BankAccount(100); // Initialize account with balance of 100 23 System.out.println(bankAccount.getBalance()); // Works: logs 100 24 } 25}
In this example, the getBalance
method provides a controlled way to access the private balance
attribute.
Java setters allow the modification of the value of a private attribute through a controlled interface. Here’s how you can define a setter method for a private attribute:
Java1class BankAccount { 2 private double balance; // Declare a private field 3 4 // Constructor to initialize the balance 5 public BankAccount(double balance) { 6 this.balance = balance; // Initialize the private attribute 7 } 8 9 // Setter for balance with validation 10 public void setBalance(double balance) { 11 if (balance >= 0) { 12 this.balance = balance; 13 } else { 14 System.out.println("Balance cannot be negative."); 15 } 16 } 17 18 // Getter for balance 19 public double getBalance() { 20 return balance; 21 } 22 23 // Method to deposit money into the account 24 public void deposit(double amount) { 25 this.balance += amount; // Deposit money 26 } 27} 28 29public class Solution { 30 public static void main(String[] args) { 31 BankAccount bankAccount = new BankAccount(100); // Initialize account with balance of 100 32 bankAccount.setBalance(200); // Works: sets balance to 200 33 System.out.println(bankAccount.getBalance()); // Works: logs 200 34 bankAccount.setBalance(-50); // Logs: "Balance cannot be negative." 35 } 36}
In this example, the setBalance
method ensures that the balance is only set to non-negative values, adding a layer of validation.
By using getters and setters, you can incorporate logic for validating or processing data, making your class more flexible and secure while adhering to encapsulation principles.
Great job refreshing your understanding of encapsulation, private attributes, and private methods concepts in Java! Correctly understanding and applying these foundational principles of OOP make your code concise, robust, and secure.
Coming up next is hands-on practice. Keep up the good work — exciting exercises in Java are just around the corner!