ā˜• Java

Java Packages and API — Syntax, Structure, Examples & Best Practices

Everything you need to know about Java Packages and the Java API — package declaration, import statements, built-in Java API packages, user-defined packages, access modifiers with packages, static imports, the most important API classes, anti-patterns, and real-world production code examples.

šŸ“…

Last Updated

March 2026

ā±ļø

Read Time

20 min

šŸŽÆ

Level

Beginner to Intermediate

šŸ·ļø

Chapter

10 of 35

What is a Package in Java?

A package in Java is a namespace mechanism that groups related classes, interfaces, enumerations, and annotations into a single logical unit. Think of it as a folder system for your Java code — just as you organize files into folders on your computer, you organize Java types into packages. Packages solve two fundamental problems that arise in large projects: name collision (two classes with the same name in different packages coexist peacefully) and controlled access (package-private members are only visible within the same package).

Packages in Java are directly mapped to the directory structure on disk. A class declared in package com.techsustainify.service must physically reside in a directory path com/techsustainify/service/ relative to the source root. This one-to-one mapping between packages and directories is enforced by the Java compiler — if the directory structure does not match the package declaration, the code will not compile.

The Java API (Application Programming Interface) is the massive collection of pre-built packages and classes that ship with every Java installation — the Java Standard Library. It provides thousands of ready-to-use classes for collections, I/O, networking, math, concurrency, date/time, and much more. Learning which packages exist in the Java API and what they contain is one of the most practical skills a Java developer can develop.

Package Declaration — Syntax & Rules

The package statement declares which package a Java source file belongs to. It must be the absolute first statement in the file — before any import statements, before any class declarations, and before any comments (though comments above the package statement are technically allowed). Only one package statement is permitted per source file.

šŸ“‹
Package Declaration Syntax & Rules

Syntax: package packageName; Example: package com.techsustainify.service; Rules: (1) Must be the FIRST statement in the source file. (2) Only ONE package declaration allowed per file. (3) Package name must be a valid Java identifier — lowercase letters, digits, underscores. (4) Multi-level packages use dots: com.company.module.submodule. (5) Package name maps directly to directory path: com/company/module/submodule/. (6) If omitted, the class belongs to the unnamed default package — acceptable for experiments, but never for production code. (7) By convention, package names use reverse domain notation to guarantee global uniqueness: com.google.gson, org.springframework.core, io.netty.channel.

šŸ“
Package-to-Directory Mapping

Java enforces a strict one-to-one mapping between package names and directory structure. Package: com.techsustainify.model → Directory: src/com/techsustainify/model/. The Java compiler (javac) and classloader use this mapping to locate class files. If EmployeeService.java declares package com.techsustainify.service; but is saved in the wrong directory, the compiler produces: error: class EmployeeService is public, should be declared in a file named EmployeeService.java. Modern IDEs (IntelliJ IDEA, Eclipse, VS Code) enforce this automatically — creating a class in a package auto-creates the matching directory structure.

šŸ·ļø
Naming Conventions — Reverse Domain

