12 クラスパスについて理解する

【第12回】クラスパスについて理解する

今回はクラスパスについての説明です。
このクラスパスを理解すれば、他のプログラマの作成したクラスを利用するための、
前準備ができたことになります。

そうなれば、自分でプログラムを組まずとも、立派なソフトウェアをたくさん、
作ることができるようになります。

楽しみですね。

前提: 複数クラスの利用にメリットを感じている(または便利そうだと思っている)

というわけで、サンプルプログラムを用意してみました。

ClassPathTest.javaと、ClassPathTest2.javaです。

ClassPathTest.java

public class ClassPathTest
{
    public static void main(String[] args)
    {
        ClassPathTest2 hoge2 = new ClassPathTest2();
    }
}

ClassPathTest2.java

public class ClassPathTest2
{
    public ClassPathTest2()
    {
        System.out.println("ClassPathTest2() is called.");
    }
}

見ての通り、ClassPathTestクラスからClassPathTest2クラスのインスタンスを作成しているだけの、
非常に単純なプログラムです。

まずは2つのファイルを同じフォルダに置いて、コンパイル・実行をしてみましょう。

C:\work>javac ClassPathTest.java
C:\work>java ClassPathTest
ClassPathTest2() is called.

C:\work>

予定通り、”ClassPathTest2() is called.”と表示されましたね。

では、今いるフォルダの下に、anotherフォルダを作成して、そこにClassPathTest2の
ソースファイルとclassファイルを移動してみましょう(エクスプローラで移動してください)。

では再度、ClassPathTest.classを実行してみましょう。

C:\work>java ClassPathTest
Exception in thread "main" java.lang.NoClassDefFoundError: ClassPathTest2
        at ClassPathTest.main(ClassPathTest.java:5)

C:\work>

「ClassPathTest2が見つからないぞ」と、怒られてしまいましたね。

では、コンパイルはどうでしょう。

C:\work>javac ClassPathTest.java
ClassPathTest.java:5: エラー: シンボルを見つけられません
        ClassPathTest2 hoge2 = new ClassPathTest2();
        ^
  シンボル:   クラス ClassPathTest2
  場所: クラス ClassPathTest
ClassPathTest.java:5: エラー: シンボルを見つけられません
        ClassPathTest2 hoge2 = new ClassPathTest2();
                                   ^
  シンボル:   クラス ClassPathTest2
  場所: クラス ClassPathTest
エラー2個

C:\work>

コンパイルも失敗するようになってしまいました。

原因はやはり、ClassPathTest2クラスが見つからなくなったことにあるようです。

同じようなエラーメッセージが第10回でも出ていましたね。

javaコマンドも、javacコマンドも、ClassPathTestクラスから使用するClassPathTest2クラスを、
ハードディスクの中から無造作に探しているわけではありません。
そんな事をしていれば、新しいクラスのインスタンスを作成する度に、
ハードディスクをガリガリとアクセスすることになり、とても実用に足る性能が出なくなります。

ですから、私たちの方で、classファイルを探す場所を指定する必要があります。
そのクラスを検索するための候補がクラスパスというわけです。

クラスパスを指定するには2つの方法があります。

1つは環境変数を使用する方法と、-classpathオプションを使用する方法です。

今回は、環境変数を使用する方法で再度、コンパイルしてみましょう。

環境変数の設定は以下のようにsetコマンドを使用します。

C:\work>set CLASSPATH=.;another

これは環境変数CLASSPATHに、カレントフォルダ(.)と、カレントフォルダの下の
anotherフォルダを指定するものです。

「カレントフォルダ」が良くわからないかもしれませんが、
これは現在作業中のフォルダを指す言葉です。
今回の場合だと、ClassPathTest.javaがあるフォルダですね。

では、コンパイルしてみましょう。

C:\work>javac ClassPathTest.java

C:\work>

無事に成功しましたね。

クラスパスにanotherと指定することによって、javacコマンドが無事、anotherフォルダの下にあるClassPathTest2.classを見つけられたようです。では、実行できるでしょうか?

C:\work>java ClassPathTest
ClassPathTest2() is called.

C:\work>

実行も成功しました。

このように、多数のフォルダに分割してクラスを置いている場合には、
クラスパスを指定してあげなくてはいけません。

えっ?こんな面倒なことしなくても、全て1つのフォルダに置いておけば良いって?

たしかに、そうすれば、こんな面倒なことをしなくてもよいのですが、
分割して整理しておく方がなにかと都合がいいものです。

今はclassファイルの数も1つ2つといったところですが、実際にソフトウェアを作成するとなると、
10や20といったクラスがあっという間に出来上がってしまいます。
また、他のソフトウェアからも使う共通ライブラリとも言えるようなクラスがあった場合は、
他のクラスとは別のフォルダに分けて置いた方が、整理しやすいですね。

というわけで、最後にちょっとした実験をしてみましょう。

another2というフォルダを作成し、その中に、ClassPathTest2.javaという名前
(another配下のファイルと同じ名前)のファイルを作成します。
新しく作成したClassPathTest2.javaの内容は以下のようにします。

public class ClassPathTest2
{
    public ClassPathTest2()
    {
        System.out.println("ClassPathTest2() is called.");
        System.out.println("This class is in another2.");
    }
}

another配下のClassPathTest2.javaにメッセージを追加しただけですね。

では、コンパイルしてみましょう。

C:\work>cd another2

C:\work\another2>javac ClassPathTest2.java

C:\work\another2>cd ..

C:\work>

さて、ClassPathTestクラスを実行してみましょう。

C:\work>java ClassPathTest
ClassPathTest2() is called.

C:\work>

では、環境変数CLASSPATHを以下のように変更して、
ClassPathTestクラスを実行するとどうなるでしょうか?

C:\work>set CLASSPATH=.;another2

C:\work>java ClassPathTest
ClassPathTest2() is called.
This class is in another2.

C:\work>

実行結果が変化しましたね。 このように、クラスパスの設定によって、
同じClassPathTestクラスの実行結果を変えることもできます。

実際には、このようにクラス設定でプログラムの動作を変化させたりはしませんが、
環境変数や-classpathオプションの設定に注意していないと、自分が考えているのとは
全く別のclassを実行することになり、思わぬ落とし穴に引っかかることになります。

また、クラスパスの指定順によっても同様な問題が発生するので注意しなくてはいけません。

今回の場合で言えば、

C:\work>set CLASSPATH=.;another;another2

C:\work>

とするのと、

C:\work>set CLASSPATH=.;another2;another

C:\work>

とするのとで、挙動が異なります。

前者ではanotherフォルダにあるClassPathTest2.classが読み込まれますが、
後者ではanother2フォルダにあるClassPathTest2.classが読み込まれることになります。
つまり、先に指定されたクラスパスが優先されるわけです。

~ まとめ ~

1.クラスパスを設定すると、複数のフォルダに整理されているクラスを利用できる
2.クラスパスの指定順によって、実行されるクラスの優先度を設定できる

今回はクラスパスの設定について、勉強しました。次回はパッケージについて勉強します。

クラスパスとパッケージを理解すれば、多数のクラスファイルを効率よく管理することが
できるようになりますし、他のプログラマが作成したクラスを自在に利用することが
できるようになります。

そうなれば、いよいよ本格的なJavaプログラミングができるようになります。
楽しみにしていてください。

それでは。

→ 次へ(第13回:パッケージについて理解する)