1. ホーム
  2. java

[解決済み] Could not find or load main class "とはどういう意味ですか?

2022-03-14 17:48:26

質問

新しいJava開発者が経験する共通の問題は、エラーメッセージとともにプログラムが実行されないことです。 Could not find or load main class ...

これは何を意味し、何が原因で、どのように修正すればいいのでしょうか?

解決方法は?

その java <class-name> コマンドの構文

まず最初に、プログラムを起動するために java (または javaw ) コマンドを使用します。

通常の構文 1 はこうです。

    java [ <options> ] <class-name> [<arg> ...]

ここで <option> はコマンドラインオプション("-"で始まる文字)です。 <class-name> は完全修飾のJavaクラス名、そして <arg> は、アプリケーションに渡される任意のコマンドライン引数です。


1 - 他にもいくつかの構文があり、この回答の最後のほうに記載されています。

クラスの完全修飾名(FQN)は、慣習的にJavaのソースコードと同じように記述します。

    packagename.packagename2.packagename3.ClassName

ただし、一部のバージョンでは java コマンドでは、ピリオドの代わりにスラッシュを使用することができます。

    packagename/packagename2/packagename3/ClassName

は、(紛らわしいことに)ファイルのパス名のように見えますが、実際にはパス名ではありません。 という用語があることに注意してください。 完全修飾名 は、標準的なJavaの用語です...あなたを混乱させるために私が作ったものではありません :-)

以下は java コマンドは次のようになります。

    java -Xmx100m com.acme.example.ListUsers fred joe bert

上記により java コマンドは次のようになります。

  1. のコンパイル済みバージョンを検索します。 com.acme.example.ListUsers クラスがあります。
  2. クラスを読み込む。
  3. このクラスが main メソッドに シグネチャ , 戻り値タイプ モディファイア によって与えられる public static void main(String[]) . (注、メソッドの引数の名前は NOT はシグネチャの一部です)。
  4. コマンドライン引数 ("fred", "joe", "bert") を渡してそのメソッドを呼び出します。 String[] .

Javaがクラスを見つけられない理由

Could not find or load main class ..."というメッセージが表示されたら、それは最初のステップに失敗したことを意味します。 その java コマンドはクラスを見つけることができませんでした。 そして実際、メッセージの中の "..." は、次のようになります。 完全修飾クラス名 その java が探している。

では、なぜそのクラスが見つからないのでしょうか?

理由その1 - classname 引数を間違えている。

まず考えられるのは、間違ったクラス名を指定したことです。 (あるいは...正しいクラス名だが間違った形式) 上記の例を考慮すると、以下のようなさまざまなものがあります。 間違った方法 のように、クラス名を指定します。

  • 例1 - シンプルなクラス名。

    java ListUser
    
    

    などのパッケージでクラスが宣言されている場合、そのクラスは com.acme.example を使用する場合は、完全なクラス名 を含む のパッケージ名は java コマンドを使用します; 例.

    java com.acme.example.ListUser
    
    
  • 例2 - クラス名ではなく、ファイル名またはパス名を指定する。

    java ListUser.class
    java com/acme/example/ListUser.class
    
    
  • 例3:ケーシングが正しくないクラス名。

    java com.acme.example.listuser
    
    
  • 例4:タイプミス

    java com.acme.example.mistuser
    
    
  • 例5 - ソースファイル名 (Java 11以降を除く。下記参照)

    java ListUser.java
    
    
  • 例6:クラス名を完全に忘れてしまった場合

    java lots of arguments
    
    

理由その2:アプリケーションのクラスパスが正しく指定されていないため

2つ目の原因として考えられるのは、クラス名は正しいのですが、そのクラスの java コマンドでそのクラスを見つけることができません。 これを理解するためには、"classpath" の概念を理解する必要があります。 これは、次のように説明されています。 さて は、Oracleのドキュメントに記載されています。

