Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
34: Encapsulation and Access Modifiers
Think about your car. To make it go faster, you press the gas pedal. To stop, you hit the brakes. You don't open the hood while driving and manually spray fuel into the cylinders or pull the brake cables by hand. Why? Because that would be chaotic, dangerous, and you'd probably break something. The car "encapsulates" the engine's complexity and only gives you a few safe, public interfaces—the pedals and the wheel—to interact with it.
In Java, encapsulation is exactly that. It's the practice of bundling the data (fields) and the methods that operate on that data into a single unit, and then hiding the internal state from the outside world. Here is how that maps to our code:
- The Engine (Private Fields): These are the internal variables that the rest of the program shouldn't touch directly.
- The Pedals (Public Methods): These are the "getters" and "setters" that let other classes interact with the data in a controlled way.
- The Car's Computer (Validation): This is the logic inside your methods that prevents someone from, say, setting a car's speed to -500 mph.
Keeping the Guts Hidden
If you leave your class variables as public, any other class in your entire project can reach in and change them. I've seen this lead to some nightmare bugs where a variable was changed in a distant part of the codebase, and it took hours to find the culprit. To prevent this, we use the private modifier.
public class PlayerCharacter {
private String name;
private int health = 100;
private int gold = 0;
public PlayerCharacter(String name) {
this.name = name;
}
}
Now, if you try to do player.health = -50; from another class, Java will throw a compiler error. You've successfully locked the door. But now we have a problem: how do we actually use this data?
Controlled Access via Getters and Setters
We don't just make everything public again. Instead, we create methods that act as gatekeepers. I call these "the API of the class." This allows you to add validation logic. If you just let someone set the health directly, they could set it to a negative number, which makes no sense in a game. By using a setter, you control the rules.
public class PlayerCharacter {
private int health = 100;
// Getter: Let people see the health, but not change it directly
public int getHealth() {
return health;
}
// Setter: Let people change the health, but ONLY if it's valid
public void setHealth(int newHealth) {
if (newHealth < 0) {
this.health = 0; // Clamp to zero
} else if (newHealth > 100) {
this.health = 100; // Cap at max health
} else {
this.health = newHealth;
}
}
}
See what happened there? The PlayerCharacter class now owns its own state. It decides what "valid" health looks like. The rest of your program doesn't need to know the rules; it just calls setHealth() and trusts the object to handle it correctly.
The Middle Ground: Protected and Default
Most of the time, you'll stick to public and private. But there are two other modifiers you'll run into as you start building larger systems. First is the "default" (or package-private) access. If you don't put any modifier at all, the variable is visible to any other class in the same folder (package), but hidden from everything else. It's a way of saying, "This is a secret from the world, but my teammates in this package can help me manage it."
Then there is protected. This is a bit more niche—it works like the default modifier, but it also allows subclasses (children) to access the field, even if they are in a different package. Use this sparingly. In my experience, if you find yourself using protected a lot, you might be making your class hierarchy too complex.
📋 Practical Task
Build a Secure Digital Wallet
Your task is to create a DigitalWallet class that prevents "illegal" financial states. A wallet should never have a negative balance, and you should be able to track how many transactions have occurred.
Requirements:
- Create a private double field called
balanceand a private int calledtransactionCount. - Provide a public getter for the
balance. - Provide a public method
deposit(double amount). This method should only add money if the amount is positive. If it is, increment thetransactionCount. - Provide a public method
withdraw(double amount). This method should only deduct money if the amount is positive AND if there are enough funds in the balance. If successful, increment thetransactionCount. - Provide a public getter for
transactionCount, but do not provide a setter for it (it should only be changeable via deposits and withdrawals).
Test your class by trying to withdraw more money than you have and verifying that the balance does not go negative and the transaction count does not increase.
There are no comments for now.