The standard Java convention for package names is reverse Internet domain notation: com.companyname.projectname.module. Examples: com.google.gson (Google's Gson library), org.springframework.boot (Spring Boot), io.micrometer.core (Micrometer metrics), com.techsustainify.tutorial. Why reverse domain? It guarantees global uniqueness — two companies with different domains cannot produce a package name collision. Rules: all lowercase, no hyphens (use underscores if necessary), avoid Java keywords as package name segments. Segments beginning with digits are technically invalid Java identifiers — a leading underscore is the workaround: com.example._123module.

ā˜• JavaPackageDeclarationExamples.java
// āœ… File: src/com/techsustainify/model/Employee.java
package com.techsustainify.model;  // ← FIRST statement — mandatory

// Import statements come AFTER package declaration
import java.time.LocalDate;
import java.util.Objects;

// Class declaration comes last
public class Employee {
    private final String name;
    private final String department;
    private final LocalDate joiningDate;

    public Employee(String name, String department, LocalDate joiningDate) {
        this.name       = Objects.requireNonNull(name, "name must not be null");
        this.department = Objects.requireNonNull(department, "department must not be null");
        this.joiningDate = Objects.requireNonNull(joiningDate, "joiningDate must not be null");
    }

    public String getName()         { return name; }
    public String getDepartment()   { return department; }
    public LocalDate getJoiningDate() { return joiningDate; }

    @Override
    public String toString() {
        return "Employee{name='" + name + "', dept='" + department
               + "', joined=" + joiningDate + "}";
    }
}

// ─────────────────────────────────────────────
// āœ… File: src/com/techsustainify/service/EmployeeService.java
// package com.techsustainify.service;
//
// import com.techsustainify.model.Employee;  // ← import from another package
// import java.time.LocalDate;
// import java.util.ArrayList;
// import java.util.List;
//
// public class EmployeeService {
//     private List<Employee> employees = new ArrayList<>();
//
//     public void hire(String name, String dept) {
//         employees.add(new Employee(name, dept, LocalDate.now()));
//         System.out.println("Hired: " + name + " in " + dept);
//     }
//
//     public List<Employee> getAll() { return List.copyOf(employees); }
// }

// ─────────────────────────────────────────────
// āŒ Default (unnamed) package — NO package declaration
// Acceptable for quick tests, never for production
// public class QuickTest {
//     public static void main(String[] args) {
//         System.out.println("This class is in the unnamed default package");
//     }
// }

Import Statement — Types, Usage & Rules

The import statement makes types from other packages available in the current file by their simple name instead of requiring the fully qualified name every time. Without import, you must write java.util.ArrayList every time you use it. With import java.util.ArrayList;, you can simply write ArrayList. Import is purely a compile-time convenience — it does not affect performance, does not load extra code, and does not change the compiled bytecode.

1ļøāƒ£
Single-Type Import (Specific Import)

Imports exactly one class or interface by its full name: import java.util.ArrayList; import java.util.HashMap; import java.time.LocalDate; This is the preferred style in professional Java code — it is explicit, unambiguous, and makes it immediately clear which specific classes the file depends on. Modern IDEs (IntelliJ, Eclipse) auto-manage specific imports — they add the exact import when you use a class and remove unused ones automatically. Google Java Style Guide mandates specific imports over wildcard imports.

🌟
Wildcard Import (On-Demand Import)

Imports all public types from an entire package at once: import java.util.*; import java.io.*; Convenient when many types from one package are used. Does NOT import sub-packages — import java.util.* does NOT import java.util.concurrent.* or java.util.function.*. Does NOT affect compile or runtime performance — the compiler resolves exactly which classes are needed. Potential downside: name ambiguity — if two imported packages both contain a class named Date (java.util.Date and java.sql.Date), the compiler reports an ambiguous symbol error and you must use the fully qualified name for at least one.

⚔
Static Import

Imports static members (fields and methods) of a class so they can be used without the class name prefix: import static java.lang.Math.PI; import static java.lang.Math.sqrt; import static java.lang.Math.pow; // Then use: double r = sqrt(pow(x, 2) + pow(y, 2)); // Instead of: double r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); Also useful for test code: import static org.junit.jupiter.api.Assertions.*; — allows assertEquals(), assertTrue() directly. Use judiciously — overuse makes code harder to understand (readers cannot tell which class a static method belongs to).

ā˜• JavaImportStatements.java
package com.techsustainify.demo;

// āœ… Single-type imports — preferred in professional code
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collections;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// āœ… Static import — use static members without class name
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.Math.pow;

public class ImportStatements {
    public static void main(String[] args) {

        // āœ… Using imported classes by simple name
        List<String> cities = new ArrayList<>();
        cities.add("Delhi");
        cities.add("Mumbai");
        cities.add("Bangalore");
        Collections.sort(cities);
        System.out.println("Sorted cities: " + cities);

        // āœ… Using Map — imported from java.util
        Map<String, Integer> population = new HashMap<>();
        population.put("Delhi",     32941000);
        population.put("Mumbai",    20667656);
        population.put("Bangalore", 13193000);
        population.forEach((city, pop) ->
            System.out.printf("%-12s: %,d%n", city, pop));

        // āœ… Using LocalDate — imported from java.time
        LocalDate today = LocalDate.now();
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MMMM yyyy");
        System.out.println("Today: " + today.format(fmt));

        // āœ… Using static import — PI, sqrt, pow without Math. prefix
        double radius = 7.5;
        double area        = PI * pow(radius, 2);
        double circumference = 2 * PI * radius;
        double diagonal    = sqrt(pow(radius, 2) + pow(radius, 2));
        System.out.printf("Circle r=%.1f: area=%.2f, circ=%.2f, diag=%.2f%n",
                          radius, area, circumference, diagonal);

        // āœ… Fully qualified name — no import needed
        java.util.Scanner sc = new java.util.Scanner(System.in);
        // Verbose but valid — used when import is omitted or ambiguous
        sc.close();

        // āœ… Name collision — both java.util.Date and java.sql.Date exist
        // If both are imported, use fully qualified names to resolve ambiguity:
        java.util.Date utilDate = new java.util.Date();
        java.sql.Date  sqlDate  = new java.sql.Date(System.currentTimeMillis());
        System.out.println("util.Date : " + utilDate);
        System.out.println("sql.Date  : " + sqlDate);
    }
}

Built-in Java API Packages — The Standard Library

The Java Standard Library ships with hundreds of packages containing thousands of classes. Knowing which packages exist and what they contain is a practical superpower — it prevents you from reinventing solutions that Java already provides. The table below covers the most important packages every Java developer must know.

PackagePurposeKey Classes / Interfaces
java.langFundamental classes — auto-importedString, Integer, Math, Object, System, StringBuilder, Thread, Exception, Runnable
java.utilData structures, collections, utilitiesArrayList, HashMap, HashSet, LinkedList, Collections, Arrays, Scanner, Optional, UUID
java.util.concurrentThread-safe collections & concurrency utilitiesConcurrentHashMap, ExecutorService, Executors, Future, CountDownLatch, Semaphore
java.util.functionFunctional interfaces (Java 8+)Function, Consumer, Supplier, Predicate, BiFunction, UnaryOperator
java.util.streamStream API for functional-style operations (Java 8+)Stream, Collectors, IntStream, LongStream, DoubleStream
java.ioByte/character I/O, serializationFile, InputStream, OutputStream, BufferedReader, PrintWriter, Serializable
java.nio.fileModern file system operations (Java 7+)Path, Paths, Files, StandardOpenOption, FileVisitor
java.netNetworking — URLs, sockets, HTTPURL, URI, Socket, ServerSocket, HttpURLConnection, InetAddress
java.mathArbitrary-precision arithmeticBigDecimal, BigInteger, MathContext, RoundingMode
java.timeModern date/time API (Java 8+, replaces java.util.Date)LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Duration, Period, DateTimeFormatter
java.sqlJDBC database connectivityConnection, Statement, PreparedStatement, ResultSet, DriverManager, Date, Timestamp
java.lang.reflectRuntime class inspection and manipulationClass, Method, Field, Constructor, Modifier
java.textText formatting — numbers, dates, messagesNumberFormat, DecimalFormat, SimpleDateFormat, MessageFormat
java.securityCryptography, authentication, authorizationMessageDigest, KeyPair, SecureRandom, Signature, Certificate

java.lang — The Automatically Imported Package

java.lang is the only Java package that is automatically imported into every Java source file. It contains the absolute bedrock of the Java language — classes so fundamental that Java programs cannot function without them. You use java.lang classes constantly without realizing it: every time you declare a String, call System.out.println(), use Math.abs(), catch a NullPointerException, or create a Thread.

ā˜• JavaJavaLangPackage.java
// No import needed — java.lang is auto-imported
public class JavaLangPackage {
    public static void main(String[] args) {

        // āœ… String — most used class in java.lang
        String name = "TechSustainify";
        System.out.println("Length    : " + name.length());            // 14
        System.out.println("Upper     : " + name.toUpperCase());        // TECHSUSTAINIFY
        System.out.println("Contains  : " + name.contains("Sustain")); // true
        System.out.println("Replace   : " + name.replace("Tech", "Pro")); // ProSustainify
        System.out.println("Substring : " + name.substring(4, 12));    // Sustaini
        System.out.println("Starts    : " + name.startsWith("Tech"));  // true
        System.out.println("Index     : " + name.indexOf("Sustain"));  // 4
        System.out.println("CharAt    : " + name.charAt(0));           // T

        // āœ… StringBuilder — mutable string (also java.lang)
        StringBuilder sb = new StringBuilder();
        sb.append("Java");
        sb.append(" ").append("Packages");
        sb.insert(0, "Learning ");
        sb.reverse();
        System.out.println("SB        : " + sb);       // segatkcaP avaJ gninraeL
        sb.reverse();                                     // Restore
        System.out.println("Restored  : " + sb);       // Learning Java Packages

        // āœ… Math — mathematical operations (java.lang)
        System.out.println("abs(-7)   : " + Math.abs(-7));              // 7
        System.out.println("max(9,3)  : " + Math.max(9, 3));            // 9
        System.out.println("min(9,3)  : " + Math.min(9, 3));            // 3
        System.out.println("pow(2,10) : " + (int) Math.pow(2, 10));     // 1024
        System.out.println("sqrt(144) : " + Math.sqrt(144));            // 12.0
        System.out.println("floor(3.9): " + Math.floor(3.9));           // 3.0
        System.out.println("ceil(3.1) : " + Math.ceil(3.1));            // 4.0
        System.out.println("round(3.5): " + Math.round(3.5));           // 4
        System.out.println("PI        : " + Math.PI);                   // 3.141592...
        System.out.println("random()  : " + Math.random());             // [0.0, 1.0)
        System.out.println("log(E)    : " + Math.log(Math.E));          // 1.0

        // āœ… Integer, Long, Double — Wrapper classes (java.lang)
        int parsed = Integer.parseInt("42");
        System.out.println("parseInt  : " + parsed);                    // 42
        System.out.println("MAX_VALUE : " + Integer.MAX_VALUE);         // 2147483647
        System.out.println("MIN_VALUE : " + Integer.MIN_VALUE);         // -2147483648
        System.out.println("toBinary  : " + Integer.toBinaryString(255)); // 11111111
        System.out.println("toHex     : " + Integer.toHexString(255));    // ff
        System.out.println("valueOf   : " + Integer.valueOf("100"));    // 100
        double d = Double.parseDouble("3.14");
        System.out.println("parseDouble: " + d);                        // 3.14
        System.out.println("isNaN     : " + Double.isNaN(0.0 / 0.0));   // true

        // āœ… System — system-level utilities (java.lang)
        long startTime = System.currentTimeMillis();
        long startNano = System.nanoTime();
        // ... do some work ...
        for (int i = 0; i < 1_000_000; i++) { }
        System.out.printf("Elapsed   : %d ms%n",
                          System.currentTimeMillis() - startTime);
        System.out.println("JVM arch  : " + System.getProperty("os.arch"));
        System.out.println("Java ver  : " + System.getProperty("java.version"));

        // āœ… Object — root of all Java classes (java.lang)
        Object obj = new Object();
        System.out.println("hashCode  : " + obj.hashCode());
        System.out.println("class     : " + obj.getClass().getName());  // java.lang.Object

        // āœ… Handling java.lang exceptions — no import needed
        try {
            int[] arr = new int[3];
            arr[5] = 10;  // Throws ArrayIndexOutOfBoundsException (java.lang)
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Caught    : " + e.getClass().getSimpleName());
        }
        try {
            String s = null;
            s.length();  // Throws NullPointerException (java.lang)
        } catch (NullPointerException e) {
            System.out.println("Caught    : " + e.getClass().getSimpleName());
        }
    }
}

java.util — Collections, Data Structures & Utilities

java.util is the second most important package in the Java API. It contains the entire Java Collections Framework (List, Set, Map, Queue and their implementations), utility classes for sorting, searching, and randomness, the Scanner for input, Optional for null-safety, and UUID for unique identifiers. If java.lang is Java's heartbeat, java.util is its muscle.

ā˜• JavaJavaUtilPackage.java
import java.util.*;
import java.util.stream.Collectors;

public class JavaUtilPackage {
    public static void main(String[] args) {

        // āœ… ArrayList — dynamic, ordered, allows duplicates
        List<String> languages = new ArrayList<>(List.of("Java", "Python", "Go", "Rust"));
        languages.add("Kotlin");
        languages.remove("Go");
        System.out.println("ArrayList     : " + languages);
        System.out.println("Contains Java : " + languages.contains("Java")); // true
        System.out.println("Size          : " + languages.size());            // 4

        // āœ… HashMap — key-value pairs, O(1) average lookup
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice",   95);
        scores.put("Bob",     82);
        scores.put("Charlie", 91);
        scores.put("Alice",   98); // Overwrites existing key
        System.out.println("\nHashMap       : " + scores);
        System.out.println("Alice's score : " + scores.get("Alice"));       // 98
        System.out.println("Bob exists    : " + scores.containsKey("Bob")); // true
        scores.getOrDefault("Diana", 0); // Returns 0 — Diana not present
        scores.forEach((name, score) ->
            System.out.printf("  %-10s: %d%n", name, score));

        // āœ… HashSet — unique elements, O(1) contains
        Set<String> uniqueTags = new HashSet<>(List.of("java", "oop", "java", "api"));
        System.out.println("\nHashSet (unique): " + uniqueTags); // no duplicates

        // āœ… LinkedList — doubly-linked, O(1) add/remove at ends
        Queue<String> taskQueue = new LinkedList<>();
        taskQueue.offer("Task A");
        taskQueue.offer("Task B");
        taskQueue.offer("Task C");
        System.out.println("\nQueue peek    : " + taskQueue.peek());  // Task A
        System.out.println("Queue poll    : " + taskQueue.poll());  // Task A (removed)
        System.out.println("Queue size    : " + taskQueue.size()); // 2

        // āœ… TreeMap — sorted by key, O(log n) operations
        Map<String, Integer> sorted = new TreeMap<>(scores);
        System.out.println("\nTreeMap (sorted): " + sorted);

        // āœ… Collections utility class
        List<Integer> nums = new ArrayList<>(List.of(5, 2, 8, 1, 9, 3));
        Collections.sort(nums);
        System.out.println("\nSorted        : " + nums);
        System.out.println("Max           : " + Collections.max(nums));  // 9
        System.out.println("Min           : " + Collections.min(nums));  // 1
        System.out.println("Frequency 5   : " + Collections.frequency(nums, 5)); // 1
        Collections.reverse(nums);
        System.out.println("Reversed      : " + nums);
        Collections.shuffle(nums, new Random(42)); // Reproducible shuffle
        System.out.println("Shuffled      : " + nums);

        // āœ… Arrays utility class
        int[] arr = {5, 2, 8, 1, 9, 3};
        Arrays.sort(arr);
        System.out.println("\nArrays.sort   : " + Arrays.toString(arr));
        System.out.println("binarySearch 8: " + Arrays.binarySearch(arr, 8)); // index 4
        int[] copy = Arrays.copyOf(arr, arr.length);
        System.out.println("Arrays.copyOf : " + Arrays.toString(copy));
        int[] filled = new int[5];
        Arrays.fill(filled, 7);
        System.out.println("Arrays.fill 7 : " + Arrays.toString(filled));

        // āœ… Optional — null-safe container (java.util, Java 8+)
        Optional<String> present = Optional.of("Hello");
        Optional<String> empty   = Optional.empty();
        System.out.println("\nOptional      : " + present.get());               // Hello
        System.out.println("OrElse        : " + empty.orElse("Default"));       // Default
        System.out.println("IsPresent     : " + present.isPresent());           // true
        present.ifPresent(v -> System.out.println("Value         : " + v));     // Hello

        // āœ… UUID — universally unique identifier
        UUID id = UUID.randomUUID();
        System.out.println("\nUUID          : " + id); // e.g. 550e8400-e29b-41d4-a716-446655440000

        // āœ… Scanner — reading input (java.util)
        // Scanner sc = new Scanner(System.in);
        // System.out.print("Enter name: ");
        // String input = sc.nextLine();
        // sc.close();
    }
}

java.io & java.nio — Input/Output Operations

java.io is the original Java I/O package — it provides stream-based I/O for reading and writing bytes and characters, working with files, and Java object serialization. java.nio (New I/O, introduced in Java 1.4) and its sub-package java.nio.file (Java 7) provide a modern, more powerful file API through Path, Files, and Paths. For new code, always prefer java.nio.file over java.io.File.

ā˜• JavaJavaIOPackage.java
import java.io.*;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class JavaIOPackage {
    public static void main(String[] args) throws IOException {

        // āœ… java.nio.file.Path & Files — modern API (preferred)
        Path filePath = Paths.get("demo_output.txt");

        // Write lines to file
        List<String> lines = List.of(
            "Java Packages and API",
            "Learning java.io and java.nio",
            "TechSustainify Tutorial 2026"
        );
        Files.write(filePath, lines, StandardCharsets.UTF_8);
        System.out.println("āœ… File written: " + filePath.toAbsolutePath());

        // Read all lines from file
        List<String> readBack = Files.readAllLines(filePath, StandardCharsets.UTF_8);
        System.out.println("Lines read: " + readBack.size());
        readBack.forEach(line -> System.out.println("  > " + line));

        // Check file properties using Files
        System.out.println("Exists  : " + Files.exists(filePath));
        System.out.println("Size    : " + Files.size(filePath) + " bytes");
        System.out.println("Readable: " + Files.isReadable(filePath));

        // Append to file
        Files.writeString(filePath, "\nAppended line",
                          StandardOpenOption.APPEND);
        System.out.println("āœ… Appended to file");

        // Read entire file as string
        String content = Files.readString(filePath, StandardCharsets.UTF_8);
        System.out.println("Full content:\n" + content);

        // Copy, move, delete
        Path copyPath = Paths.get("demo_copy.txt");
        Files.copy(filePath, copyPath, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("āœ… File copied to: " + copyPath);
        Files.delete(copyPath);
        System.out.println("āœ… Copy deleted");
        Files.delete(filePath);
        System.out.println("āœ… Original deleted");

        // āœ… java.io.BufferedReader — character-based streaming read
        String csvData = "Alice,30,HR\nBob,25,IT\nCarol,28,Finance";
        try (BufferedReader br = new BufferedReader(
                new StringReader(csvData))) {  // StringReader for in-memory demo
            String line;
            System.out.println("\nBufferedReader:");
            while ((line = br.readLine()) != null) {
                String[] parts = line.split(",");
                System.out.printf("  Name: %-8s Age: %s Dept: %s%n",
                                  parts[0], parts[1], parts[2]);
            }
        } // Auto-closes BufferedReader — try-with-resources

        // āœ… java.io.PrintWriter — formatted text output
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            pw.printf("%-10s %5s %10s%n", "Name", "Age", "Department");
            pw.printf("%-10s %5d %10s%n", "Alice", 30, "HR");
            pw.printf("%-10s %5d %10s%n", "Bob",   25, "IT");
        }
        System.out.println("\nPrintWriter output:\n" + sw);

        // āœ… Path operations
        Path base = Paths.get("/home/user/projects/java");
        System.out.println("\nPath operations:");
        System.out.println("  FileName  : " + base.getFileName()); // java
        System.out.println("  Parent    : " + base.getParent());   // /home/user/projects
        System.out.println("  NameCount : " + base.getNameCount()); // 4
        System.out.println("  Name(0)   : " + base.getName(0));    // home
    }
}

java.math — Arbitrary-Precision Arithmetic

java.math provides two critical classes for situations where primitive double and long are simply not precise enough: BigDecimal for exact decimal arithmetic (financial calculations, tax, currency) and BigInteger for arbitrarily large integers (cryptography, factorial of large numbers, number theory). These classes are slower than primitives but completely accurate — never use double for money calculations.

ā˜• JavaJavaMathPackage.java
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;

public class JavaMathPackage {
    public static void main(String[] args) {

        // āŒ Why double fails for money — classic precision bug
        double d1 = 0.1 + 0.2;
        System.out.println("double 0.1+0.2 = " + d1);         // 0.30000000000000004 āŒ
        System.out.println("double 1.03-0.42 = " + (1.03-0.42)); // 0.6100000000000001 āŒ

        // āœ… BigDecimal — exact decimal arithmetic
        BigDecimal b1 = new BigDecimal("0.1");   // ALWAYS use String constructor
        BigDecimal b2 = new BigDecimal("0.2");
        System.out.println("BigDecimal 0.1+0.2 = " + b1.add(b2));         // 0.3 āœ…

        // āœ… Financial calculation — GST example
        BigDecimal price   = new BigDecimal("1499.00");
        BigDecimal gstRate = new BigDecimal("0.18");  // 18% GST
        BigDecimal gst     = price.multiply(gstRate).setScale(2, RoundingMode.HALF_UP);
        BigDecimal total   = price.add(gst);
        System.out.println("\n--- GST Calculation ---");
        System.out.println("Base Price  : ₹" + price);   // 1499.00
        System.out.println("GST (18%)   : ₹" + gst);     // 269.82
        System.out.println("Total       : ₹" + total);   // 1768.82

        // āœ… BigDecimal arithmetic operations
        BigDecimal a = new BigDecimal("100.50");
        BigDecimal b = new BigDecimal("33.25");
        System.out.println("\nBigDecimal ops:");
        System.out.println("  add      : " + a.add(b));                                  // 133.75
        System.out.println("  subtract : " + a.subtract(b));                            // 67.25
        System.out.println("  multiply : " + a.multiply(b));                            // 3341.6250
        System.out.println("  divide   : " + a.divide(b, 4, RoundingMode.HALF_UP));     // 3.0226
        System.out.println("  compareTo: " + a.compareTo(b));                           // 1 (a > b)
        System.out.println("  abs      : " + new BigDecimal("-50.5").abs());           // 50.5
        System.out.println("  scale    : " + a.scale());                               // 2
        System.out.println("  precision: " + a.precision());                           // 5

        // āœ… BigDecimal — always use HALF_UP for rounding money
        BigDecimal val = new BigDecimal("2.345");
        System.out.println("  HALF_UP  : " + val.setScale(2, RoundingMode.HALF_UP));   // 2.35
        System.out.println("  HALF_DOWN: " + val.setScale(2, RoundingMode.HALF_DOWN)); // 2.34
        System.out.println("  FLOOR    : " + val.setScale(2, RoundingMode.FLOOR));     // 2.34
        System.out.println("  CEILING  : " + val.setScale(2, RoundingMode.CEILING));   // 2.35

        // āœ… BigInteger — arbitrarily large integers
        BigInteger big1 = new BigInteger("99999999999999999999999999999");
        BigInteger big2 = new BigInteger("11111111111111111111111111111");
        System.out.println("\n--- BigInteger ---");
        System.out.println("  add      : " + big1.add(big2));
        System.out.println("  multiply : " + big1.multiply(big2));
        System.out.println("  isPrime  : " + big1.isProbablePrime(50));

        // āœ… Factorial of 50 — impossible with long, trivial with BigInteger
        BigInteger factorial = BigInteger.ONE;
        for (int i = 2; i <= 50; i++) {
            factorial = factorial.multiply(BigInteger.valueOf(i));
        }
        System.out.println("  50!      : " + factorial);
        // 30414093201713378043612608166979581188299763898377856200000000000

        // āœ… MathContext — precision control for BigDecimal division
        BigDecimal x = new BigDecimal("10");
        BigDecimal y = new BigDecimal("3");
        BigDecimal result = x.divide(y, new MathContext(10));
        System.out.println("  10/3 (10 sig digits): " + result); // 3.333333333
    }
}

java.time — The Modern Date & Time API

The java.time package (introduced in Java 8, JSR-310) is the definitive replacement for the legacy java.util.Date and Calendar classes, which were notoriously confusing, mutable, and poorly designed. java.time provides an immutable, thread-safe, and richly expressive API for dates, times, durations, periods, and time zones. In all new Java code, always use java.time — never java.util.Date or java.util.Calendar.

ā˜• JavaJavaTimePackage.java
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class JavaTimePackage {
    public static void main(String[] args) {

        // āœ… LocalDate — date without time or timezone
        LocalDate today     = LocalDate.now();
        LocalDate birthday  = LocalDate.of(1995, Month.JULY, 15);
        LocalDate nextMonth = today.plusMonths(1);
        LocalDate lastYear  = today.minusYears(1);
        System.out.println("Today         : " + today);
        System.out.println("Birthday      : " + birthday);
        System.out.println("Next month    : " + nextMonth);
        System.out.println("Last year     : " + lastYear);
        System.out.println("Day of week   : " + today.getDayOfWeek());   // e.g. MONDAY
        System.out.println("Day of year   : " + today.getDayOfYear());   // 1-366
        System.out.println("Is leap year  : " + today.isLeapYear());
        System.out.println("Before birthday: " + today.isBefore(birthday));

        // āœ… LocalTime — time without date or timezone
        LocalTime now          = LocalTime.now();
        LocalTime meetingTime  = LocalTime.of(14, 30, 0); // 2:30 PM
        LocalTime afterTwoHrs  = meetingTime.plusHours(2);
        System.out.println("\nCurrent time  : " + now);
        System.out.println("Meeting       : " + meetingTime); // 14:30
        System.out.println("+2 hours      : " + afterTwoHrs); // 16:30
        System.out.println("Hour          : " + now.getHour());
        System.out.println("Minute        : " + now.getMinute());

        // āœ… LocalDateTime — date + time, no timezone
        LocalDateTime dateTime   = LocalDateTime.now();
        LocalDateTime deadline   = LocalDateTime.of(2026, 12, 31, 23, 59, 59);
        System.out.println("\nDateTime      : " + dateTime);
        System.out.println("Deadline      : " + deadline);
        System.out.println("Before deadline: " + dateTime.isBefore(deadline));

        // āœ… ZonedDateTime — with timezone
        ZonedDateTime istNow = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
        ZonedDateTime utcNow = ZonedDateTime.now(ZoneId.of("UTC"));
        ZonedDateTime nyNow  = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("\nIST           : " + istNow);
        System.out.println("UTC           : " + utcNow);
        System.out.println("New York      : " + nyNow);

        // āœ… Duration — time-based amount (hours, minutes, seconds)
        LocalTime start = LocalTime.of(9, 0);
        LocalTime end   = LocalTime.of(17, 30);
        Duration workDay = Duration.between(start, end);
        System.out.println("\nWork duration : " + workDay);          // PT8H30M
        System.out.println("Hours         : " + workDay.toHours()); // 8
        System.out.println("Minutes total : " + workDay.toMinutes()); // 510

        // āœ… Period — date-based amount (years, months, days)
        LocalDate dob    = LocalDate.of(1995, 7, 15);
        LocalDate refDay = LocalDate.of(2026, 3, 20);
        Period age = Period.between(dob, refDay);
        System.out.println("\nAge           : " + age.getYears() + " years, "
                           + age.getMonths() + " months, "
                           + age.getDays() + " days");
        long daysOld = ChronoUnit.DAYS.between(dob, refDay);
        System.out.println("Days old      : " + daysOld);

        // āœ… DateTimeFormatter — formatting and parsing
        DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("dd MMMM yyyy, hh:mm a");
        DateTimeFormatter iso  = DateTimeFormatter.ISO_LOCAL_DATE;
        System.out.println("\nFormatted     : " + today.format(fmt1)); // 20/03/2026
        System.out.println("Formatted     : " + dateTime.format(fmt2));
        System.out.println("ISO format    : " + today.format(iso));    // 2026-03-20

        // āœ… Parsing a date string
        LocalDate parsed = LocalDate.parse("25/12/2025",
                               DateTimeFormatter.ofPattern("dd/MM/yyyy"));
        System.out.println("Parsed date   : " + parsed);              // 2025-12-25
    }
}

User-Defined Packages — Structuring Your Own Code

Beyond using the Java API, you will create your own packages to organize your application's source code. A well-designed package structure reflects your application's architecture — it groups classes by their responsibility and layer, makes navigation intuitive, and controls what is visible across package boundaries.

šŸ—ļø
Common Package Structures in Production

Layer-based (traditional): com.company.app.model, com.company.app.service, com.company.app.repository, com.company.app.controller, com.company.app.util, com.company.app.exception. Feature-based (modern, preferred for large systems): com.company.app.employee, com.company.app.payroll, com.company.app.reporting — each feature package contains its own model, service, repository, and controller. The feature-based structure is favored in microservices and domain-driven design because each feature's code is co-located and can be independently extracted into a separate service.

šŸ“¦
Sub-Packages — Not Inherited

Java packages are NOT hierarchical in terms of access — a sub-package does NOT have special access to its parent package. com.example.app and com.example.app.service are completely separate, independent packages. Package-private members in com.example.app are NOT visible in com.example.app.service — only public and protected members (with inheritance) are accessible. This surprises many beginners who assume sub-packages can access parent package internals. Each package is a completely isolated namespace — only the naming convention (dots) creates the visual hierarchy; there is no actual inheritance of access rights.

šŸ”§
Compiling and Running with Packages

Compiling: javac -d out src/com/techsustainify/model/Employee.java — the -d flag specifies the output directory root; javac creates the matching subdirectory structure automatically. Running: java -cp out com.techsustainify.Main — the fully qualified class name (package + class) is required. With multiple source files: javac -d out src/**/*.java or use a build tool (Maven, Gradle) which handles compilation, packaging, and dependency management automatically. In practice, you will almost always use a build tool — manual javac commands are for learning purposes only.

ā˜• JavaUserDefinedPackages — Full Project Structure
// ── Project structure ──────────────────────────
// src/
//   com/techsustainify/
//     model/
//       Product.java
//     service/
//       ProductService.java
//     repository/
//       ProductRepository.java
//     exception/
//       ProductNotFoundException.java
//     Main.java
// ───────────────────────────────────────────────

// āœ… FILE: com/techsustainify/model/Product.java
package com.techsustainify.model;

import java.math.BigDecimal;
import java.util.Objects;

public class Product {
    private final String  id;
    private final String  name;
    private final BigDecimal price;
    private       int     stock;

    public Product(String id, String name, BigDecimal price, int stock) {
        this.id    = Objects.requireNonNull(id,    "id must not be null");
        this.name  = Objects.requireNonNull(name,  "name must not be null");
        this.price = Objects.requireNonNull(price, "price must not be null");
        this.stock = stock;
    }

    public String     getId()    { return id; }
    public String     getName()  { return name; }
    public BigDecimal getPrice() { return price; }
    public int        getStock() { return stock; }
    public void       setStock(int s) { this.stock = s; }

    @Override public String toString() {
        return String.format("Product{id='%s', name='%s', price=%s, stock=%d}",
                             id, name, price, stock);
    }
}

// āœ… FILE: com/techsustainify/exception/ProductNotFoundException.java
package com.techsustainify.exception;

public class ProductNotFoundException extends RuntimeException {
    public ProductNotFoundException(String productId) {
        super("Product not found: " + productId);
    }
}

// āœ… FILE: com/techsustainify/repository/ProductRepository.java
package com.techsustainify.repository;

import com.techsustainify.model.Product;
import com.techsustainify.exception.ProductNotFoundException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class ProductRepository {
    private final Map<String, Product> store = new HashMap<>();

    public void save(Product p)  { store.put(p.getId(), p); }

    public Product findById(String id) {
        Product p = store.get(id);
        if (p == null) throw new ProductNotFoundException(id);
        return p;
    }

    public Collection<Product> findAll() { return store.values(); }
    public boolean exists(String id)    { return store.containsKey(id); }
    public void delete(String id)       { store.remove(id); }
}

// āœ… FILE: com/techsustainify/service/ProductService.java
package com.techsustainify.service;

import com.techsustainify.model.Product;
import com.techsustainify.repository.ProductRepository;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.UUID;

public class ProductService {
    private final ProductRepository repo = new ProductRepository();

    public Product addProduct(String name, BigDecimal price, int stock) {
        String  id = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
        Product p  = new Product(id, name, price, stock);
        repo.save(p);
        System.out.println("  Added  : " + p);
        return p;
    }

    public Product getProduct(String id) { return repo.findById(id); }
    public Collection<Product> getAllProducts() { return repo.findAll(); }

    public void updateStock(String id, int delta) {
        Product p = repo.findById(id);
        p.setStock(p.getStock() + delta);
        System.out.println("  Updated: " + p);
    }
}

// āœ… FILE: com/techsustainify/Main.java
package com.techsustainify;

import com.techsustainify.service.ProductService;
import com.techsustainify.model.Product;
import com.techsustainify.exception.ProductNotFoundException;
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        ProductService svc = new ProductService();

        Product laptop = svc.addProduct("Laptop Pro", new BigDecimal("89999.00"), 10);
        Product phone  = svc.addProduct("SmartPhone X", new BigDecimal("29999.00"), 25);
        svc.addProduct("Wireless Mouse", new BigDecimal("999.00"), 50);

        System.out.println("\nAll Products:");
        svc.getAllProducts().forEach(p -> System.out.println("  " + p));

        System.out.println("\nUpdate stock:");
        svc.updateStock(laptop.getId(), -2); // Sold 2 laptops

        System.out.println("\nGet specific product:");
        Product found = svc.getProduct(phone.getId());
        System.out.println("  Found: " + found.getName());

        System.out.println("\nTry non-existent product:");
        try {
            svc.getProduct("INVALID_ID");
        } catch (ProductNotFoundException e) {
            System.out.println("  Error: " + e.getMessage());
        }
    }
}

