IT234 Object Oriented Programming with Java

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.util for collections) and avoid naming conflicts.
  • Access modifiers (public, protected, private, default) control visibility of members across packages and subclasses.
  • Encapsulation hides internal state (via private fields) and exposes controlled access (via public getters/setters).
  • import statements bring classes from other packages into scope without fully qualifying names.
  • Default package (unnamed) is discouraged in production code but used in small programs.
  • package declaration 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.util for 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

  1. Declare the package at the top of the file:
    package com.example.myapp;
    
  2. Directory structure must match the package name:
    src/
    └── com/
        └── example/
            └── myapp/
                └── MyClass.java
    
  3. Compile and run:
    javac com/example/myapp/MyClass.java
    java com.example.myapp.MyClass
    

Default Package (No Package)

  • If no package declaration 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: TokenValidator methods are private to prevent misuse.
  • Reusability: Transaction class 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 isActive or iccid.
  • Forces validation through public methods like activate().

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

  1. Declare fields as private.
  2. Provide public getter/setter methods to control access.
  3. 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

  1. Overusing public fields:

    public class BadDesign {
        public int sensitiveData;  // Breaks encapsulation!
    }
    
    • Fix: Use private fields + getters/setters.
  2. 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); }
      
  3. Ignoring package structure:

    • Mixing unrelated classes in the same package leads to chaos.

✅ Best Practices

  1. Use private by default for fields and methods.
  2. Minimize public classes/methods (fewer = easier to maintain).
  3. Group related classes into packages (e.g., com.company.app.utils).
  4. Document access intent with @apiNote or @internal (JavaDoc).

Exam Tip: How This Unit is Tested

  1. Package Declaration:

    • Write the correct package statement for a given directory structure.
    • Example:
      src/
      └── edu/
          └── tu/
              └── exam/
                  └── Question.java
      
      Answer: package edu.tu.exam;
  2. Access Modifier Questions:

    • Given a scenario, choose the correct modifier (e.g., "Should main() be public or private?").
    • Common trick: Subclasses in different packages can access protected members.
  3. Encapsulation Violations:

    • Identify code that breaks encapsulation (e.g., public fields, no validation in setters).
    • Fix: Convert to proper encapsulation.
  4. Import Statements:

    • Write the correct import for a given class (e.g., java.util.Scanner).
    • Distinguish between single-class and wildcard imports (import java.util.*).
  5. 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.core for transaction processing, com.nrb.security for encryption, and com.nrb.api for REST endpoints. Sensitive fields like accountBalance would be private with controlled access via public methods like transferFunds()."


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 (private fields + 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 import and fully qualified names (e.g., java.util.ArrayList vs. import java.util.*).

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

Discussion

Loading…