To create a JAR file, we run jar –create –file app.jar -C classes ., and to extract it, we run jar –extract –file app.jar, or the short forms jar cf and jar xf. The jar tool ships with every JDK, and the commands are the same on Linux, macOS and Windows.
A Java ARchive (JAR) file is a ZIP file that holds compiled classes, resources such as property files and images, and a manifest that describes the archive. We create JAR files to ship a library or a runnable app as one file, and we extract them to inspect what a build put inside.
The following example packs the compiled classes of a small recipes app, runs it and unpacks it again.
jar --create --file recipes.jar --main-class com.example.recipes.RecipeApp -C classes . # create a runnable JAR
java -jar recipes.jar # Recipes app 1.0
jar --list --file recipes.jar # list the entries
jar --extract --file recipes.jar # extract into the current folder
Notice that -C classes . adds the content of the classes folder without the folder name itself, which is what java expects. We start with a cheat sheet, walk through creating, running, listing, updating and extracting JAR files, and finish with ways to open a JAR without a JDK.
1. Cheat Sheet for Linux, macOS and Windows
Every jar option has a short form that we can group, such as cvf, and a long GNU-style form, such as –create –verbose –file. The two styles cannot be mixed freely, because a grouped short option such as -cvf takes its file name as the next argument. All of these commands run in a Linux or macOS terminal, in Windows Command Prompt and in PowerShell.
| Task | Short form | Long form |
|---|---|---|
| Create a JAR from the content of a folder | jar cf app.jar -C classes . | jar –create –file app.jar -C classes . |
| Create with verbose output | jar cvf app.jar -C classes . | jar –create –verbose –file app.jar -C classes . |
| Create a runnable JAR | jar cfe app.jar com.example.App -C classes . | jar –create –file app.jar –main-class com.example.App -C classes . |
| Create with our own manifest | jar cfm app.jar manifest.txt -C classes . | jar –create –file app.jar –manifest manifest.txt -C classes . |
| Create from a list of files | jar cf app.jar @files.txt | jar –create –file app.jar @files.txt |
| List the content | jar tf app.jar | jar –list –file app.jar |
| Extract everything into the current folder | jar xf app.jar | jar –extract –file app.jar |
| Extract into another folder (JDK 24+) | jar xf app.jar -C out | jar –extract –file app.jar –dir out |
| Extract one file | jar xf app.jar app.properties | jar –extract –file app.jar app.properties |
| Update a file inside the JAR | jar uf app.jar -C classes app.properties | jar –update –file app.jar -C classes app.properties |
| Run a runnable JAR | java -jar app.jar | java -jar app.jar |
On Windows, the tool is jar.exe in the bin folder of the JDK. If the command is not found, the JDK bin folder is missing from the PATH, which we fix as described in installing Java on Windows.
2. Creating a JAR File
A JAR file has a fixed layout. Class files sit in folders that match their packages, resources sit next to them, and the manifest sits at META-INF/MANIFEST.MF. The jar tool creates the manifest for us, so we only need to point it at the compiled classes.