さて......クラス名が正しく指定されていれば、次に確認するのはクラスパスが正しく指定されているかということです。

  1. 上記リンク先の3つのドキュメントを読んでみてください。 (そうです ... 読んでください。 Javaプログラマにとって重要なのは 理解する 少なくともJavaのクラスパスの仕組みの基本は知っておいてください)。
  2. を実行したときに有効なコマンドラインと/またはCLASSPATH環境変数を見ます。 java コマンドを実行します。 ディレクトリ名とJARファイル名が正しいかどうか確認してください。
  3. がある場合 相対 を実行したときに有効なカレントディレクトリから、 クラスパスのパス名が正しく解決されることを確認します。 java コマンドを実行します。
  4. エラーメッセージに記載されている)クラスが 有効 クラスパス
  5. なお、クラスパスの文法は 異なる の場合、Windows と Linux や Mac OS は異なります。(クラスパスのセパレータは ; を使用し、Windowsでは : を使用します。 もし、あなたのプラットフォームで間違ったセパレータを使用した場合、明確なエラーメッセージは表示されません。 その代わり、パス上に存在しないファイルやディレクトリが表示され、黙って無視されます)。

理由その2a - クラスパスに間違ったディレクトリが存在するため

クラスパスにディレクトリを置くと、それは概念的に修飾名空間のルートに対応します。 クラスは、そのルートの下のディレクトリ構造に配置されます。 完全修飾名をパス名にマッピングすることで . したがって、例えば、"/usr/local/acme/classes"がクラスパスにある場合、JVMが com.acme.example.Foon というパス名のファイルを探します。

  /usr/local/acme/classes/com/acme/example/Foon.class

もし、クラスパスに "/usr/local/acme/classes/com/acme/example" を置いたなら、JVMはクラスを見つけることができないでしょう。

理由その2b - サブディレクトリのパスがFQNと一致しない。

クラスのFQNが com.acme.example.Foon とすると、JVMはディレクトリ"com/acme/example"にある"Foon.class"を探そうとします。

  • もし、ディレクトリ構造が上記のパターンのようにパッケージの命名と一致しない場合、JVMはあなたのクラスを見つけることができません。

  • もし リネーム を移動させた場合も同様に失敗しますが、例外のスタックトレースは異なります。 このような表現になる可能性があります。

    Caused by: java.lang.NoClassDefFoundError: <path> (wrong name: <name>)
    
    

    クラスファイルの FQN がクラスローダーが期待するものと一致しないためです。

具体的な例を挙げると、仮に。

  • を実行したい場合 com.acme.example.Foon クラスがあります。
  • の場合、ファイルのフルパスは /usr/local/acme/classes/com/acme/example/Foon.class ,
  • 現在の作業ディレクトリは /usr/local/acme/classes/com/acme/example/ ,

では

# wrong, FQN is needed
java Foon

# wrong, there is no `com/acme/example` folder in the current working directory
java com.acme.example.Foon

# wrong, similar to above
java -classpath . com.acme.example.Foon

# fine; relative classpath set
java -classpath ../../.. com.acme.example.Foon

# fine; absolute classpath set
java -classpath /usr/local/acme/classes com.acme.example.Foon

注意事項

  • -classpath オプションは、次のように短縮することができます。 -cp は、ほとんどのJavaリリースで については、それぞれのマニュアルのエントリを確認してください。 java , javac といった具合です。
  • クラスパスで絶対パス名と相対パス名のどちらを選択するかは、慎重に考えてください。 相対パス名は、カレント・ディレクトリが変更された場合に "break"する可能性があることを覚えておいてください。

理由その2c - クラスパスから依存関係が抜けている

クラスパスには、すべての その他 (非システム) クラスに依存しています。 (システムクラスは自動的に配置されるので、ほとんど気にする必要はありません)。 メインクラスが正しくロードされるために、JVMは以下を見つける必要があります。

(注意:JLSとJVMの仕様では、JVMがクラスを"lazily"ロードするためのいくつかの範囲を認めており、これはクラスローダー例外が投げられるときに影響することがあります)。

理由その3:クラスが間違ったパッケージで宣言されている

