Java Packages - Complete Tutorial
Master Java packages: Learn built-in packages, create user-defined packages, import statements, package hierarchy, and best practices for modular programming.
Built-in Packages
java.lang, java.util, etc.
User-defined
Create custom packages
Import Statements
Access package members
Hierarchy
Package structure
1. Introduction to Java Packages
A package in Java is a namespace that organizes classes and interfaces. Think of it as a folder in a file directory used to prevent naming conflicts, control access, and make searching/locating classes easier.
Why Use Packages?
- Name Conflict Resolution: Prevent class name collisions
- Access Control: Package-level access protection
- Code Organization: Group related classes together
- Reusability: Easily reuse code across projects
- Data Encapsulation: Hide implementation details
- Searchability: Easier to locate related classes
Types of Packages
- Built-in Packages: Provided by Java API (java.lang, java.util, etc.)
- User-defined Packages: Created by programmers
- Default Package: No package declaration (not recommended)
- Sub-packages: Packages inside packages
Package Naming Conventions
Package names should be in lowercase, use reverse domain name convention (com.company.project), and avoid Java keywords. Example: com.example.mypackage
Package Hierarchy Example
com
└── example
└── myproject
├── utils
│ ├── StringUtils.java
│ └── MathUtils.java
├── models
│ ├── User.java
│ └── Product.java
└── Main.java
2. Java Built-in Packages
Java provides a rich set of built-in packages that contain pre-written classes for common programming tasks. These packages are part of the Java Development Kit (JDK).
| Package Name | Description | Common Classes | Auto-imported? |
|---|---|---|---|
| java.lang | Fundamental language classes | String, System, Math, Object, Thread | Yes |
| java.util | Utility classes and collections | ArrayList, HashMap, Scanner, Date, Random | No |
| java.io | Input/output operations | File, InputStream, OutputStream, Reader, Writer | No |
| java.net | Networking operations | Socket, URL, HttpURLConnection | No |
| java.awt | Abstract Window Toolkit (GUI) | Button, Frame, Color, Font | No |
| javax.swing | Swing GUI components | JFrame, JButton, JTextField | No |
| java.sql | Database operations | Connection, Statement, ResultSet | No |
| java.time | Date and time API (Java 8+) | LocalDate, LocalTime, DateTimeFormatter | No |
| java.math | Mathematical operations | BigInteger, BigDecimal | No |
| java.text | Text formatting | DateFormat, NumberFormat | No |
// java.lang is automatically imported
// No need for: import java.lang.*;
// Import specific class from java.util
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
// Import entire package (not recommended for large packages)
import java.util.*;
// Import static members
import static java.lang.Math.PI;
import static java.lang.Math.pow;
import static java.lang.System.out;
public class BuiltInPackagesExample {
public static void main(String[] args) {
// Using java.lang (auto-imported)
String message = "Hello from java.lang!";
out.println(message); // Using static import
// Using java.util
Scanner scanner = new Scanner(System.in);
ArrayList list = new ArrayList<>();
HashMap map = new HashMap<>();
// Using java.math
java.math.BigDecimal bigDecimal = new java.math.BigDecimal("123.456");
// Using static imports
double radius = 5.0;
double area = PI * pow(radius, 2);
out.println("Area of circle: " + area);
// Using multiple packages
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
out.println("Util Date: " + utilDate);
out.println("SQL Date: " + sqlDate);
scanner.close();
}
}
3. Creating User-defined Packages
You can create your own packages to organize your classes. User-defined packages follow the same principles as built-in packages.
Project Structure: 📁 myproject/ ├── 📁 com/ │ └── 📁 example/ │ └── 📁 utils/ │ ├── StringUtils.java │ └── MathUtils.java ├── 📁 models/ │ ├── User.java │ └── Product.java └── Main.java
// Define package name
package com.example.utils;
public class StringUtils {
// Public method - accessible from anywhere
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
// Package-private method - accessible only within same package
static boolean isPalindrome(String str) {
String reversed = reverse(str);
return str.equalsIgnoreCase(reversed);
}
// Protected method - accessible within package and subclasses
protected static int countVowels(String str) {
int count = 0;
String vowels = "aeiouAEIOU";
for(char c : str.toCharArray()) {
if(vowels.indexOf(c) != -1) {
count++;
}
}
return count;
}
// Private method - accessible only within this class
private static String sanitize(String str) {
return str.trim().toLowerCase();
}
}
package com.example.utils;
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
public static int multiply(int a, int b) {
return a * b;
}
public static double divide(int a, int b) {
if(b == 0) {
throw new ArithmeticException("Division by zero!");
}
return (double) a / b;
}
public static int factorial(int n) {
if(n < 0) throw new IllegalArgumentException("Number must be non-negative");
if(n == 0 || n == 1) return 1;
int result = 1;
for(int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
public static boolean isPrime(int n) {
if(n <= 1) return false;
if(n == 2) return true;
if(n % 2 == 0) return false;
for(int i = 3; i <= Math.sqrt(n); i += 2) {
if(n % i == 0) return false;
}
return true;
}
}
package models;
public class User {
// Private fields - encapsulated
private String username;
private String email;
private int age;
// Constructor
public User(String username, String email, int age) {
this.username = username;
this.email = email;
this.age = age;
}
// Public getters and setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 0) {
this.age = age;
} else {
throw new IllegalArgumentException("Age cannot be negative");
}
}
// Public method
public void displayInfo() {
System.out.println("Username: " + username);
System.out.println("Email: " + email);
System.out.println("Age: " + age);
}
// Package-private method
void updateEmail(String newEmail) {
this.email = newEmail;
}
}
4. Importing and Using Packages
The import statement is used to bring classes and interfaces from other packages into your current file.
// Import specific classes
import com.example.utils.StringUtils;
import com.example.utils.MathUtils;
import models.User;
// Import entire package (use with caution)
// import com.example.utils.*;
// Import static members
import static com.example.utils.MathUtils.add;
import static com.example.utils.MathUtils.multiply;
public class Main {
public static void main(String[] args) {
System.out.println("=== Using User-defined Packages ===");
// Using StringUtils
String text = "Hello World";
String reversed = StringUtils.reverse(text);
System.out.println("Original: " + text);
System.out.println("Reversed: " + reversed);
// Using MathUtils
int sum = MathUtils.add(10, 20);
int product = MathUtils.multiply(5, 6);
System.out.println("\n10 + 20 = " + sum);
System.out.println("5 * 6 = " + product);
// Using static imports
int staticSum = add(15, 25);
int staticProduct = multiply(7, 8);
System.out.println("\nUsing static imports:");
System.out.println("15 + 25 = " + staticSum);
System.out.println("7 * 8 = " + staticProduct);
// Check prime numbers
System.out.println("\n=== Prime Numbers Check ===");
int[] numbers = {2, 3, 4, 5, 17, 20, 29};
for(int num : numbers) {
boolean isPrime = MathUtils.isPrime(num);
System.out.println(num + " is prime? " + isPrime);
}
// Factorial calculation
System.out.println("\n=== Factorials ===");
for(int i = 1; i <= 5; i++) {
int factorial = MathUtils.factorial(i);
System.out.println(i + "! = " + factorial);
}
// Using User class from models package
System.out.println("\n=== User Management ===");
User user1 = new User("john_doe", "john@example.com", 25);
User user2 = new User("jane_smith", "jane@example.com", 30);
user1.displayInfo();
System.out.println("---");
user2.displayInfo();
// Update user information
user1.setAge(26);
System.out.println("\nUpdated age for " + user1.getUsername() + ": " + user1.getAge());
// Working with arrays of objects from packages
System.out.println("\n=== Array of Users ===");
User[] users = {
new User("alice", "alice@example.com", 22),
new User("bob", "bob@example.com", 28),
new User("charlie", "charlie@example.com", 35)
};
for(User user : users) {
user.displayInfo();
System.out.println("---");
}
}
}
| Import Type | Syntax | Description | When to Use |
|---|---|---|---|
| Specific Class | import package.ClassName; |
Import single class from package | Most common, recommended |
| Entire Package | import package.*; |
Import all classes from package | Avoid for large packages |
| Static Import | import static package.Class.method; |
Import static members | For utility class methods |
| Fully Qualified | package.Class obj = new package.Class(); |
Use class without import | When name conflicts occur |
5. Package Access Control
Java provides four access modifiers that control the visibility of classes, methods, and variables. Packages play a crucial role in access control.
| Access Modifier | Within Class | Within Package | Subclasses | Outside Package | Example |
|---|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ | private int count; |
| default (no modifier) | ✓ | ✓ | ✗ | ✗ | int count; (package-private) |
| protected | ✓ | ✓ | ✓ | ✗ | protected void method() |
| public | ✓ | ✓ | ✓ | ✓ | public class MyClass |
package com.example.access;
// Public class - accessible from anywhere
public class AccessControlExample {
// Public field
public String publicField = "I'm public";
// Protected field
protected String protectedField = "I'm protected";
// Default (package-private) field
String defaultField = "I'm package-private";
// Private field
private String privateField = "I'm private";
// Public method
public void publicMethod() {
System.out.println("Public method called");
privateMethod(); // Can call private method from same class
}
// Protected method
protected void protectedMethod() {
System.out.println("Protected method called");
}
// Default (package-private) method
void defaultMethod() {
System.out.println("Default method called");
}
// Private method
private void privateMethod() {
System.out.println("Private method called");
}
// Method to demonstrate access
public void demonstrateAccess() {
System.out.println("\nAccess from within same class:");
System.out.println("Public field: " + publicField);
System.out.println("Protected field: " + protectedField);
System.out.println("Default field: " + defaultField);
System.out.println("Private field: " + privateField);
}
}
// Default (package-private) class - only accessible within same package
class PackagePrivateClass {
public void sayHello() {
System.out.println("Hello from package-private class!");
}
}
package com.example.access;
public class AnotherClassInSamePackage {
public static void main(String[] args) {
AccessControlExample example = new AccessControlExample();
System.out.println("Access from same package:");
// Can access public members
System.out.println("Public field: " + example.publicField);
example.publicMethod();
// Can access protected members (same package)
System.out.println("Protected field: " + example.protectedField);
example.protectedMethod();
// Can access default members (same package)
System.out.println("Default field: " + example.defaultField);
example.defaultMethod();
// Cannot access private members
// System.out.println(example.privateField); // Compilation error
// example.privateMethod(); // Compilation error
// Can access package-private class
PackagePrivateClass pkgClass = new PackagePrivateClass();
pkgClass.sayHello();
}
}
package com.example.different;
import com.example.access.AccessControlExample;
public class DifferentPackageClass {
public static void main(String[] args) {
AccessControlExample example = new AccessControlExample();
System.out.println("Access from different package:");
// Can only access public members
System.out.println("Public field: " + example.publicField);
example.publicMethod();
// Cannot access protected, default, or private members
// System.out.println(example.protectedField); // Compilation error
// System.out.println(example.defaultField); // Compilation error
// System.out.println(example.privateField); // Compilation error
// Cannot access package-private class from different package
// PackagePrivateClass pkgClass = new PackagePrivateClass(); // Compilation error
}
}
// Subclass in different package
class SubClass extends AccessControlExample {
public void testAccess() {
System.out.println("Access from subclass in different package:");
// Can access public members
System.out.println("Public field: " + publicField);
publicMethod();
// Can access protected members (through inheritance)
System.out.println("Protected field: " + protectedField);
protectedMethod();
// Cannot access default or private members
// System.out.println(defaultField); // Compilation error
// System.out.println(privateField); // Compilation error
}
}
6. Sub-packages and Package Hierarchy
Sub-packages are packages inside other packages. They help create a hierarchical structure for better organization.
Sub-package Structure
com
└── example
├── mainapp
│ ├── controllers
│ │ ├── UserController.java
│ │ └── ProductController.java
│ ├── models
│ │ ├── User.java
│ │ └── Product.java
│ └── Main.java
└── utils
├── StringHelper.java
└── MathHelper.java
// File: com/example/mainapp/controllers/UserController.java
package com.example.mainapp.controllers;
import com.example.mainapp.models.User;
import com.example.utils.StringHelper;
public class UserController {
public User createUser(String username, String email) {
// Validate username using StringHelper from utils sub-package
if(StringHelper.isValidUsername(username)) {
return new User(username, email);
} else {
throw new IllegalArgumentException("Invalid username");
}
}
public void displayUserInfo(User user) {
System.out.println("User: " + user.getUsername());
System.out.println("Email: " + user.getEmail());
}
}
// File: com/example/utils/StringHelper.java
package com.example.utils;
public class StringHelper {
public static boolean isValidUsername(String username) {
if(username == null || username.trim().isEmpty()) {
return false;
}
// Username must be 3-20 characters, alphanumeric
return username.matches("^[a-zA-Z0-9]{3,20}$");
}
public static String capitalize(String str) {
if(str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
}
// File: com/example/mainapp/Main.java
package com.example.mainapp;
import com.example.mainapp.controllers.UserController;
import com.example.mainapp.models.User;
import com.example.utils.StringHelper;
public class Main {
public static void main(String[] args) {
System.out.println("=== Working with Sub-packages ===");
// Create user controller
UserController controller = new UserController();
try {
// Create valid user
User user1 = controller.createUser("john123", "john@example.com");
controller.displayUserInfo(user1);
// Try to create invalid user
User user2 = controller.createUser("ab", "invalid@example.com");
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
// Use StringHelper from utils package
String name = "jane doe";
String capitalized = StringHelper.capitalize(name);
System.out.println("\nOriginal: " + name);
System.out.println("Capitalized: " + capitalized);
// Check username validity
String[] usernames = {"alice", "bob123", "charlie_doe", "a", "verylongusername123456789"};
System.out.println("\n=== Username Validation ===");
for(String username : usernames) {
boolean valid = StringHelper.isValidUsername(username);
System.out.println(username + ": " + (valid ? "Valid" : "Invalid"));
}
}
}
import com.example.*does NOT import sub-packages- Sub-packages are treated as separate packages
- Import each sub-package separately:
import com.example.utils.* - Package hierarchy doesn't imply access privileges
- Use meaningful sub-package names: controllers, models, views, utils, etc.
7. Practical Package Examples
Example 1: E-commerce System Package Structure
// File: com/estore/models/Product.java
package com.estore.models;
public class Product {
private String id;
private String name;
private double price;
private int stock;
public Product(String id, String name, double price, int stock) {
this.id = id;
this.name = name;
this.price = price;
this.stock = stock;
}
// Getters and setters
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
public int getStock() { return stock; }
public void setStock(int stock) { this.stock = stock; }
public void displayInfo() {
System.out.printf("Product: %s | Price: $%.2f | Stock: %d\n",
name, price, stock);
}
}
// File: com/estore/services/InventoryService.java
package com.estore.services;
import com.estore.models.Product;
public class InventoryService {
public void updateStock(Product product, int quantity) {
if(product.getStock() + quantity < 0) {
throw new IllegalArgumentException("Insufficient stock");
}
product.setStock(product.getStock() + quantity);
System.out.println("Updated stock for " + product.getName() +
" to " + product.getStock());
}
public boolean checkAvailability(Product product, int quantity) {
return product.getStock() >= quantity;
}
}
// File: com/estore/utils/PriceCalculator.java
package com.estore.utils;
public class PriceCalculator {
public static double calculateTotal(double price, int quantity) {
return price * quantity;
}
public static double applyDiscount(double total, double discountPercent) {
return total * (1 - discountPercent / 100);
}
public static double calculateTax(double amount, double taxRate) {
return amount * taxRate / 100;
}
}
// File: com/estore/Main.java
package com.estore;
import com.estore.models.Product;
import com.estore.services.InventoryService;
import com.estore.utils.PriceCalculator;
public class Main {
public static void main(String[] args) {
System.out.println("=== E-commerce System ===");
// Create products
Product laptop = new Product("P001", "Laptop", 999.99, 10);
Product phone = new Product("P002", "Smartphone", 699.99, 25);
// Display products
laptop.displayInfo();
phone.displayInfo();
// Use InventoryService
InventoryService inventory = new InventoryService();
inventory.updateStock(laptop, -2); // Sell 2 laptops
inventory.updateStock(phone, 10); // Restock 10 phones
// Check availability
boolean available = inventory.checkAvailability(laptop, 5);
System.out.println("\n5 laptops available? " + available);
// Use PriceCalculator
double total = PriceCalculator.calculateTotal(laptop.getPrice(), 3);
double discounted = PriceCalculator.applyDiscount(total, 10);
double tax = PriceCalculator.calculateTax(discounted, 8);
double finalPrice = discounted + tax;
System.out.println("\n=== Price Calculation ===");
System.out.printf("Subtotal (3 laptops): $%.2f\n", total);
System.out.printf("After 10%% discount: $%.2f\n", discounted);
System.out.printf("Tax (8%%): $%.2f\n", tax);
System.out.printf("Final Price: $%.2f\n", finalPrice);
}
}
8. Package Best Practices
- Default package usage: Always use explicit packages
- Poor naming: Use meaningful, reverse-domain names
- Circular dependencies: Avoid package A depending on B and B depending on A
- Wildcard imports: Avoid
import java.util.*in large projects - Package sprawl: Too many small packages can be confusing
- Ignoring access modifiers: Use appropriate access levels
Package Design Best Practices
- Use reverse domain name convention: com.company.project
- Keep packages cohesive - related classes together
- Avoid cyclic dependencies between packages
- Use sub-packages for logical separation
- Follow naming conventions (all lowercase)
- Keep package size manageable (5-30 classes per package)
- Use package-info.java for documentation
Organization Strategies
- By feature: com.example.auth, com.example.payment
- By layer: controllers, models, services, utils
- By module: user, product, order modules
- Mixed approach: Combine based on project needs
- Separate API from implementation
- Use common package for shared utilities
Package Design Principles
High Cohesion: Related classes should be together
Low Coupling: Minimize dependencies between packages
Reuse-Release Equivalence: What you release is what you reuse
Common Closure: Classes that change together belong together
Stable Abstractions: Stable packages should be abstract
/**
* Provides utility classes for common operations in the application.
*
* This package contains helper classes that provide reusable functionality
* for string manipulation, mathematical calculations, date formatting,
* and other common tasks.
*
* Package Specification
*
* - {@link com.example.utils.StringHelper} - String manipulation utilities
* - {@link com.example.utils.MathHelper} - Mathematical operations
* - {@link com.example.utils.DateHelper} - Date and time utilities
* - {@link com.example.utils.ValidationHelper} - Input validation
*
*
* @since 1.0
* @author Your Name
* @version 1.0
*/
package com.example.utils;