Create and Extract a JAR File on Linux, macOS and Windows

Create and extract a JAR file with the jar command on Linux, macOS and Windows. Cheat sheet, runnable JARs, manifest, –dir and unzip options.

Folder with compiled classes packed into recipes.jar with META-INF/MANIFEST.MF and extracted again

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.

TaskShort formLong form
Create a JAR from the content of a folderjar cf app.jar -C classes .jar –create –file app.jar -C classes .
Create with verbose outputjar cvf app.jar -C classes .jar –create –verbose –file app.jar -C classes .
Create a runnable JARjar cfe app.jar com.example.App -C classes .jar –create –file app.jar –main-class com.example.App -C classes .
Create with our own manifestjar cfm app.jar manifest.txt -C classes .jar –create –file app.jar –manifest manifest.txt -C classes .
Create from a list of filesjar cf app.jar @files.txtjar –create –file app.jar @files.txt
List the contentjar tf app.jarjar –list –file app.jar
Extract everything into the current folderjar xf app.jarjar –extract –file app.jar
Extract into another folder (JDK 24+)jar xf app.jar -C outjar –extract –file app.jar –dir out
Extract one filejar xf app.jar app.propertiesjar –extract –file app.jar app.properties
Update a file inside the JARjar uf app.jar -C classes app.propertiesjar –update –file app.jar -C classes app.properties
Run a runnable JARjava -jar app.jarjava -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.

Folder with compiled classes packed into recipes.jar with META-INF/MANIFEST.MF and extracted again
jar –create packs the folder content and adds a manifest, and jar –extract restores the same tree

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 !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.