時々、ソースコード・ファイルを を省略した場合、あるいは package という宣言があります。 IDEでこのようなことをすれば、IDEのコンパイラがすぐに教えてくれるでしょう。 同様に、まともなJavaビルドツールを使っている場合、そのツールは javac を検出するようにします。 しかし、手作業でJavaコードをビルドする場合、コンパイラが問題に気づかないような方法でビルドしてしまい、結果として ".class" ファイルが期待した場所にないことがあるのです。

まだ問題が見つからない?

チェックすべき項目がたくさんあり、見落としが起こりがちです。 そこで -Xdiag オプションを java コマンドラインの java ). クラスの読み込みに関する様々なことが出力され、本当の問題が何であるかを知る手がかりになるかもしれません。

また、Webサイトや文書などから見えない文字や非ASCII文字をコピー&ペーストすることで発生する可能性のある問題も考慮してください。 また、2つの文字や記号が同じように見えて、実は違うという「ホモグリフ」についても考えてみてください。

に無効または不正な署名がある場合、この問題に遭遇することがあります。 META-INF/*.SF . お気に入りの ZIP エディターで .jar を開き、以下のファイルを削除してください。 META-INF のみとなります。 MANIFEST.MF . しかし、これは一般的には推奨されません。 (無効な署名は、誰かがオリジナルの署名付き JAR ファイルにマルウェアを注入した結果かもしれません。 無効な署名を消してしまうと、あなたのアプリケーションにマルウェアを感染させてしまうことになります!) 推奨されるアプローチは、有効な署名を持つJARファイルを入手するか、(本物の)オリジナルのソースコードからJARファイルを再構築することです。

最後に、この問題はどうやら MANIFEST.MF ファイル(参照 https://stackoverflow.com/a/67145190/139985 ).


の代替構文 java

を使用してJavaプログラムを起動するには、3つの代替構文があります。 java command .

  1. 実行可能なJARファイルの起動に使用される構文は以下のとおりです。

    java [ <options> ] -jar <jar-file-name> [<arg> ...]
    
    

    など

    java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred
    
    

    エントリーポイントクラスの名前(つまり com.acme.example.ListUser ) とクラスパスがJARファイルのMANIFESTに指定されています。

  2. モジュールからアプリケーションを起動するための構文(Java 9以降)は、以下のとおりです。

    java [ <options> ] --module <module>[/<mainclass>] [<arg> ...]
    
    

    エントリポイントクラスの名前は <module> そのものか、あるいはオプションの <mainclass> .

  3. Java 11 以降は java コマンドを使用すると、次の構文で1つのソースコード・ファイルをコンパイルして実行できます。

    java [ <options> ] <sourcefile> [<arg> ...]
    
    

    ここで <sourcefile> は、(通常)接尾辞が ".java" であるファイルです。

の公式ドキュメントを参照してください。 java コマンドを使用します。


IDE

典型的なJava IDEは、IDE JVM自体または子JVMでJavaアプリケーションを実行するためのサポートを持っています。 これらは 一般的に IDEは独自のメカニズムでランタイムクラスパスを構築し、メインクラスを識別して java コマンドラインを使用します。

しかし、IDEの裏側でいろいろなことをすると、この例外が発生する可能性があります。 例えば、EclipseでJavaアプリのApplication Launcherを設定した後、"main"クラスを含むJARファイルをファイルシステム内の別の場所に移動させた場合です。 Eclipseに内緒で そのため、Eclipseは知らず知らずのうちに不正なクラスパスでJVMを起動することになります。

要するに、IDEでこの問題が発生した場合、IDEの状態が古くなっていないか、プロジェクトの参照が壊れていないか、ランチャーの設定が壊れていないかなどを確認します。

また、IDEが単に混乱している可能性もあります。 IDEは、多くの相互作用する部分からなる非常に複雑なソフトウェアの一部です。 これらのパーツの多くは、IDE が全体として応答するように、さまざまなキャッシュ戦略を採用しています。 これらは時々うまくいかないことがあり、可能性のある症状の1つは、アプリケーションの起動時の問題です。 このような現象が発生する可能性がある場合、IDE を再起動したり、プロジェクトを再構築するなど、他の方法を試してみる価値があります。


その他のリファレンス