Access Modifiers & Package Visibility

Java's four access modifiers work in direct conjunction with packages. Understanding their interaction is essential for designing clean APIs and enforcing encapsulation. The package-private level (no modifier) is one of Java's most underused and most powerful encapsulation tools.

ModifierSame ClassSame PackageSubclass (diff pkg)Anywhere
publicāœ… Yesāœ… Yesāœ… Yesāœ… Yes — visible everywhere
protectedāœ… Yesāœ… Yesāœ… YesāŒ No — only via inheritance
(no modifier) package-privateāœ… Yesāœ… YesāŒ NoāŒ No — package only
privateāœ… YesāŒ NoāŒ NoāŒ No — class only
ā˜• JavaAccessModifiersPackages.java
// āœ… FILE: com/techsustainify/engine/QueryEngine.java
package com.techsustainify.engine;

public class QueryEngine {

    // public — visible to ALL packages
    public String executeQuery(String sql) {
        validate(sql);
        String plan = buildPlan(sql);
        return runPlan(plan);
    }

    // package-private — visible ONLY inside com.techsustainify.engine
    // Other packages cannot call this — it is an internal implementation detail
    String buildPlan(String sql) {
        return "PLAN[" + sql.toUpperCase() + "]";
    }

    // package-private — internal utility
    boolean validate(String sql) {
        if (sql == null || sql.isBlank()) {
            throw new IllegalArgumentException("SQL cannot be blank");
        }
        return true;
    }

