JAR Files
A JAR file packages compiled classes, resources, and metadata into a single distributable archive — the standard unit of Java packaging since the platform's earliest days.
A JAR (Java ARchive) is a ZIP-format file that bundles compiled .class files, resources (property files, images, configuration), and metadata into one distributable unit. JARs are how Java libraries are shared, how applications are packaged for deployment, and — with the right manifest entries — how a standalone program can be launched with a single java -jar command. Understanding JAR structure, the manifest file, and how JARs interact with the classpath and module path is foundational to building and deploying real Java software.
JAR Structure
A JAR is literally a ZIP archive (any ZIP tool can open one) with a conventional internal layout: compiled classes mirror their package structure as directories, resources sit alongside them, and a special META-INF directory holds metadata, most importantly MANIFEST.MF.
A module-aware JAR additionally contains a module-info.class at its root, making it usable as a named module on the module path (see the Java Modules topic).
myapp.jar
├── META-INF/
│ └── MANIFEST.MF
├── com/
│ └── example/
│ ├── Main.class
│ └── util/
│ └── Helper.class
└── config/
└── settings.propertiesCreating and Inspecting JARs with the jar Tool
The JDK's jar command creates, lists, and extracts JAR contents, mirroring tar's flag style.
# create a JAR from a compiled output directory
jar --create --file myapp.jar -C out .
# list contents
jar --list --file myapp.jar
# extract into the current directory
jar --extract --file myapp.jar
# create with an explicit main class (writes the manifest for you)
jar --create --file myapp.jar --main-class com.example.Main -C out .The Manifest File
META-INF/MANIFEST.MF is a plain-text file of key: value header lines (with a strict 72-byte line-wrapping rule in the format, though tools handle this automatically) describing the JAR. Common headers:
Main-Class names the entry point java -jar should invoke. Class-Path lists additional JARs (relative to this JAR's own location) to add to the classpath when this JAR is run — useful for simple multi-JAR deployments, though it does not support wildcards or absolute paths and is largely superseded by build-tool-generated launcher scripts or fat/uber JARs for anything nontrivial. Automatic-Module-Name gives a plain (non-modular) JAR a stable name when placed on the module path, as covered in the Java Modules topic.
Manifest-Version: 1.0
Main-Class: com.example.Main
Class-Path: lib/commons-lang3.jar lib/gson.jar
Implementation-Title: MyApp
Implementation-Version: 2.1.0
Automatic-Module-Name: com.example.myappExecutable JARs
A JAR becomes directly runnable with java -jar name.jar when its manifest specifies Main-Class. This is the most common distribution shape for standalone Java applications.
java -jar ignores any -classpath/-cp argument given alongside it — an executable JAR's classpath comes only from its own Class-Path manifest entry (plus, since Java 9, an optional module path). This trips people up when they expect -cp to add to it.
package com.example;
public class Main {
public static void main(String[] args) {
System.out.println("Running from an executable JAR");
}
}javac -d out src/com/example/Main.java
jar --create --file myapp.jar --main-class com.example.Main -C out .
java -jar myapp.jar
# -> Running from an executable JARClasspath: How JARs Are Found at Run Time
For non-jar-launched programs, JARs are added to the run-time search path via -classpath (or -cp), a platform-specific-separated list (: on Unix-like systems, ; on Windows) of JAR files and/or directories.
For any nontrivial dependency graph, hand-listing every JAR quickly becomes unmanageable, which is exactly the problem build tools and dependency management (Maven/Gradle, covered in the Dependency Management topic) solve by computing and assembling the classpath automatically.
java -cp "myapp.jar:lib/commons-lang3.jar:lib/gson.jar" com.example.Main # Unix
java -cp "myapp.jar;lib/commons-lang3.jar;lib/gson.jar" com.example.Main # WindowsModule Path vs. Classpath for JARs
The same JAR file can be placed on either the classpath (-cp) or the module path (--module-path/-p), with different consequences. On the classpath, its classes land in the unnamed module with full classpath-style visibility. On the module path, a JAR with a module-info.class becomes a real named module honoring JPMS encapsulation rules; a JAR without one becomes an automatic module (a compatibility bridge — see the Java Modules topic for the full behavior of both).
Fat/Uber JARs
A 'fat' or 'uber' JAR bundles an application's own classes together with the unpacked contents of every one of its dependency JARs into a single self-contained archive, so it can be deployed and run with nothing else needed on the classpath. Build-tool plugins (the Maven Shade Plugin, Gradle's Shadow plugin) produce these, typically also handling conflicts when two dependencies contain a file at the same path (most commonly colliding META-INF/services entries, which need merging rather than one simply overwriting the other).
Fat JARs trade convenience (one file to deploy) for size and, if built carelessly, for silently overwritten resources — a build tool's shading/merging strategy matters, not just concatenating ZIPs.
Signed JARs
The jarsigner tool can cryptographically sign a JAR's contents, adding signature files under META-INF/ that let the JVM (or another verifier) confirm the JAR hasn't been tampered with and identify who signed it — relevant for code that runs under a security manager or for verifying supply-chain integrity of published artifacts.
Common Packaging Patterns
Libraries typically ship as a 'thin' JAR (their own classes only) plus published dependency coordinates, letting consumers' build tools resolve the dependency graph. Standalone applications typically ship as either a fat/uber JAR, or a thin JAR alongside a lib/ directory of dependencies referenced by the manifest's Class-Path, or — increasingly — a custom minimal runtime image built with jlink for a fully self-contained deployment that doesn't even require a separately installed JVM.
Key Takeaways
A JAR is a ZIP archive of compiled classes and resources, with metadata (most importantly MANIFEST.MF) under META-INF/.
Main-Class in the manifest makes a JAR runnable via java -jar; that launch mode uses only the JAR's own manifest Class-Path, ignoring any external -cp argument.
-classpath/-cp lists JARs and directories to search at run time; for real dependency graphs this is normally computed by a build tool, not hand-written.
The same JAR behaves differently on the classpath (unnamed module) versus the module path (named or automatic module) — see the Java Modules topic.
Fat/uber JARs bundle all dependencies into one self-contained archive, trading size for deployment simplicity.
java -jar myapp.jar -cp lib/* (expecting -cp to extend the classpath)
List additional JARs in the manifest's Class-Path header instead, or build a fat JAR, or launch with java -cp "myapp.jar:lib/*" com.example.Main instead of -jar.
java -jar uses only the executing JAR's own manifest Class-Path; any -cp/-classpath argument given alongside -jar is silently ignored.
Assuming a plain JAR on the module path enforces the same encapsulation as a real module.
Recognize it becomes an automatic module — it reads every other module and exports all its packages, providing no real encapsulation until it ships a proper module-info.class.
Automatic modules are a migration bridge, not equivalent to genuine JPMS strong encapsulation.
Concatenating dependency JARs into a fat JAR without a merge strategy for colliding resource paths.
Use a build-tool shading plugin (Maven Shade, Gradle Shadow) configured to merge known collision-prone files like META-INF/services entries.
A naive last-JAR-wins overwrite can silently drop service-provider registrations or other resources contributed by more than one dependency.
What file format is a JAR file actually built on?
ZIP — a JAR is a ZIP archive with a conventional internal structure, most notably META-INF/MANIFEST.MF.
What manifest header makes a JAR runnable with java -jar, and what does it specify?
Main-Class, which names the fully qualified class containing the main method the JVM should invoke.
Why does java -jar myapp.jar -cp extra.jar not add extra.jar to the classpath?
Launching with -jar uses only the executing JAR's own manifest Class-Path entry for its classpath; any external -cp/-classpath argument is ignored in that launch mode.
What is the difference between placing a JAR on the classpath versus the module path?
On the classpath it loads into the unnamed module with classic classpath visibility. On the module path, a JAR with a module-info.class becomes a real named module enforcing JPMS rules; one without becomes an automatic module, a compatibility bridge with weaker encapsulation.
What is a fat/uber JAR, and what problem does it solve?
A JAR that bundles an application's own classes together with the unpacked contents of all its dependencies into one self-contained archive, so it can be deployed and run without any other JARs present on the classpath.
What does the Automatic-Module-Name manifest header do?
It gives a plain (non-modularized) JAR a stable, explicit module name to use when it's placed on the module path as an automatic module, instead of relying on a name guessed from the JAR's filename.
Why is signing a JAR with jarsigner useful?
It lets a verifier confirm the JAR's contents haven't been tampered with since signing and identify who signed it, relevant to code run under a security manager or to verifying supply-chain integrity of published artifacts.
Write the shell commands to compile a Main class in package com.example, package it into an executable JAR, and run it.
javac -d out src/com/example/Main.java jar --create --file myapp.jar --main-class com.example.Main -C out. java -jar myapp.jar javac compiles into the out directory preserving package structure; jar --create with --main-class both packages the classes and writes a manifest with the correct Main-Class header; java -jar reads that header to find and invoke com.example.Main.main.
If myapp.jar with this manifest is at /apps/myapp.jar, from where does the JVM look for lib/a.jar and lib/b.jar when launched with java -jar /apps/myapp.jar?
Class-Path entries in the manifest are resolved relative to the location of the JAR itself, so the JVM looks for /apps/lib/a.jar and /apps/lib/b.jar.
Write the jar command to list the contents of an existing archive named lib.jar without extracting it.
jar --list --file lib.jar The --list (or -t) option prints every entry path inside the archive, letting you inspect its structure — including checking for a module-info.class or the presence of META-INF/MANIFEST.MF — without unpacking anything to disk.
# MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.example.Main
Class-Path: lib/a.jar lib/b.jar