📝

入門クラスパス

に公開

普段はIDEやビルドツール(MavenやGradle)が影でやってくれているので、あまり意識しないと思います。そもそもクラスパスというものの存在を知らない人も多いのではないでしょうか。

環境

  • JDK 21
  • macOS 15

コマンドでコンパイル・実行してクラスパスを体験しよう

カレントフォルダがcp-sampleだとします。この直下にcom/example/Main.javaがあるとします。

コンパイル前のフォルダ構造
cp-sample/  <---- 今ここ
└── com
    └── example
        └── Main.java
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がこれらに依存しているためです)。

pom.xml
    <dependencies>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.18</version>
        </dependency>
    </dependencies>

IntelliJからmain()メソッドを実行すると以下のように表示されます。

IntelliJでの実行結果(見やすいように改行を入れています)
/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