    // private — visible only inside THIS class
    private String runPlan(String plan) {
        return "RESULT of " + plan;
    }
}

// āœ… FILE: com/techsustainify/engine/QueryOptimizer.java
// This is in the SAME package — can access package-private members
package com.techsustainify.engine;

public class QueryOptimizer {
    private final QueryEngine engine = new QueryEngine();

    public String optimizeAndExecute(String sql) {
        // āœ… Can access buildPlan() — same package
        String plan = engine.buildPlan(sql);
        System.out.println("Optimizing plan: " + plan);
        return engine.executeQuery(sql);
    }
}

// āœ… FILE: com/techsustainify/api/QueryController.java
// DIFFERENT package — can only access public members
package com.techsustainify.api;

import com.techsustainify.engine.QueryEngine;

public class QueryController {
    private final QueryEngine engine = new QueryEngine();

    public String handleQuery(String sql) {
        // āœ… CAN call executeQuery() — it is public
        return engine.executeQuery(sql);
        // āŒ CANNOT call engine.buildPlan() — package-private, different package
        // āŒ CANNOT call engine.validate() — package-private, different package
        // Compile error: buildPlan() is not public in QueryEngine;
        //                cannot be accessed from outside package
    }
}

Static Import — Using Static Members Without Class Prefix

