Object Oriented Programming with JavaUnit 1011 min read
Packages, Access Modifiers & Java Encapsulation
Unit 10 of Object Oriented Programming with Java covers how to organize code into packages, control access to classes/methods using modifiers (public, private, protected, default), and enforce encapsulation—key concepts for modular, secure Java programs.
TAKEAWAYS:
- Packages group related classes/interfaces into namespaces (e.g.,
java.utilfor collections) and avoid naming conflicts. - Access modifiers (
public,protected,private,default) control visibility of members across packages and subclasses. - Encapsulation hides internal state (via
privatefields) and exposes controlled access (viapublicgetters/setters). importstatements bring classes from other packages into scope without fully qualifying names.- Default package (unnamed) is discouraged in production code but used in small programs.
packagedeclaration must be the first line in a Java file and matches the directory structure.
Core Concepts: Packages in Java
What is a Package?
A package is a mechanism to group related classes and interfaces under a single namespace. It helps in:
- Code organization: Logical grouping (e.g.,
java.utilfor utility classes). - Avoiding naming conflicts: Classes with the same name can coexist in different packages.
- Access control: Restrict visibility of classes/methods to specific packages.
How to Define a Package
- Declare the package at the top of the file:
package com.example.myapp; - Directory structure must match the package name:
src/ └── com/ └── example/ └── myapp/ └── MyClass.java - Compile and run:
javac com/example/myapp/MyClass.java java com.example.myapp.MyClass
Default Package (No Package)
- If no
packagedeclaration is given, the class is in the default package. - Disadvantages:
- Cannot be imported by other packages.
- Risk of naming conflicts in larger projects.
- Use case: Small programs or quick prototypes.
Importing Classes
Use import to bring classes from other packages into scope:
import java.util.ArrayList; // Specific class
import java.util.*; // All classes in the package
- Static imports: Import static members (e.g.,
Math.PI):import static java.lang.Math.PI;
classDiagram
class PackageA {
+public void show()
}
class PackageB {
+public void usePackageA()
}
PackageA --> PackageB : "import PackageA"
note for PackageA "Declared in com.example.packagea"
note for PackageB "Declared in com.example.packageb"Real-World Example: Khalti’s Payment System
Khalti organizes its Java backend into packages like:
com.khalti.core: Core payment logic (e.g.,Transaction.java).com.khalti.security: Encryption and validation (e.g.,TokenValidator.java).com.khalti.api: REST endpoints (e.g.,PaymentController.java).
Why?
- Separation of concerns: Security logic is isolated from business logic.
- Access control:
TokenValidatormethods areprivateto prevent misuse. - Reusability:
Transactionclass can be imported by other modules.
Access Modifiers: Controlling Visibility
Access modifiers determine where a class/method/field can be accessed. There are four types:
| Modifier | Class | Package | Subclass (same package) | Subclass (different package) | Anywhere |
|---|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ✅ | ❌ |
default |
✅ | ✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ | ❌ |
1. public
- Accessible from anywhere.
- Used for APIs, library classes, or shared utilities.
- Example:
public class Calculator { public int add(int a, int b) { return a + b; } }
2. protected
- Accessible within the same package or by subclasses (even in different packages).
- Used for inherited methods/fields that should not be exposed publicly.
- Example:
class Vehicle { protected String model; } class Car extends Vehicle { public void setModel(String m) { model = m; } // Allowed }
3. default (Package-Private)
- Accessible only within the same package.
- Achieved by omitting any modifier.
- Example:
class DatabaseHelper { void connect() { System.out.println("Connected!"); } // Default access }
4. private
- Accessible only within the same class.
- Used for encapsulation (hiding internal state).
- Example:
class BankAccount { private double balance; public void deposit(double amount) { balance += amount; } }
classDiagram
class Account {
-private String pin
+public void setPin(String pin)
+public String getPin()
}
class Admin {
+public void resetPin(Account acc, String newPin)
}
Account --> Admin : "Admin can access setPin()\nbut not pin directly"
note for Account "Encapsulation: pin is hidden\nbut modified via public methods"Real-World Example: Ncell’s SIM Activation System
Ncell’s Java backend uses private fields for sensitive data like:
class SIMCard {
private String iccid; // Hidden from outside
private boolean isActive; // Modified via controlled methods
public void activate() {
if (isValidICCID(iccid)) { // Internal check
isActive = true;
}
}
private boolean isValidICCID(String iccid) { ... }
}
Why?
- Prevents direct tampering with
isActiveoriccid. - Forces validation through
publicmethods likeactivate().
Encapsulation: The Power of private + Getters/Setters
Encapsulation bundles data (fields) and methods (getters/setters) into a single unit (class) while restricting direct access.
How It Works
- Declare fields as
private. - Provide
publicgetter/setter methods to control access. - Add validation logic in setters.
Example: Daraz Order Queue System
class Order {
private String orderId;
private int quantity;
private double pricePerItem;
// Constructor
public Order(String orderId, int quantity, double pricePerItem) {
this.orderId = orderId;
this.setQuantity(quantity); // Uses setter for validation
this.setPricePerItem(pricePerItem);
}
// Getter (read-only)
public String getOrderId() { return orderId; }
// Setter with validation
public void setQuantity(int quantity) {
if (quantity <= 0) throw new IllegalArgumentException("Quantity must be > 0");
this.quantity = quantity;
}
public double getTotalPrice() { return quantity * pricePerItem; }
}
Trace: Creating and Modifying an Order
| Step | Code Executed | orderId |
quantity |
pricePerItem |
State |
|---|---|---|---|---|---|
| 1 | Order o = new Order("D123", 5, 100); |
"D123" | 5 | 100.0 | Valid order created |
| 2 | o.setQuantity(-2); |
"D123" | 5 | 100.0 | Exception thrown (invalid) |
| 3 | o.setQuantity(3); |
"D123" | 3 | 100.0 | Quantity updated to 3 |
Real-World Tie-In: Daraz’s backend uses similar encapsulation to:
- Prevent invalid order quantities (e.g., negative values).
- Calculate totals dynamically (
getTotalPrice()). - Log changes (e.g., add timestamps in setters).
Comparing Access Modifiers
| Scenario | Recommended Modifier | Example |
|---|---|---|
| Class meant for external use | public |
java.util.ArrayList |
| Class for internal framework use | default |
Helper classes in the same module |
| Method for subclasses only | protected |
java.lang.Object.clone() |
| Internal implementation detail | private |
BankAccount.balance |
flowchart TD
A["Class A"] -->|"public"| B["Any Class"]
A -->|"protected"| C["Subclass in same/different package"]
A -->|"default"| D["Same Package Only"]
A -->|"private"| E["Only within Class A"]Common Pitfalls and Best Practices
❌ Anti-Patterns
Overusing
publicfields:public class BadDesign { public int sensitiveData; // Breaks encapsulation! }- Fix: Use
privatefields + getters/setters.
- Fix: Use
Exposing implementation details:
public class LeakyImplementation { public ArrayList<String> getItems() { return items; } // Returns mutable list! }- Fix: Return a copy or use
unmodifiableList():public List<String> getItems() { return Collections.unmodifiableList(items); }
- Fix: Return a copy or use
Ignoring package structure:
- Mixing unrelated classes in the same package leads to chaos.
✅ Best Practices
- Use
privateby default for fields and methods. - Minimize
publicclasses/methods (fewer = easier to maintain). - Group related classes into packages (e.g.,
com.company.app.utils). - Document access intent with
@apiNoteor@internal(JavaDoc).
Exam Tip: How This Unit is Tested
Package Declaration:
- Write the correct
packagestatement for a given directory structure. - Example:
Answer:src/ └── edu/ └── tu/ └── exam/ └── Question.javapackage edu.tu.exam;
- Write the correct
Access Modifier Questions:
- Given a scenario, choose the correct modifier (e.g., "Should
main()bepublicorprivate?"). - Common trick: Subclasses in different packages can access
protectedmembers.
- Given a scenario, choose the correct modifier (e.g., "Should
Encapsulation Violations:
- Identify code that breaks encapsulation (e.g.,
publicfields, no validation in setters). - Fix: Convert to proper encapsulation.
- Identify code that breaks encapsulation (e.g.,
Import Statements:
- Write the correct
importfor a given class (e.g.,java.util.Scanner). - Distinguish between single-class and wildcard imports (
import java.util.*).
- Write the correct
Real-World Scenarios:
- Explain how a company (e.g., Nepal Rastra Bank) might use packages to organize financial transaction logic.
- Example Answer:
"NRB’s Java backend could use
com.nrb.corefor transaction processing,com.nrb.securityfor encryption, andcom.nrb.apifor REST endpoints. Sensitive fields likeaccountBalancewould beprivatewith controlled access viapublicmethods liketransferFunds()."
Summary Checklist
Before the exam, ensure you can:
- Declare a package and match it to a directory structure.
- List the four access modifiers and their visibility rules.
- Write a class with proper encapsulation (
privatefields + getters/setters). - Fix encapsulation violations in given code snippets.
- Explain the purpose of packages in large-scale systems (e.g., eSewa’s payment modules).
- Differentiate between
importand fully qualified names (e.g.,java.util.ArrayListvs.import java.util.*).
Based on the TU BIM syllabus for Object Oriented Programming with Java (IT234), unit 10.
Discussion
Loading…