The following example is a small console app in the package com.example.recipes. It reads its version from app.properties on the classpath, so the JAR must contain both the class and the property file.
public class RecipeApp {
public static void main(String[] args) throws IOException {
Properties props = new Properties();
try (InputStream in = RecipeApp.class.getResourceAsStream("/app.properties")) {
if (in == null) {
throw new IllegalStateException("app.properties is missing from the JAR");
}
props.load(in);
}
System.out.println("Recipes app " + props.getProperty("version"));
}
}
We compile the class into a classes folder, copy the property file next to it and pack the folder. The c option creates an archive, f names the file, and v prints each entry with its compression.
javac -d classes src/com/example/recipes/RecipeApp.java
cp resources/app.properties classes/
jar cvf recipes.jar -C classes .
added manifest
adding: app.properties(in = 12) (out= 14)(deflated -16%)
adding: com/(in = 0) (out= 0)(stored 0%)
adding: com/example/(in = 0) (out= 0)(stored 0%)
adding: com/example/recipes/(in = 0) (out= 0)(stored 0%)
adding: com/example/recipes/RecipeApp.class(in = 1584) (out= 886)(deflated 44%)
The -C classes option changes into the classes folder before adding ., so the entries start at com/. If we write jar cf recipes.jar classes instead, every entry starts with classes/, and Java cannot find com.example.recipes.RecipeApp in the archive. The tiny app.properties even grew by 2 bytes when compressed, while the class file shrank by 44%.
When the list of files is long or generated by a script, we write the paths into a text file and pass it with @. Each line holds one path, relative to the folder where jar runs.
cd classes
find . -name "*.class" > ../classes.list
jar --create --file ../recipes-classes.jar @../classes.list
3. Making a Runnable JAR File
The JAR from the previous step contains the right files, but java -jar cannot start it yet.
java -jar recipes.jar
no main manifest attribute, in recipes.jar
The java launcher reads the Main-Class attribute from the manifest to know which main() method to call. The default manifest that jar writes contains only the version and the JDK that created it, so we add the entry point with –main-class (short form e).
jar --create --file recipes.jar --main-class com.example.recipes.RecipeApp -C classes .
java -jar recipes.jar
Recipes app 1.0
With that option, the generated manifest carries the entry point. We can print it without extracting anything, because a JAR is a ZIP file.
unzip -p recipes.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: 25.0.4.1 (Eclipse Adoptium)
Main-Class: com.example.recipes.RecipeApp
For more attributes, such as Implementation-Version or Class-Path, we write them into a text file and pass it with –manifest (short form m). The manifest file must end with a newline, otherwise jar drops the last line. For example, a manifest.txt with only Main-Class: com.example.recipes.RecipeApp and no line break produces a JAR that fails with the same “no main manifest attribute” error.
printf "Main-Class: com.example.recipes.RecipeApp\nImplementation-Version: 1.0\n" > manifest.txt
jar cfm recipes.jar manifest.txt -C classes .
The order of the file arguments follows the order of the letters, so in cfm the JAR name comes first and the manifest second. The old habit of mixing styles, such as jar -cvf app.jar –main-class com.example.App app/, does not work, because jar treats –main-class and the class name as file names and stops with “no such file or directory”.
A runnable JAR with libraries needs one more step. The Class-Path attribute lists other JARs relative to the location of our JAR, such as Class-Path: lib/gson.jar, and the -cp option of java is ignored when we start with -jar. Most projects let the build tool do this, either with a Maven executable JAR with dependencies or with a Spring Boot fat JAR.
4. Listing and Updating a JAR File
Before we ship a JAR, or when an app fails with a ClassNotFoundException, the first check is whether the class is in the archive at the right path. The t option lists the entries, and v adds sizes and timestamps.
jar tvf recipes.jar
0 Sat Oct 10 13:26:52 UTC 2026 META-INF/
109 Sat Oct 10 13:26:52 UTC 2026 META-INF/MANIFEST.MF
12 Sat Oct 10 13:26:52 UTC 2026 app.properties
0 Sat Oct 10 13:26:52 UTC 2026 com/
0 Sat Oct 10 13:26:52 UTC 2026 com/example/
0 Sat Oct 10 13:26:52 UTC 2026 com/example/recipes/
1584 Sat Oct 10 13:26:52 UTC 2026 com/example/recipes/RecipeApp.class
Say a support engineer has to change the version string in a JAR on a test server without rebuilding the project. The u option replaces or adds entries in an existing JAR and keeps the rest.
echo "version=1.1" > classes/app.properties
jar uf recipes.jar -C classes app.properties
java -jar recipes.jar # Recipes app 1.1
For builds that must produce the same bytes every time, the –date option sets one timestamp for all entries, for example jar –create –date 2026-10-01T00:00:00Z –file recipes.jar -C classes .. Two builds with the same input and the same date give JAR files with the same checksum.
5. Extracting a JAR File
Extracting works like unzipping, and the files keep their folder structure. The x option extracts, f names the JAR, and v prints each entry.
mkdir unpacked
cd unpacked
jar xvf ../recipes.jar
extracting to directory: /home/dev/recipes/unpacked
created: META-INF/
inflated: META-INF/MANIFEST.MF
inflated: app.properties
created: com/
created: com/example/
created: com/example/recipes/
inflated: com/example/recipes/RecipeApp.class
Up to JDK 23, jar always extracts into the current folder, which is why we create the folder and change into it first. Since JDK 24, we can name the target folder with –dir or -C, and jar creates it if it does not exist.
jar --extract --file recipes.jar --dir out
jar xf recipes.jar -C out
To extract only some entries, we add their paths after the JAR name. The paths are the entry names that jar tf prints, and a folder name extracts everything below it.
jar xf recipes.jar app.properties
jar xf recipes.jar com/example/recipes/
Extracting overwrites files with the same name without asking. The -k (–keep-old-files) option, available since JDK 21, keeps the existing files instead, which protects a local app.properties that we already edited.
6. Extracting a JAR Without the JDK
A JAR is a standard ZIP file, so any ZIP tool can open it. This helps on a server that has only a JRE or no Java at all.
- On Linux and macOS, unzip recipes.jar -d out extracts the JAR into out, and unzip -l recipes.jar lists it.
- On Windows 10 and 11, the built-in tar.exe reads ZIP files, so tar -xf recipes.jar extracts into the current folder.
- The PowerShell command Expand-Archive accepts only files with the .zip extension, so we copy recipes.jar to recipes.zip first.
- Graphical tools such as 7-Zip open a JAR when we choose “Open archive” from the context menu.
ZIP tools treat the manifest as an ordinary file. For creating JARs, we still use jar or the build tool, because a ZIP tool does not write the manifest or the Main-Class entry for us.
7. JAR File FAQs
Each answer starts from a real situation, either an error from java -jar or a JAR that someone else built.
7.1. How do I run a JAR file?
We run java -jar app.jar when the manifest has a Main-Class. If it has none, we name the class ourselves with java -cp app.jar com.example.recipes.RecipeApp, which also lets us add more JARs to the classpath.
7.2. Why does java -jar say “no main manifest attribute”?
The manifest has no Main-Class line. We rebuild with –main-class or e, or we check that our own manifest file ends with a newline, because jar drops a last line without one.
7.3. Can a JAR file contain other JAR files?
Yes, as files, but the standard class loader does not load classes from a JAR inside a JAR. Spring Boot fat JARs work because they use their own launcher. For a plain JAR, we either unpack the libraries into the archive (the Maven Shade or Assembly plugins do that) or ship them next to it and list them in Class-Path.
7.4. What is the difference between a JAR and a ZIP file?
The format is the same. A JAR adds the META-INF folder with the manifest and, optionally, signatures and service files, and Java tools such as java -jar and jarsigner read those entries.
7.5. How do I see the source code of a class inside a JAR?
A JAR holds compiled .class files, not source code. Extracting it gives bytecode, which we can inspect with javap -c -cp app.jar com.example.recipes.RecipeApp or open in an IDE with a decompiler. Libraries often publish a separate -sources.jar with the source files.
8. Conclusion
The jar tool creates, lists, updates and extracts JAR files with the same commands on Linux, macOS and Windows. We pack the content of the classes folder with -C classes ., add an entry point with –main-class or a manifest file that ends with a newline, and check the result with jar tf.
Extraction uses jar xf, with –dir for a target folder on JDK 24 and later, and any ZIP tool works when no JDK is installed. The Java tutorial lists more command-line and build topics.
9. References
Happy Learning !!