☕ Java

User-defined Packages

User-defined packages are packages you create to organise your own application's classes and interfaces. They follow the same rules as Java's built-in packages — the package declaration must be the first statement in the file, the directory structure must match the package hierarchy, and access modifiers control visibility across package boundaries. Designing a meaningful package structure is a foundational software engineering skill that directly affects how maintainable and navigable a codebase remains as it grows.

Declaring and Using User-defined Packages

Declaring a package is the first line of any Java source file (before imports). The statement package com.example.service; tells the compiler and JVM that the class in this file belongs to the com.example.service namespace. The file must be placed in a directory structure matching the package — the file for com.example.service.OrderService must be at the path com/example/service/OrderService.java relative to the source root. This correspondence between package declaration and file location is enforced by the toolchain. If you declare package com.example.service but place the file in com/example/model/, compilation will likely succeed but the class will not be findable at runtime using the declared package name, producing a ClassNotFoundException or NoClassDefFoundError. IDEs enforce this alignment automatically. Maven and Gradle expect source files under src/main/java and derive the package from the directory structure. When one class in a package needs to use a class from a different package, it uses a fully qualified class name (com.example.model.Order) or imports the class. Within the same package, classes can reference each other by simple name without any import. This is part of why related classes belong in the same package — they can reference each other's package-private members and use simple names, reducing verbosity. Compiling packages from the command line uses the -sourcepath and -d flags: javac -sourcepath src -d out src/com/example/service/OrderService.java compiles the source file and places the .class file in the matching directory under out/. Running: java -cp out com.example.service.OrderService executes the class using its fully qualified name.
Java
// ── File: src/com/example/model/Order.java ───────────────────────────
package com.example.model;      // FIRST statement — before imports

public class Order {
    private String  id;
    private String  customerId;
    private double  total;

    public Order(String id, String customerId, double total) {
        this.id         = id;
        this.customerId = customerId;
        this.total      = total;
    }

    public String getId()         { return id;         }
    public String getCustomerId() { return customerId; }
    public double getTotal()      { return total;      }
}

// ── File: src/com/example/service/OrderService.java ───────────────────
package com.example.service;

import com.example.model.Order;         // import from another package

public class OrderService {

    // Can use Order by simple name because it was imported:
    public Order createOrder(String customerId, double total) {
        String id = generateId();
        return new Order(id, customerId, total);  // simple name
    }

    // Without import, must use fully qualified name:
    public com.example.model.Order findOrder(String id) {
        // ...
        return null;
    }

    private String generateId() {
        return "ORD-" + System.currentTimeMillis();
    }
}

// ── Directory structure: ──────────────────────────────────────────────
//  src/
//  └── com/
//      └── example/
//          ├── model/
//          │   └── Order.java
//          └── service/
//              └── OrderService.java
//
// ── Compile from command line: ────────────────────────────────────────
//  javac -sourcepath src -d out src/com/example/service/OrderService.java
//
// ── Run from command line: ────────────────────────────────────────────
//  java -cp out com.example.service.OrderService

Package Structure Design Patterns