Static import (introduced in Java 5) allows you to import static methods and fields directly, so they can be used without qualifying them with the class name. It is most useful for heavily used constants like Math.PI, test assertions like Assertions.assertEquals, and factory methods. Use it judiciously — overuse makes code confusing because readers cannot tell which class a static member belongs to.

ā˜• JavaStaticImport.java
package com.techsustainify.demo;

// āœ… Static import — import specific static members
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.Math.pow;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;

// āœ… Static import of constants from a custom class
import static com.techsustainify.demo.AppConstants.MAX_RETRIES;
import static com.techsustainify.demo.AppConstants.DEFAULT_TIMEOUT_MS;
import static com.techsustainify.demo.AppConstants.APP_NAME;

// āœ… Wildcard static import — imports ALL static members
// import static java.lang.Math.*;  // Use with care — reduces clarity

public class StaticImport {

    public static void main(String[] args) {

        // āœ… Using Math constants/methods WITHOUT Math. prefix
        double radius = 5.0;
        double area   = PI * pow(radius, 2);       // No Math.PI, Math.pow
        double circum = 2 * PI * radius;
        double hyp    = sqrt(pow(3, 2) + pow(4, 2)); // 5.0
        System.out.printf("Circle r=%.1f: area=%.2f, circumference=%.2f%n",
                          radius, area, circum);
        System.out.println("Hypotenuse: " + hyp);
        System.out.println("abs(-42)  : " + abs(-42));  // 42
        System.out.println("max(9,4)  : " + max(9, 4)); // 9
        System.out.println("min(9,4)  : " + min(9, 4)); // 4

        // āœ… Using custom constants directly
        System.out.println("\nApp: " + APP_NAME);
        System.out.println("Max retries : " + MAX_RETRIES);
        System.out.println("Timeout(ms) : " + DEFAULT_TIMEOUT_MS);

        // āœ… When static import helps vs hurts readability
        // GOOD: abs(x) is universally understood
        // GOOD: PI is universally understood
        // BAD:  processOrder(id) — which class does processOrder come from?
        // Rule: if the static member's name is self-documenting and globally
        //       recognizable, static import is fine. Otherwise, keep the class name.
    }
}

