Dev Study
← 解説「成果物の単位: .class・JAR・WAR・実行可能 JAR」に戻る

サンプルコードで身につける: 成果物の単位: .class・JAR・WAR・実行可能 JAR

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1ソース1ファイルから .class が9個できる

Shapes.java という1つのソースをコンパイルした結果です。入れ子のやは Shapes$Shape.class のように $ でつないだ名前の別ファイルになり、合わせて9個できました。 は「ソースファイルごと」ではなく「型ごと」の単位です。(その場で書く関数)だけは、ファイルになりません。

$ javac -d out の結果(JDK 25)コマンド出力
$ find src -type f
src/com/example/units/Shapes.java
 
$ javac -d out src/com/example/units/Shapes.java
 
$ find out -type f | sort
out/com/example/units/SecondTopLevel.class
out/com/example/units/Shapes$1.class
out/com/example/units/Shapes$1Local.class
out/com/example/units/Shapes$Circle.class
out/com/example/units/Shapes$Helper.class
out/com/example/units/Shapes$Inner.class
out/com/example/units/Shapes$Shape.class
out/com/example/units/Shapes$Unit.class
out/com/example/units/Shapes.class
 
$ find out -type f | wc -l
9
// src/com/example/units/Shapes.java
package com.example.units;

import java.util.function.Supplier;

public class Shapes {                                  // 1: Shapes.class

    interface Shape { double area(); }                 // 2: Shapes$Shape.class(入れ子のインターフェース)

    record Circle(double r) implements Shape {         // 3: Shapes$Circle.class(入れ子の record)
        public double area() { return Math.PI * r * r; }
    }

    enum Unit { CM, INCH }                             // 4: Shapes$Unit.class(入れ子の enum)

    static class Helper {                              // 5: Shapes$Helper.class(static な入れ子のクラス)
        static String label(Unit u) { return u.name().toLowerCase(); }
    }

    class Inner { }                                    // 6: Shapes$Inner.class(内部クラス)

    public static void main(String[] args) {
        Shape square = new Shape() {                   // 7: Shapes$1.class(無名クラス)
            public double area() { return 4.0; }
        };
        Supplier<String> lambda = () -> "lambda";      // ファイルはできない
        Runnable methodRef = System.out::println;      // ファイルはできない
        class Local { }                                // 8: Shapes$1Local.class(メソッドの中のクラス)
        System.out.println(new Circle(1).area() + " " + square.area() + " " + lambda.get() + " " + Helper.label(Unit.CM));
        System.out.println("lambda class at run time: " + lambda.getClass().getName().replaceAll("0x[0-9a-f]+", "0x..."));
        methodRef.run();
    }
}

class SecondTopLevel { }                               // 9: SecondTopLevel.class(同じファイルの2つ目の型)

2JAR の中身: .class の階層とマニフェスト

2クラスだけのプログラムを コマンドでまとめた app.jar(1,800 B)の中です。.class がフォルダの階層ごと入り、 が足されています。の の行が、 で起動するときの入口です。

$ jar tf app.jar と、中のマニフェストコマンド出力
$ ls -l app.jar && jar tf app.jar
-rw-r--r-- 1 root root 1800 Oct 1 22:26 app.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/app/
com/example/app/Main.class
com/example/greet/
com/example/greet/Greeter.class
 
# META-INF/MANIFEST.MF の中身
Manifest-Version: 1.0
Created-By: 25.0.4.1 (Eclipse Adoptium)
Main-Class: com.example.app.Main

3WAR の中身: 置く場所が決まっている

で作った の (7,825,305 B)の中です。自分の .class は の下、 は の下、設定は と、置く場所が決まっています。 はこの決まりに従って中身を読みます。マニフェストに Main-Class は無く、java -jar で起動するものではありません。

