Java の分割コンパイルとクラスパス
Java の分割コンパイル
通常、アプリケーションは複数のソースコードから作成されます。
Java ではパブリックのクラスは、クラス名と同じ名前のファイルにひとつ定義できます。したがって、複数のクラスを定義するときには複数のファイルから構成されることになります。
複数のソースコードをバラバラにコンパイルすることは、一般的に 分割コンパイル と呼ばれます。
Java の分割コンパイルの例
Java の分割コンパイルがどのように行われるか、具体例で見てみましょう。
ここではまず、次のような Person クラスを定義します。これは Person.java として保存してください。
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public void printName() {
System.out.println(this.name);
}
public void printAge() {
System.out.println(this.age);
}
}
そして、次のコードを TestApp.java として、Person.java と同じディレクトリに保存してください。
public class TestApp {
public static void main(String[] args) {
Person p = new Person(20, "John Doe");
p.printName();
p.printAge();
}
}
ポイントは TestApp.java にて、Person クラスを利用していることです。
TestApp.java をコンパイルしてみましょう。
javac TestApp.java
すると TestApp.class の他、自動的に Person.java もコンパイルされ Person.class が作成されます。
ls *.class
Person.class TestApp.class
このように javac では必要に応じて、クラスを探してそれを利用します。また必要に応じて、関連ファイルをコンパイルし直します。
Java における分割コンパイルは、自動的に必要なファイルをみつけコンパイルし直したりしてくれるので、とても簡単です。
ここで作成してプログラム TestApp は次のように実行できます。
java TestApp
John Doe
20
Java のクラスパスとは?
さて、もしここで Person クラスに関するファイルをサブディレクトリ dir1 内に移動したらどうなるでしょうか。試してみましょう。
現在のディレクトリには次のように TestApp と Person があります。
ls
Person.class Person.java TestApp.class TestApp.java
そこで、ディレクトリ dir1 を作り、 Person を移動します。
mkdir dir1
mv Person.* dir1
ls
TestApp.class TestApp.java dir1
ls dir1
Person.class Person.java
そこでもう一度 TestApp を実行してみましょう。
java TestApp
Exception in thread "main" java.lang.NoClassDefFoundError: Person
at TestApp.main(TestApp.java:3)
Caused by: java.lang.ClassNotFoundException: Person
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
今度はエラーが発生しました。
エラーメッセージをよく見れば、 Person クラスが見つからないことによる ClassNotFoundException 例外が発生したことが原因であることがわかります。
Java のランタイムに、サブディレクトリに Person を探しに行かせることはできないでしょうか。
それを可能にするのが、クラスパスです。
Java はクラスを探すときに、「クラスパス」として指定されたパスを探します。指定が無い場合は . (現在のディレクトリ) を探します。
CLASSPATH 環境変数
クラスパスは環境変数 CLASSPATH で指定できます。
Java はクラスパスとして、環境変数 CLASSPATH で指定された場所を探し、指定が無い場合は . (現在のディレクトリ) を探します。
この結果、 Java はデフォルトでは、同じディレクトリ内にあるクラスを自動的に参照します。
上の例では Person クラスは TestApp クラスと同じディレクトリ内にあったために、Person クラスを自動的にみつけることができた、というわけです。
CLASSPATH 環境変数の設定
上で発生した問題を解決するには Java が Person を見つけられるように、次のように環境変数 CLASSPATH を構成します。
export CLASSPATH=.:./dir1
: コロンで区切り、 . (現在のディレクトリ) と ./dir1 サブディレクトリを追加しています。
Windows では次のようにします。
SET CLASSPATH=.;.\dir1
セミコロン ;で区切って、パスを複数指定できます。ここでは現在のディレクトリを表すドット . と、 現在のディレクトリのサブディレクトリ .\dir1 の二つを指定しています。
CLASSPATH を設定し、もう一度 TestApp を実行します。
java TestApp
John Doe
20
今度は正しく実行されました。
このように環境変数 CLASSPATH を構成することによって、Java が必要なクラスを見つけることができるようになります。
javac でも CLASSPATH は有効
クラスパス環境変数 CLASSPATH は java コマンドで有効なだけでなく、 Java の実行環境全体で使用されます。
例えば javac コンパイラでもクラスファイルを参照するために CLASSPATH が使われます。
クラスパスをコマンドオプション -classpath で指定
環境変数 CLASSPATH でクラスパスを指定する他、コマンドラインのオプションとしてコマンド実行時にクラスパスを渡すこともできます。
例えば、上の例では Person クラスを探すために ./dir1 サブディレクトリを環境変数 CLASSPATH に設定しました。同様の効果は次のように、 コマンドオプション -classpath を指定することでも実現できます。
java -classpath .:./dir1 TestApp
-classpath は省略形として -cp も使えます。