Creating Custom Packages in Java

Packages in Java are used to organize classes and interfaces into namespaces, making code more modular, reusable, and maintainable. Here's a comprehensive guide to creating and using custom packages.

1. Basic Package Structure

Directory Structure

project-root/
├── com/
│   └── mycompany/
│       └── util/
│           ├── MathHelper.java
│           └── StringHelper.java
└── Main.java

Creating a Simple Package

com/mycompany/util/MathHelper.java

package com.mycompany.util;
public class MathHelper {
public static int add(int a, int b) {
return a + b;
}
public static int multiply(int a, int b) {
return a * b;
}
public static boolean isEven(int number) {
return number % 2 == 0;
}
}

com/mycompany/util/StringHelper.java

package com.mycompany.util;
public class StringHelper {
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
public static boolean isPalindrome(String str) {
String cleaned = str.replaceAll("\\s+", "").toLowerCase();
return cleaned.equals(reverse(cleaned));
}
}

2. Using Packages

Importing Specific Classes

import com.mycompany.util.MathHelper;
import com.mycompany.util.StringHelper;
public class Main {
public static void main(String[] args) {
int sum = MathHelper.add(5, 3);
String reversed = StringHelper.reverse("Hello");
System.out.println("Sum: " + sum);
System.out.println("Reversed: " + reversed);
}
}

Importing Entire Package

import com.mycompany.util.*;
public class Main {
public static void main(String[] args) {
System.out.println("Is 10 even? " + MathHelper.isEven(10));
System.out.println("Is 'radar' palindrome? " + StringHelper.isPalindrome("radar"));
}
}

Using Fully Qualified Names (Without Import)

public class Main {
public static void main(String[] args) {
int result = com.mycompany.util.MathHelper.multiply(4, 5);
System.out.println("Multiplication result: " + result);
}
}

3. Package with Access Modifiers

com/mycompany/model/Person.java

package com.mycompany.model;
public class Person {
private String name;
private int age;
// Package-private constructor
Person(String name, int age) {
this.name = name;
this.age = age;
}
// Public factory method
public static Person createPerson(String name, int age) {
return new Person(name, age);
}
// Public getters
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Package-private method
void setAge(int age) {
this.age = age;
}
}

com/mycompany/model/Employee.java (same package)

package com.mycompany.model;
public class Employee {
public void demoAccess() {
// Can access package-private members within same package
Person person = new Person("John", 25); // Constructor is accessible
person.setAge(30); // Method is accessible
}
}

4. Subpackages

Directory Structure

project-root/
├── com/
│   └── mycompany/
│       ├── util/
│       │   ├── MathHelper.java
│       │   └── StringHelper.java
│       └── service/
│           ├── UserService.java
│           └── ProductService.java
└── Main.java

com/mycompany/service/UserService.java

package com.mycompany.service;
import com.mycompany.util.StringHelper; // Import from different subpackage
public class UserService {
public void processUsername(String username) {
String formattedName = StringHelper.reverse(username);
System.out.println("Formatted username: " + formattedName);
}
public static void displayServiceInfo() {
System.out.println("User Service - manages user operations");
}
}

5. Compiling and Running

Command Line Compilation

# Compile all Java files
javac com/mycompany/util/*.java com/mycompany/service/*.java Main.java
# Or compile from project root
javac -d . com/mycompany/util/MathHelper.java
javac -d . com/mycompany/util/StringHelper.java
javac -d . com/mycompany/service/UserService.java
javac -d . Main.java
# Run the application
java Main

Using CLASSPATH

# Set CLASSPATH and compile
export CLASSPATH=/path/to/project-root
javac com/mycompany/util/MathHelper.java
javac Main.java
java Main

6. Advanced Package Example

Complete Example Structure

bank-app/
├── com/
│   └── bank/
│       ├── model/
│       │   ├── Account.java
│       │   └── Customer.java
│       ├── service/
│       │   ├── AccountService.java
│       │   └── TransactionService.java
│       └── util/
│           └── Validator.java
└── BankApp.java

com/bank/model/Account.java

package com.bank.model;
public class Account {
private String accountNumber;
private double balance;
private Customer customer;
public Account(String accountNumber, Customer customer) {
this.accountNumber = accountNumber;
this.customer = customer;
this.balance = 0.0;
}
// Getters and setters
public String getAccountNumber() { return accountNumber; }
public double getBalance() { return balance; }
public Customer getCustomer() { return customer; }
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
}

com/bank/service/AccountService.java

package com.bank.service;
import com.bank.model.Account;
import com.bank.util.Validator;
public class AccountService {
public static Account createAccount(String accountNumber, String customerName) {
if (!Validator.isValidAccountNumber(accountNumber)) {
throw new IllegalArgumentException("Invalid account number");
}
return new Account(accountNumber, customerName);
}
public static void transfer(Account from, Account to, double amount) {
if (from.withdraw(amount)) {
to.deposit(amount);
System.out.println("Transfer successful: $" + amount);
} else {
System.out.println("Transfer failed: Insufficient funds");
}
}
}

com/bank/util/Validator.java

package com.bank.util;
public class Validator {
public static boolean isValidAccountNumber(String accountNumber) {
return accountNumber != null && 
accountNumber.matches("\\d{8,12}") && 
!accountNumber.startsWith("0");
}
public static boolean isValidAmount(double amount) {
return amount > 0;
}
}

BankApp.java

import com.bank.model.Account;
import com.bank.service.AccountService;
public class BankApp {
public static void main(String[] args) {
try {
Account acc1 = AccountService.createAccount("12345678", "John Doe");
Account acc2 = AccountService.createAccount("87654321", "Jane Smith");
acc1.deposit(1000);
acc2.deposit(500);
AccountService.transfer(acc1, acc2, 300);
System.out.println("Account 1 balance: $" + acc1.getBalance());
System.out.println("Account 2 balance: $" + acc2.getBalance());
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}

7. Best Practices

  1. Use reverse domain name convention: com.companyname.project.module
  2. Keep packages focused: Each package should have a single responsibility
  3. Use meaningful names: Avoid generic names like util, common
  4. Follow Java naming conventions: Use lowercase for package names
  5. Minimize package dependencies: Reduce coupling between packages
  6. Use access modifiers appropriately:
  • public: Accessible from anywhere
  • protected: Accessible within package and subclasses
  • default (package-private): Accessible only within package
  • private: Accessible only within class

8. Common Issues and Solutions

Class Not Found

// Error: cannot find symbol
// Solution: Check package declaration and imports
import com.mycompany.util.MathHelper;

Compilation Issues

# Compile with proper directory structure
javac -d . com/mycompany/util/MathHelper.java
# Run from correct directory with proper package structure
java com.mycompany.Main

This comprehensive guide covers creating, organizing, and using custom packages in Java, helping you build well-structured, maintainable applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper