java.lang.ClassNotFoundException って、どういうことよ?

f:id:ts0818:20190823145126j:plain

『昨日探し当てた場所に 今日もジャンプしてみるけれど なぜか NOT FOUND 今日は NOT FOUND~♪(「NOT FOUND:Mr.Children」)』 

はい、どうも~。ボクです。

というわけで、Java のお話です。

奇しくも、404記事目という...Not Found~♪、レッツトライ~。

 

java.lang.ClassNotFoundException ってよく分かってなかった

まぁ、何て言うか、これまで、あんまり考えたこと無かったんだけど、今さらながら、java.lang.ClassNotFoundException って何で起こるのか?ってことを、職場の同僚の方に教えていただいたので、整理をば。

 

ズバリ、java.lang.ClassNotFoundException っ何で起きるのか?

OracleJava API のドキュメントによりますと、 

public class ClassNotFoundException
extends ReflectiveOperationException
アプリケーションが、クラスの文字列名を使用して次のメソッドでロードしようとしたが、指定された名前のクラスの定義が見つからなかった場合にスローされます。
  • クラス Class の forName メソッド。
  • クラス ClassLoader の findSystemClass メソッド。
  • クラス ClassLoader の loadClass メソッド。

 

1.4 リリースでは、この例外は汎用的な例外チェーンメカニズムに適合するように改良されています。「クラスのロード時に生じたオプション例外」 (構築時にスローされ、getException() メソッドを介してアクセス可能) は、cause メソッドと呼ばれるようになり、前述の「レガシーメソッド」に加えて Throwable.getCause() メソッドを介してアクセス可能です。

導入されたバージョン:
JDK1.0

ClassNotFoundException (Java Platform SE 8)

⇧  説明によると、いずれもクラスが見つからない時に起きる現象のようですが、 

  1. Class
    • forName メソッド
  2. ClassLoader
    • findSystemClass メソッド
    • loadClass メソッド

の3パターンのいずれかで、クラス が見つからなかった場合でしか起こりえないと。

あくまで、「上記のメソッドが正常に実施され、かつ、クラス が見つからなかった 」っていう場合にのみ発生するんだと。

「forName」「findSystemClass」「loadClass」って、3つのメソッドの違いはよく分かりませんが、いずれも、役割的には、「クラスの文字列名をキーにクラスを見つける」ってことをやっているらしいと。

 

Java の仕組み

ここで、Java の仕組みに繋がると。

どういうことかと言いますと、

Java - Wikipedia

⇧  っていうか、ほとんどっていうのがどうにも気になるな...。

んで、説明が、抽象的なんですが、1つのファイルを処理するで考えた場合、 

ファイルの拡張子 内容
.java ソースコード
.class 中間言語(またの名を、バイトコード

ってファイルが最低限、必要なんだと。

「.jar」「.war」「.ear」なんかについては、

java-wizard.hatenadiary.org

⇧  上記サイト様が詳しいです。

JVMJava Virtual MachineJava仮想マシン)が、.class ファイルを実行することで、Java のプログラムが実行されるということらしい。

上記の図で分かる通り、JREJava Runtime Environment)は、

という構成になっているので、JavaPythonコンパイラーは含まれていない。

つまり、実際に、Javaアプリケーションを配布ってなった場合は、「中間言語」となっている状態にしておく必要があると。

なので、コンパイル済にしておく必要があるんじゃと。

ちなみに、JDKJava Development Kit)は、「JRE」+ 「α」 っていう構成になると思うので、コンパイラーなんかが含まれるであろうと。

 

ビルド と コンパイル の違いって、何だ?

「ビルド」と「コンパイル」の違いって何なのさ?って疑問を持たれた方は、安心してください、私と同じく情弱(情報弱者)の仲間入りです、一緒にがんばって参りましょう(涙)。 

qiita.com

⇧  上記サイト様が詳しいです。 

つまり、Javaの標準API以外に、何かしら外部のAPIなんかを使いたい場合は、ビルドしてあげないと、駄目ってことみたい。

コンパイルは、あくまで、「ソースコードから中間言語への変換」のみを行い、それ以外のことについては一切何も行わないということらしい。

 

結局、java.lang.ClassNotFoundExceptionって?

ここまでの流れで、コンパイルエラーが原因で、.class ファイルが作成されないのが原因かと思ってたんですが、

teratail.com

一部の関連するクラスファイルが残っていると、コンパイルは通るのに
実行時にクラスファイルがないというエラーが起きます。

Java - 2回目以降、コンパイル時にクラスファイルが更新・生成されない|teratail

