サンプルコードで身につける: java・javac・jar・javap: JDK のコマンド
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1用語早見表: JDK の4つのコマンド
4つのコマンドを、一言の定義、手元での打ち方、Node で近いものの順に並べた表です。「近いもの」は位置のたとえで、当たらない点をかっこの中に添えました。、、 は、大文字小文字と点の有無だけが違う別の語です。
用語 | 一言で手元では: 何として見えるか近いもの: Node / TypeScript で近いものjavac | .java から .class を作るコンパイラ手元では: javac Hello.java で Hello.class ができる近いもの: tsc(javac は JDK に入っている。tsc は npm で後から入れる)java | JVM を起動して、指定したクラスの main を呼ぶ手元では: java Hello、java -jar app.jar近いもの: node(node にはファイル名を渡す。java にはクラスの名前を渡す)jar | .class などを JAR にまとめる。JAR の中を見る手元では: jar --create ...、jar tf app.jar近いもの: zip を作る道具(jar はマニフェストという説明書きを自動で入れる点が違う。npm pack との対応は確かめていない)javap | .class の中身を、人が読める形で表示する手元では: javap Hello.class、javap -p -c ...近いもの: 無い(tsc が出す .js はそのまま読める)JDK の bin | これらのコマンドが入っているフォルダ手元では: JDK を入れた場所の bin/Temurin 25.0.4.1 では30個近いもの: 対応を確かめていないJAR | .class などをまとめた zip 形式のファイル(形式の名前)jar | その JAR を作る、中を見るコマンド(道具の名前).jar | JAR のファイル名に付く拡張子手元では: jar tf app.jar の、jar がコマンドでapp.jar がファイル近いもの: 無い
2オプション早見表: コースに出るものの意味
コースの図やラベルに出てくるオプションを、コマンドごとにまとめました。意味は 25 のツールリファレンスの説明を短くしたもので、 の(なし)と の行だけは実測です。-X、-XX、-p、-Djarmode は後のレッスンに出るので、ここでは形だけ見てください。
オプション | 意味例: 実際のコマンド行# java(書式は java [オプション] クラス名 [引数])-cp 場所 | クラスを探す場所の一覧-classpath、--class-path とも書く複数並べるときは : で区切る(Windows は ;)例: java -cp out app.Main-jar ファイル | JAR を指定して起動するJAR の中のマニフェスト(説明書きのファイル)のMain-Class の行に書かれたクラスが動くこのとき -cp など他の場所の指定は無視される例: java -jar app.jar(クラス名や JAR 名の後ろ) | java は読まない動かすプログラムへの引数になる例: java -jar demo.jar --server.port=8084例: java ... PlainApp --debug-version | 版を表示して終わる例: java -version-D名前=値 | システムプロパティ(名前と値の組)を設定する例: java -cp out -Dwho=Java app.Main-Xlog:... | JVM のログを出す例: java -Xlog:class+load -cp out app.Main-Xint | 機械語への変換をせず、解釈実行だけで動かす-Xshare:... | クラスデータ共有(CDS)の使い方を決める-Xshare:off は、共有されたクラスのデータを使わない例: java -Xshare:off -Xlog:class+load -cp out ...-XX:... | HotSpot の細かい調整用。変わりうる例: -XX:AOTCache=ファイル(AOT キャッシュの場所)-p 場所 | モジュール(部品の単位の1つ)を探す場所--module-path とも書く。javap の -p とは別の意味--add-modules 名前 | 追加で読み込むモジュールを指定する例: java -p ... --add-modules spring.core,org.slf4j ...-Djarmode=tools | -D の1例。Spring Boot の JAR に付いた道具を、アプリの代わりに動かす指定JAR 名の後ろの list-layers や extract は、その道具への引数(層の一覧、中身の取り出し)例: java -Djarmode=tools -jar demo.jar list-layers例: java -Djarmode=tools -jar application.jarextract --layers --destination extracted# javac(引数はソースのファイル名)-d フォルダ | .class の出力先。無ければ作られる書かないと、ソースと同じ場所に .class が出る例: javac -d out src/app/Main.java-cp 場所 | コンパイルに使う .class を探す場所-sourcepath 場所 | 使っているクラスのソースを探す場所例: javac -d out -sourcepath src src/app/Main.java--release 版 | その版の Java 向けの .class を作る例: javac -d rel22 --release 22 Hello.java-parameters | メソッドの引数の名前を .class に残す-g:none | デバッグ用の情報を付けない-version | javac の版を表示する例: javac -version-O | JDK 25 のマニュアルの一覧には無い受け付けられたが、出力の .class は付けない場合と同じ# jar(最初に、何をするかを書く)c または --create | JAR を作るt または --list | JAR の中のファイル名を一覧するx または --extract | JAR からファイルを取り出すf または --file | 扱う JAR のファイル名を、次に書くe または --main-class | 入口のクラス名を、次に書く(マニフェストの Main-Class の行になる)-C フォルダ | そのフォルダに移ってから、ファイルを入れる例: jar tf app.jar= jar --list --file app.jar例: jar cfe app2.jar app.Main -C out .= jar --create --file app2.jar--main-class app.Main -C out .# javap(引数は .class のファイル名かクラスの名前)(なし) | private 以外のフィールドとメソッドの一覧-p | private も含めて全部-c | メソッドごとの命令列(バイトコード)-v | 大きさ、版の番号、定数の表などの詳しい情報-cp 場所 | クラスを探す場所例: javap -p -cp out lib.util.Greeter
3javac から javap まで、一巡する
2つのを でコンパイルし、 で動かし、jar でまとめ、 で動かし、javap で中を見るまでの記録です。Main.java しか渡していないのに が2つできたのは、-sourcepath src で、使っている別のクラスのソースの場所を教えたからです。 の一覧にある は、jar コマンドが自動で作った(JAR の説明書きのファイル)です。
$ javac -d out -sourcepath src src/app/Main.java$ find out -type f | sortout/app/Main.classout/lib/util/Greeter.class$ java -cp out app.MainHello, world! sum=5$ jar --create --file app.jar --main-class app.Main -C out .$ jar tf app.jarMETA-INF/META-INF/MANIFEST.MFapp/app/Main.classlib/lib/util/lib/util/Greeter.class$ java -jar app.jarHello, world! sum=5$ javap -cp out lib.util.GreeterCompiled from "Greeter.java"public class lib.util.Greeter {public lib.util.Greeter();public java.lang.String greet(java.lang.String);}
4javap のオプションなし・-p・-c・-v で、見えるものが変わる
上のコードは、小さなクラスをコンパイルして、javap をオプションなし、-p、-c の順に呼びます(private な secret は -p のときだけ出ます)。下の実物は JDK 25 での の先頭12行で、強調した major version: 69 が .class の版の番号です。ブラウザでの実行は JDK 22 なので、-c の字下げなどは JDK 25 と少し違います。
Classfile /w/Hello.classLast modified (date); size 401 bytesSHA-256 checksum 10d6241194cddab9435b00d8b6141477175943693eed9d2fcca26d4a3217f10fCompiled from "Hello.java"public class Hellominor version: 0major version: 69flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #21 // Hellosuper_class: #2 // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 1Constant pool:
5混同しやすいもの: 渡すのはファイル名か、クラスの名前か
javac にはファイル名を、java にはクラスの名前を渡します。無い名前を渡したときと、javac に別のクラスの場所を教えなかったときの実物です。[exit 1] は、実験用のスクリプトが足した終了コード(0 が成功、0 以外が失敗)の行で、# で始まる行は補足です。
# 何を渡すか# javac ソースのファイル名 javac Hello.java# java クラスの名前 java Hello# java -jar のときは JAR の名前 java -jar app.jar# jar JAR と、入れるファイル jar tf app.jar# javap .class のファイル名か、クラスの名前# java に、無いクラスの名前を渡す$ java NopeError: Could not find or load main class NopeCaused by: java.lang.ClassNotFoundException: Nope[exit 1]# javap に、無いクラスの名前を渡す$ javap NopeError: class not found: Nope[exit 1]# javac に、Main が使う Greeter の場所を教えない$ mkdir -p out2 && javac -d out2 src/lib/util/Greeter.java && javac -d out3 src/app/Main.javasrc/app/Main.java:3: error: package lib.util does not existimport lib.util.Greeter;^src/app/Main.java:9: error: cannot find symbolSystem.out.println(new Greeter().greet(who) + " sum=" + add(2, 3));^symbol: class Greeterlocation: class Main2 errors[exit 1]# 先に作った Greeter.class の場所を -cp で教えると通る$ javac -d out3 -cp out2 src/app/Main.java