入門クラスパス
普段はIDEやビルドツール(MavenやGradle)が影でやってくれているので、あまり意識しないと思います。そもそもクラスパスというものの存在を知らない人も多いのではないでしょうか。
環境
- JDK 21
- macOS 15
コマンドでコンパイル・実行してクラスパスを体験しよう
カレントフォルダがcp-sampleだとします。この直下にcom/example/Main.javaがあるとします。
cp-sample/ <---- 今ここ
└── com
└── example
└── Main.java
package com.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
これをコンパイルするにはjavac .javaファイルの相対パス
とします。
cp-sample $ javac com/example/Main.java
先頭の
cp-sample $
は、カレントフォルダcp-sampleでjavac
コマンドを実行しているを示しています。以下の説明でも同様です。
そうするとクラスファイルMain.classが作られます。
cp-sample/ <---- 今ここ
└── com
└── example
├── Main.class <---- これが作られた!
└── Main.java
これを実行するにはjava クラスの完全修飾名
とします。
cp-sample $ java com.example.Main
Hello, World!
これはカレントフォルダcp-sampleで
java
コマンドを実行した結果「Hello, World!」が出力されたことを示します。以下の説明でも同様です。
ではここで、cp-sampleと同じ階層にあるcp-sample2フォルダに移動してみましょう。
cp-sample $ cd ../cp-sample2
.
├── cp-sample
│ └── com
│ └── example
│ ├── Main.class
│ └── Main.java
└── cp-sample2 <---- 今ここ
そして、そこで先程と同じjava
コマンドでMain
クラスの実行を試してみます。
cp-sample2 $ java com.example.Main
エラー: メイン・クラスcom.example.Mainを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: com.example.Main
するとMain
クラスが存在しませんというエラーになりました。カレントフォルダのcp-sample2配下にはMain.classが無いので当然ですね。
実は クラスパス を指定すると、cp-sample2がカレントフォルダであってもMainクラスを実行可能です。
cp-sample2 $ java -classpath ../cp-sample com.example.Main
Hello, World!
-classpath
オプション( -cp
でも同じ意味です)で指定した../cp-sample
がクラスパスです。クラスパスとは、クラスを含むパッケージが入っているフォルダのことです。クラスパスに指定するものはトップレベルパッケージ(今回はcom
)を直下に含むフォルダのことです(今回はcp-sampleフォルダ)。
-classpath
オプションを指定しない場合はデフォルトでカレントフォルダがクラスパスに追加されます。なので、cp-sampleがカレントフォルダだった時は-classpath
オプション無しでも実行できたんですね。
java -classpath ../cp-sample
のように、1つでも明示的にクラスパスを指定した場合は、カレントフォルダはクラスパスに追加されませんので注意してください。クラスパスは:
で区切って複数のフォルダを指定できますので、カレントフォルダもクラスパスに追加したい場合は java -classpath ../cp-sample:.
とします。
Windowsの場合の区切り文字は
;
だったと思います。たぶん。
JARファイルの場合は java -classpath JARファイルのパス1:JARファイルのパス2:...
とします。
IDEを使った場合
例えばIntelliJでMavenプロジェクトを作成し、以下のようにlogback-classicを依存性に追加します。すると、推移的依存性としてlogbacl-coreとslf4j-apiも追加されます(logback-classicがこれらに依存しているためです)。
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
</dependencies>
IntelliJからmain()
メソッドを実行すると以下のように表示されます。
/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk/Contents/Home/bin/java ...
-classpath /Users/tada/IdeaProjects/cp-sample-idea/target/classes
:/Users/tada/.m2/repository/ch/qos/logback/logback-classic/1.5.18/logback-classic-1.5.18.jar
:/Users/tada/.m2/repository/ch/qos/logback/logback-core/1.5.18/logback-core-1.5.18.jar
:/Users/tada/.m2/repository/org/slf4j/slf4j-api/2.0.17/slf4j-api-2.0.17.jar
com.example.Main
...
javaコマンドと共に、-classpath
オプションがあることが分かりますね!
プロジェクト直下のtarget/classesフォルダにはコンパイルしたクラスファイルが入っているので、このフォルダがクラスパスに追加されています。加えて、~/.m2/repository配下にあるlogback-classic.jarなどのJARファイルもクラスパスに追加されていることが分かります。
これを人間が手作業で書くのは大変なので、IntelliJなどのIDEが自動でやってくれている訳ですね。IDEって便利。
Discussion