// āœ… AppConstants.java — same package
// package com.techsustainify.demo;
// public class AppConstants {
//     public static final int    MAX_RETRIES        = 3;
//     public static final long   DEFAULT_TIMEOUT_MS = 5000L;
//     public static final String APP_NAME           = "TechSustainify App";
//     private AppConstants() { /* prevent instantiation */ }
// }

Common Mistakes & Pitfalls

These are the most common package and import mistakes in Java — from directory-package mismatches and forgotten imports, to the classic double precision bug that makes java.math.BigDecimal essential, to the fall-through of sub-package access rules.

ā˜• JavaPackageMistakes.java
// āŒ MISTAKE 1: Package declaration does not match directory
// File saved at: src/com/example/Employee.java
// But declares:  package com.example.model;
// Compiler error: class Employee is public, should be in file Employee.java
// inside directory com/example/model/
// āœ… Fix: either move file to com/example/model/ or change package to com.example

// āŒ MISTAKE 2: Placing package declaration after import
// import java.util.List;
// package com.example;           // āŒ COMPILE ERROR — package must be FIRST
// public class MyClass { }
// āœ… Fix: package always comes before imports and class

// āŒ MISTAKE 3: Assuming wildcard import covers sub-packages
import java.util.*;
// java.util.* does NOT import java.util.concurrent.* or java.util.function.*
// āŒ ExecutorService executors = ...; // COMPILE ERROR — not in java.util.*
// āœ… Fix: explicitly import java.util.concurrent.ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// āŒ MISTAKE 4: Assuming sub-package inherits parent package access
// com.example.service.UserService CANNOT access package-private members
// of com.example — they are SEPARATE packages despite the naming
// āœ… Fix: make the member public, or move the class into the same package

// āŒ MISTAKE 5: Using double for money — precision bug
double price1 = 19.99;
double tax1   = 0.18;
double total1 = price1 * (1 + tax1);
System.out.println("double total: " + total1); // 23.588200000000003 — WRONG
// āœ… Fix: use BigDecimal for ALL financial arithmetic
import java.math.BigDecimal;
import java.math.RoundingMode;
BigDecimal price2 = new BigDecimal("19.99");
BigDecimal tax2   = new BigDecimal("0.18");
BigDecimal total2 = price2.multiply(BigDecimal.ONE.add(tax2))
                         .setScale(2, RoundingMode.HALF_UP);
System.out.println("BigDecimal total: " + total2); // 23.59 — CORRECT

// āŒ MISTAKE 6: new BigDecimal(0.1) — still uses double imprecision!
BigDecimal wrong = new BigDecimal(0.1);  // āŒ Same floating-point imprecision
System.out.println("BD from double: " + wrong);
// 0.1000000000000000055511151231257827021181583404541015625 — WRONG
BigDecimal right = new BigDecimal("0.1"); // āœ… Always use String constructor
System.out.println("BD from String: " + right); // 0.1 — CORRECT