⇧  上記サイト様によりますと、コンパイルは正常に終了しても、中間言語である .class ファイルが見つからない状態が起こりえて、 

  • Class
    • forName メソッド
  • ClassLoader
    • findSystemClass メソッド
    • loadClass メソッド

で、クラスを参照しに行った際に、クラスが見つからずに起こる現象であると。

厄介なところは、大規模プロジェクトなんかだと、クラスの数が膨大になるため、何が影響してクラスファイルが作成されなかったのかが分かりにくいということでしょうか。

フレームワークなんかを使っている場合は、さらに原因が分かりにくくなる感じでしょうか。

SVN」や「Git」などのバージョン管理システムなどを使っている場合は、プロジェクト内で状況を説明したりして、不具合の起きている事象への対応をお願いしている間は、全体のリビジョンを戻したりして対応する感じになるんですかね?

 

www.techscore.com

⇧  上記サイト様によりますと、ビルドが正常に終了して、.class ファイルが生成されている状態でも、クラスパスの問題で、.class ファイルが見つからんと怒られる場合があるのだと。

なので、 

  • .classファイルが生成されていない
  • .classファイルは生成されているけど、クラスパスが解決できない

のどっちかによって、ClassNotFoundException が起こっているってことなんすかね?

 

結局、ClassNotFoundException が、具体的にどういう時に起こりえるのかはよく分からんのだけれども、ビルド、ないしは、コンパイルがすべてのファイルに対して行われていない、または、クラスパスを解決できない場合に起こりえるんだと。

 

ちなみに、Eclipse なんかのIDEだと、

codeday.me

⇧「クラスパス」は自動で設定してくれるそうなので、「ビルドパス」ってものを意識していけば良いそうな。

「ビルドパス」の確認をすることで、生成された .class ファイルがどこに格納されるか分かるらしいです。

http://www.hot-surprise.org/IntroEclipse/Operation/N01/3_4.html

 

というわけで、Eclipseで適当なプロジェクトを選択し、右クリックで、「ビルド・パス(B)」>「ビルド・パスの構成(C)...」で。

f:id:ts0818:20190823180452p:plain

Java のビルド・パス」の「ソース」タブで、「デフォルト出力フォルダー(T):」ってところに、.class ファイルが格納されるらしい。

f:id:ts0818:20190823180823p:plain

そんなフォルダ表示され取らんやん...

f:id:ts0818:20190823181032p:plain

どうやら、フィルターがかかってしまっているらしい。

www.ibm.com

⇧ 上記のサイト様を参考にするも、そもそも、「ビューのカスタマイズ...」が選択肢にないんですよね。

f:id:ts0818:20190823181212p:plain

仕方ないので、「ナビゲーター」のほうで、「フィルター(F)...」を選択します。

「ナビゲーター」が表示されていない場合は、「ウィンドウ(W)」>「ビューの表示(V)」>「ナビゲーター」で表示させます。

f:id:ts0818:20190823181641p:plain

f:id:ts0818:20190823182039p:plain

⇧ 「*.class」にフィルターがかかってないことを確認。

「FunctionIFTest/bin」に、.class ファイルが生成されてることが確認できました。

f:id:ts0818:20190823182141p:plain


すみません...、「ビューのカスタマイズ...」は「Filters and Customization(F)...」に該当するようです。

f:id:ts0818:20190823182615p:plain

Java 出力フォルダー」にチェックが付いてしまっているので、

f:id:ts0818:20190823182905p:plain

チェックを外し、「OK」で。

f:id:ts0818:20190823182957p:plain

「プロジェクト・エクスプローラー」でも「bin」フォルダが表示されました。

f:id:ts0818:20190823183052p:plain

見つからないクラスファイルの名前が分かっている場合は、.classファイルの出力先のフォルダを確認し、.class ファイルが生成されているか確認するのも1つの手ですかね?クラスファイルの数が多い場合は厳しいでしょうけど。

 

あと、Eclipse経由でGC(ガベージコレクター)を実施できるってことも職場の同僚の方に教えていただきました。

f:id:ts0818:20190823185752p:plain

「一般」>「ヒープ・ステータスを表示(W)」にチェックを入れればOKらしい。

f:id:ts0818:20190823185843p:plain

チェックで。

f:id:ts0818:20190823190003p:plain

Eclipse の右下に、「ヒープ・ステータス」が表示されました。

f:id:ts0818:20190823190039p:plain

f:id:ts0818:20190823190216p:plain をクリックすると、ガベージコレクターが実行されるようです。

ログとかを可視化してくれるプラグインとかも存在するそうな。

qiita.com

 

というわけで、今回も本当に知りたい部分については分からいままとなってしまいモヤモヤ感が半端ない...

今回はこのへんで。