☕ Java
JAR Files
A JAR (Java ARchive) file is a compressed archive in ZIP format that packages compiled Java class files, resources (images, configuration files, properties), and optional metadata into a single distributable file. JAR files are the standard unit of packaging and distribution for Java code — libraries, applications, and frameworks are all distributed as JARs. Every dependency you add to a Maven or Gradle project is a JAR file downloaded from a repository.
What a JAR File Contains
A JAR file is a ZIP archive — you can open any JAR with a ZIP tool to inspect its contents. Inside, you find a hierarchy of directories mirroring the package structure of the compiled code, plus any resource files the application needs, plus the META-INF directory that holds metadata.
The class files are the compiled bytecode of every Java class in the jar. They follow the directory structure of the package hierarchy: com/example/service/OrderService.class is the bytecode for the class in package com.example.service. The JVM loads class bytecode from JARs using the classpath — when code references com.example.service.OrderService, the JVM looks for com/example/service/OrderService.class in each JAR on the classpath.
Resource files are any non-Java files the application needs at runtime: configuration files (.properties, .yaml, .xml), SQL scripts, HTML templates, image files, and so on. They are placed in the JAR alongside the class files and loaded at runtime using the ClassLoader's getResourceAsStream() method, which searches the classpath just as class loading does.
The META-INF directory contains metadata about the JAR. The most important file here is MANIFEST.MF — the manifest file that describes the JAR's contents and properties. META-INF also contains service loader configuration files (META-INF/services/), framework annotations and descriptors (META-INF/spring.factories, META-INF/persistence.xml), digital signatures (.SF and .RSA files for signed JARs), and the module descriptor (module-info.class) for modular JARs.
The JAR format's ZIP compression makes it significantly smaller than an equivalent directory of class files. For deployment, a single JAR is much simpler to transfer, copy, and version than a directory tree with thousands of files.
Java
// ── JAR contents (viewed with jar tf myapp.jar or unzip -l): ─────────
//
// META-INF/
// META-INF/MANIFEST.MF
// META-INF/services/
// META-INF/services/javax.annotation.processing.Processor
// com/
// com/example/
// com/example/service/
// com/example/service/OrderService.class
// com/example/service/UserService.class
// com/example/model/
// com/example/model/Order.class
// com/example/model/User.class
// application.properties
// db/migration/V1__create_tables.sql
// templates/email/welcome.html
// static/images/logo.png
// ── Loading resources from a JAR at runtime: ─────────────────────────
// Resources are loaded via ClassLoader — searches classpath including JARs:
InputStream config = getClass()
.getResourceAsStream("/application.properties");
// With leading slash — searches from root of classpath:
InputStream template = getClass()
.getResourceAsStream("/templates/email/welcome.html");
// Without leading slash — relative to current class's package:
InputStream query = OrderService.class
.getResourceAsStream("queries.sql");
// searches: com/example/service/queries.sql in any JAR on classpath
// ── Listing JAR contents from command line: ───────────────────────────
// jar tf myapp.jar — list all files in jar
// jar xf myapp.jar — extract all files
// jar xf myapp.jar META-INF/MANIFEST.MF — extract specific file
// unzip -l myapp.jar — list with sizes (Unix)
// unzip -p myapp.jar META-INF/MANIFEST.MF — print file contentCreating JAR Files
JAR files are created with the jar command-line tool bundled with the JDK, or by build tools like Maven and Gradle that call the jar tool internally. Understanding the jar command is valuable even if you use a build tool because it clarifies exactly what a JAR is and how it is assembled.
The jar tool uses a flags-and-files syntax where the first argument specifies the operation: c (create), t (table of contents), x (extract), u (update), or i (generate index). The f flag specifies the output file name. The m flag specifies a manifest file to include. The e flag specifies the main class for an executable JAR. The v flag produces verbose output.
Build tools generate JARs as part of the build lifecycle. Maven's mvn package creates a JAR in the target/ directory. Gradle's gradle jar creates it in build/libs/. These generated JARs contain only your compiled classes and resources — they do not include dependencies. To create a fat JAR (also called an uber-jar or standalone JAR) that includes all dependencies, special plugins are used: the Maven Shade Plugin (maven-shade-plugin), the Maven Assembly Plugin, or Gradle's Shadow Plugin.
Fat JARs are the standard deployment artifact for executable applications — a single file that contains everything needed to run. Spring Boot applications, for example, are deployed as a fat JAR that contains the Spring framework, all dependencies, and an embedded web server (Tomcat or Netty). Running java -jar myapp.jar starts the entire application from a single file. This contrasts with library JARs (thin JARs) that intentionally contain only the library's own code, expecting the using application to provide the dependencies on its classpath.
Java
// ── Create a JAR from compiled class files: ──────────────────────────
// Assume .class files are in the 'out/' directory:
// Basic JAR — no manifest:
// jar cf myapp.jar -C out .
// c = create
// f = output file name follows
// -C out = change to 'out' directory
// . = include everything from there
// JAR with custom manifest:
// jar cfm myapp.jar META-INF/MANIFEST.MF -C out .
// m = manifest file follows (after the JAR filename)
// Executable JAR with main class specified via -e flag:
// jar cfe myapp.jar com.example.Main -C out .
// e = entry point (main class) follows
// Verbose output:
// jar cvf myapp.jar -C out .
// v = verbose: prints each added file
// ── Viewing JAR contents: ────────────────────────────────────────────
// jar tf myapp.jar — table of contents (list files)
// jar tvf myapp.jar — verbose listing with sizes
// jar tf myapp.jar | grep ".class" — show only class files
// ── Extracting from a JAR: ────────────────────────────────────────────
// jar xf myapp.jar — extract all
// jar xf myapp.jar META-INF/MANIFEST.MF — extract one file
// ── Adding a JAR to the classpath: ───────────────────────────────────
// java -cp myapp.jar:lib/gson.jar com.example.Main (Unix)
// java -cp myapp.jar;libgson.jar com.example.Main (Windows)
// java -cp "lib/*" com.example.Main — all JARs in lib/
// ── Running an executable JAR: ───────────────────────────────────────
// java -jar myapp.jar
// java -jar myapp.jar arg1 arg2 — with command line arguments
// java -Xmx512m -jar myapp.jar — with JVM flags
// ── Maven creates a JAR with: mvn package ─────────────────────────────
// Output: target/myapp-1.0.0.jar (thin JAR, no dependencies)
//
// For fat JAR with Spring Boot:
// mvn spring-boot:repackage or mvn package (if spring-boot plugin configured)
// Output: target/myapp-1.0.0.jar (fat JAR — includes all dependencies)
// ── Fat JAR — everything bundled: ────────────────────────────────────
//
// myapp-1.0.0-fat.jar contents:
// META-INF/MANIFEST.MF
// com/example/service/OrderService.class ← your code
// com/example/model/Order.class ← your code
// com/google/gson/Gson.class ← Gson dependency
// org/springframework/web/... ← Spring dependency
// ch/qos/logback/... ← Logback dependency
// application.properties ← your resourcesClasspath and JAR Resolution
The classpath tells the JVM where to look for class files and resources. It is a list of directories and JAR files. When the JVM needs to load a class, it searches each entry in the classpath in order, looking for the class file in the expected package directory path. The first entry that contains the required class wins — this is called classpath shadowing or classpath priority.
Classpath shadowing can be a source of subtle bugs: if two JARs on the classpath both contain the same class, the one listed first in the classpath is loaded. This is particularly relevant with conflicting library versions — if your application has both gson-2.8.jar and gson-2.10.jar on the classpath, whichever comes first determines which version is used. Build tools like Maven and Gradle manage dependency conflicts automatically using dependency mediation rules, but direct classpath management requires care.
The ClassLoader hierarchy is an important detail for JAR loading. Java has a delegation model for class loading: when a class needs to be loaded, the class loader first asks its parent to load it. If the parent cannot find it, the child tries. The bootstrap class loader loads java.* classes from the JDK. The platform class loader (extension class loader in Java 8) loads javax.* and extension classes. The application class loader loads from the classpath. Custom class loaders (used by application servers, OSGi, and Java agents) can intercept and customise loading further.
JAR signing provides a mechanism for verifying the origin and integrity of a JAR. A JAR can be signed with a private key; the public key certificate is included in META-INF. The JVM can verify the signature before loading classes, ensuring the JAR has not been tampered with. This is used for applets (historically), Java Web Start, and security-sensitive deployments.
Java
// ── Classpath declaration: ───────────────────────────────────────────
// Single JAR:
// java -cp myapp.jar com.example.Main
// Multiple JARs and directories:
// java -cp "classes:lib/gson.jar:lib/logback.jar" com.example.Main
// All JARs in a directory (Java 6+):
// java -cp "classes:lib/*" com.example.Main
// ── Classpath shadowing — order matters: ──────────────────────────────
// If both these JARs contain com.google.gson.Gson:
// java -cp "lib/gson-2.8.jar:lib/gson-2.10.jar" com.example.Main
// → gson-2.8.jar Gson is loaded (first in classpath)
// ── Loading resources — classpath search: ────────────────────────────
// getResourceAsStream searches classpath entries in order,
// just like class loading:
public class Config {
public static Properties load() throws IOException {
Properties props = new Properties();
// Searches each classpath entry for "config.properties"
// at the root level:
try (InputStream is = Config.class
.getClassLoader()
.getResourceAsStream("config.properties")) {
if (is == null) {
throw new IOException("config.properties not found on classpath");
}
props.load(is);
}
return props;
}
}
// ── ClassLoader hierarchy: ────────────────────────────────────────────
ClassLoader app = Main.class.getClassLoader();
ClassLoader platform = app.getParent();
ClassLoader boot = platform.getParent(); // null — bootstrap CL
System.out.println(app.getClass().getName()); // jdk.internal.loader.ClassLoaders$AppClassLoader
// ── Maven dependency resolution (no classpath shadowing issues): ──────
// Maven selects the nearest version in the dependency tree.
// pom.xml:
// <dependencies>
// <dependency>
// <groupId>com.google.code.gson</groupId>
// <artifactId>gson</artifactId>
// <version>2.10.1</version> ← explicit version — no ambiguity
// </dependency>
// </dependencies>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.
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.
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.