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.