The way you structure packages shapes how navigable and maintainable a codebase is. Two major philosophies exist: package-by-layer and package-by-feature, and real projects often use a combination. Package-by-layer groups classes by their architectural role. All controllers together, all services together, all repositories together. This is the traditional structure taught in most tutorials and used in many enterprise codebases. The advantage is that every developer knows exactly where to find any type of class. The disadvantage is that changing a feature requires touching many packages — all the classes for the "orders" feature are spread across orders.controller, orders.service, orders.repository, and orders.model. Package-by-feature groups all classes for a feature together — the controller, service, repository, model, and exceptions for orders all live in com.example.order. This aligns packages with the business domain rather than the technical architecture. When you work on a feature, all the relevant code is in one place. The disadvantage is that shared utilities and cross-cutting concerns need separate packages. A hybrid approach is increasingly popular: package by feature at the top level, with sub-packages for technical layers within each feature package. com.example.order contains the feature as a whole; com.example.order.service, com.example.order.repository, and com.example.order.dto contain the technical layers within the feature. This gives both the navigability of feature packages and the clarity of layer packages. Regardless of the approach, the package structure should reflect how developers think about and navigate the code, not the implementation technology. Package names like com.example.hibernate or com.example.springboot are poor choices because they couple the package names to implementation technology that may change.
Java
// ── Package-by-layer structure: ───────────────────────────────────────
//
//  com.example
//  ├── controller
//  │   ├── OrderController.java
//  │   └── UserController.java
//  ├── service
//  │   ├── OrderService.java
//  │   └── UserService.java
//  ├── repository
//  │   ├── OrderRepository.java
//  │   └── UserRepository.java
//  ├── model
//  │   ├── Order.java
//  │   └── User.java
//  └── exception
//      ├── OrderNotFoundException.java
//      └── UserNotFoundException.java
//
// Pro: easy to find any class of a given type
// Con: changes to one feature touch many packages

// ── Package-by-feature structure: ────────────────────────────────────
//
//  com.example
//  ├── order
//  │   ├── OrderController.java
//  │   ├── OrderService.java
//  │   ├── OrderRepository.java
//  │   ├── Order.java
//  │   └── OrderNotFoundException.java
//  ├── user
//  │   ├── UserController.java
//  │   ├── UserService.java
//  │   ├── UserRepository.java
//  │   ├── User.java
//  │   └── UserNotFoundException.java
//  └── common
//      ├── BaseEntity.java
//      └── ApiResponse.java
//
// Pro: all code for a feature in one place
// Con: shared code needs a separate 'common' package

// ── Hybrid structure: ─────────────────────────────────────────────────
//
//  com.example
//  ├── order
//  │   ├── controller
//  │   │   └── OrderController.java
//  │   ├── service
//  │   │   └── OrderService.java
//  │   ├── repository
//  │   │   └── OrderRepository.java
//  │   ├── dto
//  │   │   ├── CreateOrderRequest.java
//  │   │   └── OrderResponse.java
//  │   ├── exception
//  │   │   └── OrderNotFoundException.java
//  │   └── Order.java
//  └── shared
//      ├── exception
//      │   └── BaseException.java
//      └── util
//          └── DateUtils.java

// ── Package-private for internal implementation: ───────────────────────
package com.example.order;

// Public API of the order package:
public class Order { ... }
public class OrderService { ... }

// Internal helpers — invisible outside com.example.order:
class OrderValidator { ... }
class OrderPriceCalculator { ... }

Related Topics in Packages and Access Control

Package Concept
A package in Java is a namespace that groups related classes, interfaces, enums, and annotations into a logical unit. Packages solve three fundamental problems in large software systems: name collision (two classes can have the same simple name if they are in different packages), access control (package-private visibility limits access to the same package), and organisation (related types live together and can be found intuitively). Every Java class belongs to a package — either explicitly declared or the unnamed default package.
Built-in Packages
Java ships with a rich standard library organised into packages under the java and javax namespaces. These built-in packages provide data structures, I/O, networking, concurrency, database access, XML processing, GUI components, and much more. Knowing which package contains which functionality, and understanding the most important classes in the most frequently used packages, is foundational knowledge for every Java developer.
import
The import statement allows you to use a class, interface, or enum by its simple name rather than its fully qualified name. Without an import, every reference to java.util.ArrayList requires writing the full name java.util.ArrayList. With import java.util.ArrayList, you write simply ArrayList. Imports are a compile-time convenience — they have no effect on performance, do not load classes, and are not present in the compiled bytecode. The compiler uses them only to resolve simple names to fully qualified names.
Static Import
Static import allows you to access static members — static fields and static methods — of a class by their simple name without qualifying them with the class name. Introduced in Java 5, static import reduces verbosity when a few specific static members are used repeatedly. It is most useful for mathematical constants and functions, assertion methods in tests, and constants. Like regular imports, static imports are a compile-time convenience with no runtime effect.