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
- Use reverse domain name convention:
com.companyname.project.module - Keep packages focused: Each package should have a single responsibility
- Use meaningful names: Avoid generic names like
util,common - Follow Java naming conventions: Use lowercase for package names
- Minimize package dependencies: Reduce coupling between packages
- Use access modifiers appropriately:
public: Accessible from anywhereprotected: 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.