IT234 Object Oriented Programming With Java

Object Oriented Programming With JavaUnit 1010 min read

Packages, Access Modifiers & Java Modularity

Unit 10 of Object Oriented Programming With Java covers Java’s package system, access modifiers (public, private, protected, default), and how they enforce encapsulation and modularity. Learn to create custom packages, import classes, and control visibility between classes—key for large-scale applications.

TAKEAWAYS:

  • Packages organize Java classes hierarchically (e.g., java.util for utilities) and avoid naming conflicts using dot notation (com.example.app).
  • Access modifiers (public, private, protected, default) restrict class/method visibility to control data hiding and reusability.
  • import statements bring external classes into scope, while package declarations define namespace boundaries.
  • Default (package-private) access allows visibility only within the same package, enabling controlled collaboration between related classes.
  • Poor access control leads to security risks (e.g., exposing sensitive data) or tight coupling; proper modifiers promote maintainability.
  • Real-world use: Banks use packages to separate Account, Transaction, and Loan logic; eSewa isolates payment processing in its own package.

1. Why Use Packages?

Java packages serve three critical purposes:

  1. Namespace Management: Prevent naming collisions (e.g., two classes named User in different packages).
  2. Access Control: Group related classes (e.g., java.util.ArrayList vs. java.sql.Array).
  3. Modularity: Split large projects into logical units (e.g., com.example.ui for UI classes).
[object Object][object Object][object Object][object Object][object Object]Unorganized CodePackage APackage BPackage CModular System
Before/after: Unorganized code vs. modular package structure

How Packages Work

  • Declared at the top of a file with package com.example.subpackage;.
  • Directory structure mirrors package names (e.g., com/example/subpackage/Class.java).
  • Default Package: Classes without a package declaration reside in an unnamed package (discouraged for production code).
comexampleapputils
Directory structure mirroring package hierarchy (com.example.app and com.example.utils)

Example: The java.util package contains ArrayList, HashMap, and Scanner, while java.io handles file operations.


2. Access Modifiers: Controlling Visibility

Access modifiers restrict how classes, methods, and fields can be accessed. Use them to enforce encapsulation.

Modifier Class Package Subclass World
public ✅ ✅ ✅ ✅
protected ✅ ✅ ✅ ❌
Default ✅ ✅ ❌ ❌
private ✅ ❌ ❌ ❌

Key Rules

  • Class-level modifiers: Only public or default (no private/protected for top-level classes).
  • Constructor modifiers: Control object creation (e.g., private constructors for singletons).
  • Method/field modifiers: Apply to members (e.g., private fields hide implementation details).

Example: A BankAccount class hides its balance field as private but exposes deposit() and withdraw() as public.

package com.example.bank;

public class BankAccount {
    private double balance; // Hidden from outside

    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    protected void transfer(BankAccount other, double amount) {
        this.balance -= amount;
        other.balance += amount;
    }
}

Trace: If BankAccount is in com.example.bank and SavingsAccount extends it in com.example.savings:

  • deposit() is accessible worldwide (public).
  • transfer() is accessible only in subclasses or the same package (protected).
  • balance is inaccessible outside the class (private).

3. Creating and Importing Packages

Step 1: Define a Package

Save Person.java in com/example/model/:

package com.example.model;

public class Person {
    private String name;
    public Person(String name) { this.name = name; }
    public String getName() { return name; }
}

Step 2: Import and Use

In Main.java (same directory or another package):

import com.example.model.Person; // Import specific class

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Rohan");
        System.out.println(p.getName()); // Output: Rohan
    }
}

Alternative: Import all classes from a package (not recommended for large packages):

import com.example.model.*; // Wildcard import

4. Real-World Applications

Example 1: eSewa’s Payment System

  • Package Structure:
    com.esewa/
    ├── core/          // Payment logic (private to eSewa)
    │   └── Transaction.java
    ├── api/           // Public APIs for merchants
    │   └── PaymentGateway.java
    └── utils/         // Shared utilities
    
  • Access Modifiers:
    • Transaction fields (amount, status) are private.
    • PaymentGateway exposes processPayment() as public for merchants.
    • Internal classes (e.g., FraudDetector) are default (package-private).

Example 2: Daraz’s Order Queue

  • Package: com.daraz.order
    • Order class (default access) manages orders within the package.
    • OrderProcessor (public) handles external requests.
  • Why? Prevents direct access to Order’s internal state (e.g., customerId), reducing bugs.