// āŒ MISTAKE 7: Using java.util.Date for new code
// java.util.Date is mutable, poorly designed, and deprecated in spirit.
// āœ… Fix: always use java.time.LocalDate / LocalDateTime / ZonedDateTime
import java.time.LocalDate;
LocalDate today = LocalDate.now(); // āœ… Immutable, thread-safe, expressive

// āŒ MISTAKE 8: Import ambiguity — both java.util.Date and java.sql.Date
// import java.util.*;
// import java.sql.*;
// Date d = new Date(); // āŒ COMPILE ERROR: reference to Date is ambiguous
// āœ… Fix: use fully qualified names for ambiguous types
java.util.Date utilDate = new java.util.Date();
java.sql.Date  sqlDate  = new java.sql.Date(System.currentTimeMillis());

Bad Practices & Anti-Patterns — What Senior Developers Reject

These anti-patterns represent common misuses of packages and the Java API in professional code. Each one either causes maintenance problems, correctness bugs, or signals poor understanding of Java's design.

🚫
Putting Everything in the Default Package

Placing all classes in the unnamed default package (no package declaration) is fine for tiny experiments but unacceptable in any real project. It makes classes impossible to import from other packages, prevents proper access control, causes IDE navigation chaos, and signals no understanding of Java project structure. Every production Java class must be in a named package following the reverse domain convention. Build tools like Maven and Gradle will also not package default-package classes correctly in JARs.

🚫
Wildcard Imports in Production Code

import java.util.*; is convenient during development but is rejected in most professional codebases and style guides (Google Java Style: specific imports only). Wildcard imports hide which classes are actually used, can cause silent ambiguity when two packages share a class name, and make code navigation harder — you cannot Ctrl+Click an import to jump to the class. Enable 'Optimize Imports' in your IDE to auto-manage specific imports. The one exception: test code with import static org.junit.jupiter.api.Assertions.* is widely accepted because all assertions clearly belong to JUnit.

🚫
Using double/float for Financial Calculations

Using primitive double or float for any monetary amount — price, tax, discount, interest rate, total — is a critical correctness bug, not just a code smell. IEEE 754 floating-point arithmetic cannot represent most decimal fractions exactly: 0.1 + 0.2 == 0.3 is FALSE in Java. For all financial and currency arithmetic, use java.math.BigDecimal with String constructor and explicit RoundingMode. ALWAYS: new BigDecimal("19.99") — NEVER: new BigDecimal(19.99). Passing a double to BigDecimal's double constructor defeats the purpose entirely.

🚫
Using java.util.Date or Calendar in New Code

java.util.Date and java.util.Calendar are deprecated in spirit — they are mutable, poorly designed, non-thread-safe, and confusing (months in Calendar are 0-based). Since Java 8, the java.time package provides everything you need — immutably, thread-safely, and expressively. New code should use LocalDate, LocalDateTime, ZonedDateTime, Duration, Period, and DateTimeFormatter exclusively. If you encounter a legacy API requiring java.util.Date, use the conversion methods: Date.from(instant) and date.toInstant(). Code reviews should reject any new java.util.Date usage.

🚫
God Package — Dumping All Classes in One Package

Putting all classes in a single package (e.g., com.company.app with 100+ classes) eliminates the organizational and access-control benefits of packages entirely. It is the package equivalent of a 10,000-line God class. Split by layer (model, service, repository, controller) or by feature (user, product, order, payment). A good rule of thumb: if a package contains more than 15-20 classes, consider splitting it. Package structure should reflect your architecture — if your architecture is well-designed, your package structure will be naturally modular.

🚫
Overusing Static Import

Static import of non-obvious static members makes code cryptic. When a reader sees processOrder(id) in your code, they have no idea which class it belongs to — they must search the imports or hover with an IDE. This slows comprehension and makes bugs harder to spot. Reserve static import for: universally known constants (PI, E, MAX_VALUE), test assertion methods (assertEquals, assertTrue in JUnit files), and factory/builder methods where the class name is obvious from context. Never static-import business logic methods or domain-specific utility methods.

Real-World Production Code Examples — Packages & API in Context

The following examples show idiomatic, production-grade Java package and API usage — patterns seen in real Spring Boot services, financial applications, and enterprise Java codebases.

ā˜• JavaInvoiceService.java — Financial Calculation with java.math & java.time
package com.techsustainify.billing;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

public class InvoiceService {

    private static final BigDecimal GST_RATE  = new BigDecimal("0.18");
    private static final DateTimeFormatter FMT =
            DateTimeFormatter.ofPattern("dd MMM yyyy");

    public record LineItem(String description, int qty, BigDecimal unitPrice) {
        public BigDecimal subtotal() {
            return unitPrice.multiply(BigDecimal.valueOf(qty))
                           .setScale(2, RoundingMode.HALF_UP);
        }
    }

    public record Invoice(String invoiceNo, String customer,
                          LocalDate issueDate, LocalDate dueDate,
                          List<LineItem> items, BigDecimal subtotal,
                          BigDecimal gst, BigDecimal total) {}

    public Invoice generateInvoice(String customer, List<LineItem> items) {
        String    invoiceNo = "INV-" + UUID.randomUUID().toString()
                                            .substring(0, 8).toUpperCase();
        LocalDate issueDate = LocalDate.now();
        LocalDate dueDate   = issueDate.plusDays(30);

        // BigDecimal accumulation — never double
        BigDecimal subtotal = items.stream()
            .map(LineItem::subtotal)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal gst   = subtotal.multiply(GST_RATE)
                                   .setScale(2, RoundingMode.HALF_UP);
        BigDecimal total = subtotal.add(gst);

        return new Invoice(invoiceNo, customer, issueDate, dueDate,
                           Collections.unmodifiableList(items),
                           subtotal, gst, total);
    }

    public void printInvoice(Invoice inv) {
        System.out.println("╔══════════════════════════════════════════════╗");
        System.out.printf( "ā•‘  INVOICE: %-35sā•‘%n", inv.invoiceNo());
        System.out.printf( "ā•‘  Customer: %-34sā•‘%n", inv.customer());
        System.out.printf( "ā•‘  Issued  : %-34sā•‘%n", inv.issueDate().format(FMT));
        System.out.printf( "ā•‘  Due     : %-34sā•‘%n", inv.dueDate().format(FMT));
        System.out.println("╠══════════════════════════════════════════════╣");
        System.out.printf( "ā•‘  %-25s %5s %12sā•‘%n", "Description", "Qty", "Amount");
        System.out.println("║──────────────────────────────────────────────║");
        for (LineItem item : inv.items()) {
            System.out.printf("ā•‘  %-25s %5d %11s ā•‘%n",
                item.description(), item.qty(), "₹" + item.subtotal());
        }
        System.out.println("╠══════════════════════════════════════════════╣");
        System.out.printf( "ā•‘  %-33s ₹%9sā•‘%n", "Subtotal",    inv.subtotal());
        System.out.printf( "ā•‘  %-33s ₹%9sā•‘%n", "GST (18%)",   inv.gst());
        System.out.printf( "ā•‘  %-33s ₹%9sā•‘%n", "TOTAL",       inv.total());
        System.out.println("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•");
    }

