48

大文字と小文字を区別しない同じ名前の 2 つのパブリック Java クラスを異なるディレクトリに書き込むと、両方のクラスが実行時に使用できなくなります。a(いくつかのバージョンの HotSpot JVM を使用して、Windows、Mac、および Linux でこれをテストしました。同時に使用できる JVM が他にAあるとしても驚かないでしょう。)

// lowercase/src/testcase/a.java
package testcase;
public class a {
    public static String myCase() {
        return "lower";
    }
}

// uppercase/src/testcase/A.java 
package testcase;
public class A {
    public static String myCase() {
        return "upper";
    }
}

上記のコードを含む 3 つの eclipse プロジェクトは、私の Web サイト から入手できます

myCase試してみると、次のように両方のクラスを呼び出します。

System.out.println(A.myCase());
System.out.println(a.myCase());

タイプチェッカーは成功しますが、上記のコードで生成されたクラス ファイルを実行すると、次のようになります。

スレッド「メイン」の例外 java.lang.NoClassDefFoundError: testcase/A (間違った名前: testcase/a)

Java では、名前は一般に大文字と小文字が区別されます。一部のファイル システム (Windows など) では大文字と小文字が区別されないため、上記の動作が発生することに驚きはありませんが、間違っているようです。残念ながら、Java の仕様は、どのクラスが表示されるかについて、奇妙に非コミットです。Java 言語仕様 (JLS)、Java SE 7 Edition (セクション 6.6.1、166 ページ) は次のように述べています。

クラスまたはインターフェイス型が public と宣言されている場合、それが宣言されているコンパイル ユニット (§7.3) が監視可能であれば、任意のコードからアクセスできます。

セクション 7.3 で、JLS はコンパイル単位の可観測性を非常にあいまいな用語で定義しています。

定義済みパッケージ java とそのサブパッケージ lang および io のすべてのコンパイル ユニットは、常に監視可能です。他のすべてのパッケージの場合、ホスト システムはどのコンパイル ユニットが監視可能かを判断します。

Java 仮想マシン仕様も同様にあいまいです(セクション 5.3.1):

次の手順を使用してロードし、それによってブートストラップ クラス ローダーを使用して [バイナリ名] N で示される非配列クラスまたはインターフェイス C を作成します [...] それ以外の場合、Java 仮想マシンは引数 N をメソッドの呼び出しに渡します。ブートストラップ クラス ローダーを使用して、プラットフォームに依存する方法で C の表現とされるものを検索します。

これらはすべて、重要度の高い順に次の 4 つの質問につながります。

  1. すべての JVM で、デフォルトのクラスローダーによってどのクラスがロード可能であるかについて保証はありますか? つまり、java.lang と java.io 以外のクラスをロードしない、有効であるが縮退した JVM を実装できますか?
  2. 保証がある場合、上記の例の動作は保証に違反していますか (つまり、動作はバグですか)?
  3. HotSpotaA同時にロードする方法はありますか? カスタム クラス ローダーの作成は機能しますか?
4

3 に答える 3

19
  • すべてのJVMのブートストラップクラスローダーによってロード可能なクラスについての保証はありますか?

言語のコアビットと部分に加えて、実装クラスをサポートします。あなたが書いたクラスを含めることは保証されていません。(通常のJVMは、ブートストラップとは別のクラスローダーにクラスをロードします。実際、通常のブートストラップローダーはJARからクラスをロードします。これにより、クラスでいっぱいの大きな古いディレクトリ構造よりも効率的なデプロイが可能になります。)

  • 保証がある場合、上記の例の動作は保証に違反しますか(つまり、動作はバグです)?
  • 「標準」JVMにaとAを同時にロードさせる方法はありますか?カスタムクラスローダーの作成は機能しますか?

Javaは、クラスのフルネームをファイル名にマッピングしてクラスをロードし、ファイル名はクラスパスで検索されます。したがって、testcase.aに行きtestcase/a.class、にtestcase.A行きtestcase/A.classます。一部のファイルシステムはこれらを混同し、一方が要求されたときにもう一方を提供する場合があります。他の人はそれを正しく理解しています(特に、JARファイルで使用されるZIP形式の変形は完全に大文字と小文字を区別し、移植可能です)。Javaがこれについてできることは何もありません(IDEは.classファイルをネイティブFSから遠ざけることで処理できますが、実際に処理できるかどうかはわかりません。JDKの場合javacはそれほど賢くありません)。

ただし、ここで注意すべき点はそれだけではありません。クラスファイルは、話しているクラスを内部的に認識しています。ファイルに予期されたクラスがないということは、ロードが失敗し、NoClassDefFoundError受信したことを意味します。あなたが得たのは、問題(少なくともある意味での誤配置)が検出され、確実に処理されたということでした。理論的には、検索を続けることでそのようなことを処理できるクラスローダーを構築できますが、なぜわざわざするのでしょうか。クラスファイルをJAR内に配置すると、問題がはるかに確実に修正されます。それらは正しく処理されます。

より一般的には、この問題が実際に頻繁に発生する場合は、大文字と小文字を区別するファイルシステム(JenkinsなどのCIシステムを推奨)を使用してUnixで本番ビルドを実行し、大文字と小文字が異なるクラスに名前を付けている開発者を見つけてください。非常に混乱するので、停止させます。

于 2012-06-05T18:10:53.373 に答える
1

Donal の優れた説明に追加することはほとんどありませんが、次のフレーズについて簡単に考えさせてください。

... 大文字と小文字を区別しない同じ名前の Java クラス ...

一般に、名前と文字列はそれ自体で大文字と小文字を区別することはありません。可能なのは解釈だけです。そして第二に、Java はそのような解釈を行いません。

したがって、あなたが念頭に置いていたことの正しい言い回しは次のようになります。

... 大文字と小文字を区別しないファイルシステムでのファイル表現が同一の名前を持つ Java クラス ...

于 2012-06-06T07:48:40.720 に答える
-2

フォルダだけを考えないでください。

クラスに明示的な異なる名前空間 (「パッケージ」) を使用し、場合によってはフォルダーを使用してクラスに一致させます。

「パッケージ」について言及するとき、「*.JAR」ファイルを意味するのではなく、次の概念のみを意味します。

package com.mycompany.mytool;

// "com.mycompany.mytool.MyClass"

public class MyClass
{
   // ...
} // class MyClass

コードのパッケージを指定しない場合、Java ツール (コンパイラ、IDE など) は、すべてに対して同じグローバル パッケージを使用すると想定します。また、類似したクラスが複数ある場合は、検索するフォルダのリストがあります。

パッケージは、コード内の「仮想」フォルダーのようなもので、クラスパスまたは Java のインストール上のすべてのパッケージに適用されます。同じ ID を持つ複数のクラスを持つことができますが、それらが異なるパッケージにあり、検索するパッケージを指定しても問題はありません。

ちょうど私の 2 セント、Java コーヒー 1 杯に

于 2012-06-05T17:36:28.337 に答える