コマンド「Java パッケージ名/クラス名」で実行できない時の対処法

概要
=

`java パッケージ名/クラス名`で実行ができなかったのに対して、
`java -classpath . パッケージ名/クラス名`では実行できたことに疑問を感じ、
`java パッケージ名/クラス名`で実行するにはどうすればいいか自分なりの対処法を見つけたので投稿します。

教科書通りに`java パッケージ名/クラス名`(もしくは`java パッケージ名.クラス名`)と入力しているのに実行できなくて困っているという方は本記事の内容を試してみてください。

環境
=
- OS: macOS Catalina 10.15.6
- JDK:14.0.1

実行するファイルについて
=
「Main.java」（「hoge」パッケージに所属させています）

実行するとターミナルに「hello.java」と表示される単純なプログラムです。
格納ディレクトリは「/Users/user/src/Java/sukiri1/Chapter6/hoge/Main.java」です。

```java:Main.java
package hoge;

public class Main {
	public static void main(String[] args) {
		System.out.println("hello.java");
  	}
}
```

原因
=

クラスファイルが格納されているカレントディレクトリを起点として実行しなくてはならないのに、該当のクラスファイルが格納されていない「.bash_profile」でクラスパスに設定しているパスを起点として実行しようとしてしまったから。

Java VM(ヴァーチャルマシン)が見当違いな場所へクラスファイルを探しに行ってしまった、、、というのが原因です。

説明
=

###①カレントディレクトリの設定
まずはカレントディレクトリを、「hoge」パッケージが格納されている「/Users/user/src/Java/sukiri1/Chapter6」に設定します。

```
HIrokinoMacBook-Pro:hoge user$ cd /Users/user/src/Java/sukiri1/Chapter6
HIrokinoMacBook-Pro:Chapter6 user$
```

###②コンパイル

```sh
HIrokinoMacBook-Pro:Chapter6 user$ javac hoge/Main.java
HIrokinoMacBook-Pro:Chapter6 user$ 
```

###③`java hoge/Main`で実行

「hoge.Mainが見つからない」とエラーになってしまいました。

```sh
HIrokinoMacBook-Pro:Chapter6 user$ java hoge/Main
エラー: メイン・クラスhoge.Mainを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: hoge.Main

```

###④現在のクラスパスを確認

ターミナルに下記のコマンドを入力し現在のクラスパスを確認します。

```sh
$ env | grep CLASSPATH
```

筆者の「.bash_profile」の「CLASSPATH=」で設定しているパスが表示されました。
「/Applications/Java/apache-tomcat-9.0.36/lib/servlet-api.jar」には、
プログラム実行に必要な「hoge/Main.class」は格納されていません。

```sh
HIrokinoMacBook-Pro:Chapter6 user$ env | grep CLASSPATH
CLASSPATH=/Applications/Java/apache-tomcat-9.0.36/lib/servlet-api.jar
```

参考　↓筆者の「.bash_profile」

```
export JAVA_HOME="$(/usr/libexec/java_home -v 14.0.1)"
export CATALINA_HOME=/Applications/Java/apache-tomcat-9.0.36
export PATH="/usr/bin:${PATH}:${JAVA_HOME}"
export ANT_HOME=/Users/user/tool/apache-ant-1.10.8
export PATH=${PATH}:${ANT_HOME}/bin
export PATH=${PATH}:/Users/user/tool
export CATALINA_OPTS=-Dfile.encoding=UTF-8
export CLASSPATH=/Applications/Java/apache-tomcat-9.0.36/lib/servlet-api.jar
```

###⑤`java -classpath . hoge/Main`で実行

Java VM(ヴァーチャルマシン)がどの"場所"から必要なクラスファイルを読み込めばよいかを指定するために
オプション「-classpath」を使って、「hoge/Main」が格納されているカレントディレクトリを指定します。
「.(ピリオド)」はカレントディレクトリを指しています。

オプション「-classpath」は、**アプリケーションごと**にクラスパスを設定する機能があり、
ここでは**"「hoge/Main」というアプリケーションを今回は「カレントディレクトリ」をクラスパスとして実行します!"**
としています。
下で説明している「CLASSPATH環境変数」でクラスパスを設定していても、このオプションを使えば、
「CLASSPATH環境変数」で設定したクラスパスが無効になり、オプション「-classpath」で設定したクラスパスが設定されます。

「CLASSPATH環境変数」について、
こちらは実行時にいちいち「-classpath」で場所をJava VMに教えなくてもいいようにOSにクラスパスを予め登録する方法です。このようにクラスパスをOSに登録するとすべてのアプリケーションに適用されます。

④でコマンド「env | grep CLASSPATH」を打った時に、筆者の「.bash_profile」の「CLASSPATH=」で設定しているパスが表示されましたが、これがOSに登録したクラスパスです。
「-classpath」で特別指定しない限り、自動的に環境変数で設定したこちらのクラスパスが適用されるため、
コマンド「env | grep CLASSPATH」を打つと、クラスファイルが格納されているカレントディレクトリではなく、
環境変数で設定したこちらのクラスパスがターミナルに出力されます。



「-classpath」と「CLASSPATH 環境変数」 は下記の記事が参考になります。
一番上の記事が非常にわかりやすいです。
https://www.javaroad.jp/java_basic2.htm
https://docs.oracle.com/javase/jp/6/technotes/tools/windows/classpath.html
https://style.potepan.com/articles/17670.html
https://www.atmarkit.co.jp/fjava/onepoint/java/jv_jcmd.html


長くなりましたが、`java -classpath . hoge/Main`で実行すると下記のように問題なく動きました。

```sh

HIrokinoMacBook-Pro:Chapter6 user$ java -classpath . hoge/Main
hello.java
```

さて、ここからコマンド「java パッケージ名/クラス名」で実行するための方法について紹介します。

###⑥クラスパスを削除する

下記のコマンドをターミナルに入力し、環境変数を削除します。

```sh
$ export CLASSPATH=
```

再度、`$ env | grep CLASSPATH`を入力すると、、、

```sh
HIrokinoMacBook-Pro:Chapter6 user$ env | grep CLASSPATH
CLASSPATH=
```
クラスパスが削除されていることが確認できました。


参考
クラスパスの削除の仕方
http://javacafebreak.tripod.com/document/cpad_tips/cpad_classpath.html

ターミナルからのCLASSPATH環境変数の書き換え方
https://style.potepan.com/articles/17670.html

###⑦`java hoge/Main`で実行

```sh
HIrokinoMacBook-Pro:Chapter6 user$ java hoge/Main
hello.java
```

成功です。
ちなみに`java hoge.Main`でも下記の通り実行できます。

```sh
HIrokinoMacBook-Pro:Chapter6 user$ java hoge.Main
hello.java
```

「CLASSPATH環境変数」「-classpath」のどちらでもクラスパスを指定していない場合、
カレントディレクトリがクラスパスとして設定されます。
そのため、`java hoge.Main`とクラスパスが含まれない形でコマンドを入力しても、
Java VMがパッケージとクラスファイルを見つけることができたので、実行できたのです。


長くなりましたが以上です!