    public static void main(String[] args) {
        InvoiceService svc = new InvoiceService();
        List<LineItem> items = List.of(
            new LineItem("Java Course (Annual)",   1, new BigDecimal("4999.00")),
            new LineItem("Tech eBooks Bundle",      2, new BigDecimal("799.00")),
            new LineItem("Premium Support (3 mo)", 1, new BigDecimal("1499.00"))
        );
        Invoice inv = svc.generateInvoice("Rahul Sharma", items);
        svc.printInvoice(inv);
    }
}
ā˜• JavaUserActivityLogger.java — java.time & java.util in Production
package com.techsustainify.audit;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class UserActivityLogger {

    private static final DateTimeFormatter LOG_FMT =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public record ActivityEvent(String userId, String action,
                                ZonedDateTime timestamp, Map<String, String> metadata) {
        @Override public String toString() {
            return String.format("[%s] USER=%s ACTION=%s META=%s",
                timestamp.withZoneSameInstant(ZoneId.of("Asia/Kolkata"))
                         .format(LOG_FMT),
                userId, action, metadata);
        }
    }

    // ConcurrentHashMap from java.util.concurrent — thread-safe
    private final Map<String, List<ActivityEvent>> eventStore =
            new ConcurrentHashMap<>();

    public void log(String userId, String action, Map<String, String> meta) {
        ActivityEvent event = new ActivityEvent(
            userId, action,
            ZonedDateTime.now(ZoneId.of("Asia/Kolkata")),
            Collections.unmodifiableMap(meta)
        );
        eventStore.computeIfAbsent(userId, k -> Collections.synchronizedList(
                       new ArrayList<>())).add(event);
        System.out.println("  Logged: " + event);
    }

    public List<ActivityEvent> getRecentActivity(String userId, int lastNMinutes) {
        ZonedDateTime cutoff = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"))
                                            .minus(lastNMinutes, ChronoUnit.MINUTES);
        return eventStore.getOrDefault(userId, Collections.emptyList())
                         .stream()
                         .filter(e -> e.timestamp().isAfter(cutoff))
                         .collect(Collectors.toList());
    }

    public Map<String, Long> getActionSummary(String userId) {
        return eventStore.getOrDefault(userId, Collections.emptyList())
                         .stream()
                         .collect(Collectors.groupingBy(
                             ActivityEvent::action,
                             Collectors.counting()));
    }

    public static void main(String[] args) {
        UserActivityLogger logger = new UserActivityLogger();

        logger.log("user_101", "LOGIN",    Map.of("ip", "192.168.1.10", "device", "Chrome"));
        logger.log("user_101", "VIEW_PAGE", Map.of("page", "/dashboard"));
        logger.log("user_101", "PURCHASE",  Map.of("item", "Java Course", "amount", "4999"));
        logger.log("user_101", "VIEW_PAGE", Map.of("page", "/profile"));
        logger.log("user_102", "LOGIN",     Map.of("ip", "10.0.0.5", "device", "Firefox"));
        logger.log("user_101", "LOGOUT",    Map.of("session_duration", "45min"));

        System.out.println("\nAction summary for user_101:");
        logger.getActionSummary("user_101").forEach((action, count) ->
            System.out.printf("  %-12s: %d time(s)%n", action, count));

        System.out.println("\nRecent activity (last 60 min):");
        logger.getRecentActivity("user_101", 60).forEach(
            e -> System.out.println("  " + e.action() + " @ " +
                 e.timestamp().format(DateTimeFormatter.ofPattern("HH:mm:ss"))));
    }
}

Package Structure Flow Diagram

This diagram shows how a Java source file's package declaration, import statements, and class definition relate to each other, and how the Java compiler resolves class references using packages and the classpath.

šŸ“„ Java Source File.java file on disk
Parse
šŸ“¦ package statementFIRST line — declares this class's package
Register package
šŸ“„ import statementsBring in types from other packages by simple name
Register imports
šŸ—ļø class declarationThe actual class/interface/enum/record
Type resolution
šŸ” Compiler resolves typesLooks up imports + classpath + java.lang auto
All types resolved
āœ… Compiles to .classBytecode stored in matching directory
āŒ Compile ErrorCannot find symbol / package mismatch

Code Execution Flow — from source to output

Java Packages & API Interview Questions — Beginner to Advanced

Package and API questions test your understanding of Java's namespace system, visibility rules, the standard library, and practical use of key API classes. These appear consistently in Java interviews at all experience levels.

Practice Questions — Test Your Packages & API Knowledge

Challenge yourself with these practice questions. Attempt each independently before reading the answer — writing and tracing code by hand significantly improves retention and interview readiness.

1. A Java file is saved at src/com/example/util/StringHelper.java. What must the first executable statement in this file be?

Easy

2. What is the output of this code and why? System.out.println(0.1 + 0.2 == 0.3); System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")).compareTo(new BigDecimal("0.3")));

Medium

3. Write a program using java.time to calculate how many days until the next New Year (January 1st of next year) from today's date.

Easy

4. Explain the output: package com.example.a; public class A { String secret = "hidden"; // package-private public String visible = "public"; } package com.example.b; import com.example.a.A; public class B { public static void main(String[] args) { A a = new A(); System.out.println(a.visible); // Line A System.out.println(a.secret); // Line B } }

Medium

5. Using java.util, write a program that: creates a list of 5 student names, sorts them alphabetically, removes all names shorter than 4 characters, and prints the final list.

Easy

6. What is wrong with this code? How many errors can you find? // File: src/MyApp.java import java.util.ArrayList; package com.example; public class MyApp { public static void main(String[] args) { BigDecimal price = new BigDecimal(9.99); } }

Medium

7. Write a program using java.math.BigDecimal to calculate compound interest: P = 10000, rate = 8.5% per annum, time = 3 years. Formula: A = P * (1 + r)^n. Round to 2 decimal places.

Medium

8. How would you properly organize a simple e-commerce application into packages? Describe the package structure and what each package contains.

Hard

Conclusion — Packages & API: Java's Organizational Backbone

Packages are not just a code organization tool — they are Java's fundamental namespace, visibility control, and architectural expression mechanism. Every professional Java codebase is a carefully designed package hierarchy that reflects the application's architecture, enforces encapsulation through access control, and prevents naming conflicts at scale. How you organize your packages reveals how clearly you understand your system's design.

The Java API — hundreds of packages and thousands of battle-tested classes — is one of Java's greatest strengths. Before writing any utility code, ask: does the Java API already have this? The answer is almost always yes. java.time for dates, java.math.BigDecimal for money, java.util for collections, java.nio.file for files — these are not optional libraries but core skills every Java developer must master. Knowing the API deeply is what separates a Java programmer from a Java developer.

ConceptKey PointBest Practice
package declarationFirst statement in file — maps to directoryAlways use reverse domain: com.company.module
import (single-type)Import exactly one class — preferred in prod codeLet your IDE manage imports; never leave unused imports
import (wildcard)import pkg.* — all types but NOT sub-packagesAvoid in production; use in test static imports only
static importUse static members without class name prefixOnly for universally known constants and JUnit assertions
java.langAuto-imported — String, Math, Integer, SystemKnow it deeply — you use it in every program
java.utilCollections, Arrays, Scanner, Optional, UUIDMaster ArrayList, HashMap, Collections, Arrays
java.math.BigDecimalExact decimal arithmetic — use for all moneyAlways String constructor; always set RoundingMode
java.timeModern date/time — replaces java.util.DateLocalDate, LocalDateTime, ZonedDateTime, DateTimeFormatter
package-private accessNo modifier — visible only in same packageUse to hide implementation details within a module
Sub-packagesNo inherited access — fully isolated namespacesNever assume sub-package can see parent package internals

Your next step: Java Arrays — the foundational data structure where everything you have learned about loops, break/continue, and the Java API comes together for traversal, searching, sorting, and manipulation. ā˜•

Frequently Asked Questions — Java Packages and API