Example 3: Ncell’s Billing System

  • Packages:
    • com.ncell.billing (private): BillGenerator, PaymentValidator.
    • com.ncell.api (public): BillingAPI (exposes generateBill()).
  • Access Control: Ensures only authorized classes (e.g., PaymentValidator) can modify billing data.

5. Common Pitfalls and Best Practices

Pitfall Solution
Overusing public Restrict access to minimize side effects.
Circular dependencies Refactor packages to reduce coupling.
Wildcard imports (*.java) Explicitly import needed classes.
Exposing implementation details Use interfaces (Unit 5) for contracts.

Best Practice:

  • Use private for fields and helper methods.
  • Use protected for subclass-specific logic.
  • Use default for package-private collaboration.
  • Use public only for APIs or truly shared utilities.

6. Worked Example: Library Management System

Scenario: A library tracks books and members. Books should not be directly modified by members.

// Package: com.library.model
package com.library.model;

public class Book {
    private String title;
    private boolean isAvailable;

    public Book(String title) {
        this.title = title;
        this.isAvailable = true;
    }

    public void borrow() {
        if (isAvailable) isAvailable = false;
    }

    public void returnBook() {
        isAvailable = true;
    }

    public boolean isAvailable() {
        return isAvailable;
    }
}
// Package: com.library.service
package com.library.service;

import com.library.model.Book;

public class Library {
    private Book[] books = new Book[10];

    public void addBook(Book book) {
        for (int i = 0; i < books.length; i++) {
            if (books[i] == null) {
                books[i] = book;
                break;
            }
        }
    }

    public Book findBook(String title) {
        for (Book book : books) {
            if (book != null && book.getTitle().equals(title)) {
                return book;
            }
        }
        return null;
    }
}

Trace: A Member class (in com.library.user) can only interact with Library via public methods:

// Package: com.library.user
package com.library.user;

import com.library.service.Library;

public class Member {
    public void borrowBook(Library library, String title) {
        Book book = library.findBook(title);
        if (book != null && book.isAvailable()) {
            book.borrow(); // Calls private method indirectly
        }
    }
}

Visualization of Access:

classDiagram
    class Book {
        -String title
        -boolean isAvailable
        +borrow()
        +returnBook()
    }
    class Library {
        -Book[] books
        +addBook(Book)
        +findBook(String)
    }
    class Member {
        +borrowBook(Library, String)
    }
    Library --> Book : "manages"
    Member --> Library : "uses"
    Member --> Book : "no direct access"

7. Exam Tip

What Examiners Look For:

  1. Correct Package Declaration: Ensure the file path matches the package name (e.g., com/example/Class.java).
  2. Proper Imports: Use import for external classes; avoid wildcard imports unless specified.
  3. Access Modifier Usage:
    • Fields/methods should use the most restrictive modifier possible.
    • Top-level classes can only be public or default.
  4. Error Handling: Common mistakes include:
    • Forgetting package declaration (default package).
    • Incorrect import paths (e.g., import com.example.Class when the class is in com.example.subpackage).
  5. Diagrams: Draw package/class diagrams showing:
    • Package boundaries (boxes).
    • Access modifiers (arrows or labels).
    • Relationships (e.g., A uses B from another package).

Sample Exam Question:

"Write a program to create two packages: com.math (with Calculator) and com.ui (with App). Import Calculator in App and demonstrate method calls. Use appropriate access modifiers."

Expected Answer Structure:

  1. Two directories: com/math/ and com/ui/.
  2. Calculator.java with public methods (e.g., add()).
  3. App.java with import com.math.Calculator; and usage.
  4. Explanation of why Calculator methods are public.

8. Quick Revision Table

Concept Syntax/Example Use Case
Package Declaration package com.example; Organize classes hierarchically.
Import Statement import com.example.Class; Access external classes.
Public Access public class X { ... } API or shared utilities.
Private Access private int value; Hide implementation details.
Protected Access protected void helper() { ... } Subclass-specific logic.
Default Access (No modifier) Package-private collaboration.

Final Note: Mastering packages and access modifiers is critical for writing scalable Java applications. Always ask:

  • Who needs to access this?
  • What’s the least permissive modifier that works?
  • How can I reduce coupling between packages?

Based on the TU BITM syllabus for Object Oriented Programming With Java (IT234), unit 10.

Discussion

Loading…