$ jar tf dist/shop.warコマンド出力
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/web.xml
WEB-INF/applicationContext.xml
WEB-INF/dispatcher-servlet.xml
index.html
WEB-INF/classes/
WEB-INF/classes/jp/
WEB-INF/classes/jp/co/
WEB-INF/classes/jp/co/example/
WEB-INF/classes/jp/co/example/shop/
WEB-INF/classes/jp/co/example/shop/service/
WEB-INF/classes/jp/co/example/shop/web/
WEB-INF/classes/jp/co/example/shop/service/GreetingService.class
WEB-INF/classes/jp/co/example/shop/web/HelloController.class
WEB-INF/lib/
WEB-INF/lib/commons-logging-1.3.5.jar
WEB-INF/lib/jspecify-1.0.0.jar
WEB-INF/lib/micrometer-commons-1.16.7.jar
WEB-INF/lib/micrometer-observation-1.16.7.jar
WEB-INF/lib/spring-aop-7.0.9.jar
WEB-INF/lib/spring-beans-7.0.9.jar
WEB-INF/lib/spring-context-7.0.9.jar
WEB-INF/lib/spring-core-7.0.9.jar
WEB-INF/lib/spring-expression-7.0.9.jar
WEB-INF/lib/spring-web-7.0.9.jar
WEB-INF/lib/spring-webmvc-7.0.9.jar

4単位ごとに「何を入れて、何に渡すか」

このコースで実際に作ったを、単位ごとに並べた表です。上から下へ、入れるものが増えていきます。jar コマンドで作る までは依存が外にあり、WAR と は依存を中に持ちます。渡す先が コマンドなのか、java の上で動いている Tomcat なのかで、起動の仕方も変わります。

成果物の単位(大きさは最小アプリでの実測値)表
.class
入れるもの : 型1つ分のバイトコード
渡す先 : JVM(クラスを読む係が、必要になった時点で読む)
 
JAR
入れるもの : .class と設定ファイルを階層ごと zip にしたもの + META-INF/MANIFEST.MF
依存 : jar コマンドで普通に作ると入らない(実行時に -cp で並べる)
渡す先 : java -cp x.jar 入口のクラス名 / Main-Class があれば java -jar x.jar
実例 : app.jar 1,800 B(.class 2個)
 
WAR
入れるもの : WEB-INF/classes/(自分の .class)+ WEB-INF/lib/(依存の JAR)+ WEB-INF/web.xml + 静的ファイル
依存 : 入る
渡す先 : Tomcat などの Servlet コンテナ(java で動いている)。webapps/ に置く。main は無い
実例 : shop.war 7,825,305 B(依存の JAR 11本)
 
実行可能 JAR(Spring Boot)
入れるもの : BOOT-INF/classes/(自分の .class)+ BOOT-INF/lib/(依存の JAR)+ 起動用のローダ
依存 : 入る(Tomcat も依存の1つとして入る)
渡す先 : java -jar x.jar
実例 : demo-0.0.1-SNAPSHOT.jar 約 19.9 MB(依存の JAR 34本)
 
実行可能 WAR(Spring Boot)
入れるもの : WEB-INF/classes/ + WEB-INF/lib/ + WEB-INF/lib-provided/(組み込みの Tomcat など)+ 起動用のローダ
渡す先 : java -jar x.war でも、Tomcat の webapps/ でも
実例 : demo-0.0.1-SNAPSHOT.war 19,904,763 B

5同じ WAR を java -jar でも Tomcat でも動かす

で作った WAR は、java -jar で直接起動することも、Tomcat の に置くこともできました(各1回の確認)。違いは誰が先に起きるかです。java -jar では が中の Tomcat を起こし、Tomcat に置いた場合は Tomcat が Spring を起こします。後者では URL に WAR の名前が入るので、/hello は 404 になり、/demo/hello で応答が返りました。

同じ demo-0.0.1-SNAPSHOT.war を2通りに動かす(ログは主な行だけ。# の行は補足)コマンド出力
# 1. java -jar で起動
$ java -jar target/demo-0.0.1-SNAPSHOT.war
2026-10-01T22:32:28.574Z INFO 1 --- [demo] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
$ curl -i http://localhost:8080/hello
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 22
 
Hello from Spring Boot
 
# 2. 同じファイルを Tomcat 11.0.26 の webapps/demo.war に置く
01-Oct-2026 22:32:29.518 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/demo.war]
2026-10-01T22:32:30.487Z INFO 1 --- [demo] [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.524 seconds (process running for 1.272)
01-Oct-2026 22:32:30.496 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/demo.war] has finished in [978] ms
$ curl -i http://localhost:8080/demo/hello
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 22
 
Hello from Spring Boot
$ curl -i http://localhost:8080/hello
HTTP/1.1 404

図やコードに出てくる用語(タップで説明)