リソースとは、プログラムのコードの位置とは無関係な方法でプログラムがアクセスする必要のあるデータ (イメージ、オーディオ、テキストなど) です。Java プログラムでは、2 つのメカニズムでリソースにアクセスできます。アプレットの場合は、Applet.getCodeBase()
を使用してアプレットコードのベース URL を取得し、そのベース URL を相対パスで展開し、たとえば Applet.getAudioClip(url)
などの関数を使用して必要なリソースをロードします。アプリケーションの場合は、System.getProperty("user.home")
や System.getProperty("java.home")
などの「よく知られた場所」を使用し、「/lib/resource」を付加して、そのファイルを開きます。
クラス Class
および ClassLoader
のメソッドでは、リソースを特定するために、位置に依存しない方法を提供します。たとえば次のような場合のリソースを特定できます。
これらのメソッドでは、ローカライズされたリソースの位置を特定することはできません。ローカライズされたリソースは、国際化によってサポートされます。
リソースは文字列で特定され、この文字列はスラッシュ (/) で区切られた一連のサブ文字列と、末尾のリソース名で構成されます。各サブ文字列は、有効な Java 識別子である必要があります。リソース名は、shortName
または shortName.extension
という形式です。shortName
と extension
は、どちらも Java 識別子である必要があります。
リソースの名前は Java の実装には依存しません。特に、パスの区切り文字は必ずスラッシュ (/) です。ただし、リソースの内容が、ファイル、データベース、その他実際のリソースを含むオブジェクトにどのようにマップされるかについては、Java の実装が制御します。
リソース名の解釈は、クラスローダーのインスタンスと関連します。ClassLoader
クラスによって実装されるメソッドがこの解釈を行います。
システムリソースとは、システムに組み込まれているリソース、またはホストの実装が保持するリソースのことで、ローカルファイルシステムがその一例です。プログラムがシステムリソースにアクセスするには、ClassLoader
のメソッドである getSystemResource
および getSystemResourceAsStream
を使用します。
たとえば、一部の実装では、システムリソースを見つけるために CLASSPATH 内の項目を検索しなければならないことがあります。ClassLoader
メソッドによって CLASSPATH 内のディレクトリ、ZIP ファイル、または JAR ファイルのそれぞれのエントリでリソースファイルが検索され、ファイルが見つかると、InputStream
またはリソースの名前が返されます。ファイルが見つからなかった場合は、null が返されます。リソースは、クラスファイルをロードした位置と同じ CLASSPATH 項目にあるとはかぎりません。
クラスローダーでの getResource
の実装方法は、ClassLoader
クラスの詳細によって異なります。たとえば、AppletClassLoader
の場合は次のようになります。
すべてのクラスローダーは、クラスファイルを探すのと同じように、リソースをまずシステムリソースとして探します。このため、すべてのリソースをローカルに上書きすることが可能になります。リソース名には一意の名前を選択する必要があります (接頭辞として会社名やパッケージ名などを使用する)。
各クラスは一般的に、リソース名を表す際に、クラスのパッケージの完全修飾名を使用し、すべてのピリオド (.) をスラッシュ (/) に変換し、リソース名を name.extension
の形式で追加するという規則を使用します。この規則をサポートし、システムクラス (getClassLoader
が null を返すクラス) の詳細の処理を簡略化するために、Class
クラスには ClassLoader
の適切なメソッドを呼び出す 2 つの簡易メソッドが用意されています。
Class
クラスのメソッドに渡されるリソース名は、先頭が「/」で始まるものがあります。これは「絶対」名を表します。先頭が「/」ではないリソース名は「相対」名です。
リソースを探す際、絶対名は、先頭の「/」が除去されただけの状態で、適切な ClassLoader
のメソッドに渡されます。相対名は、前述の規則に従って修正されたあと、ClassLoader
のメソッドに渡されます。
Class
クラスでは、リソースをロードするためのメソッドをいくつか実装しています。
getResource()
メソッドはリソースの URL を返します。この URL およびその表現は、実装および JVM に固有です。つまり、ある実行時インスタンスで取得された URL は、別の実行時インスタンスでは動作しない可能性があります。プロトコルは通常、リソースをロードする ClassLoader
に特有です。リソースが存在しない場合や、セキュリティー上の理由で不可視である場合、メソッドは null を返します。
クライアントコードでリソースの内容を InputStream
として読み込むには、その URL に openStream()
メソッドを適用します。これはよく行われるため、getResourceAsStream()
が Class
と ClassLoader
に追加されています。getResourceAsStream()
は入出力例外をキャッチして null の InputStream
を返しますが、それ以外は getResourceAsStream()
は getResource().openStream()
と同じです。
クライアントコードは、URL に対して java.net.URL.getContent()
メソッドを適用することによって、リソースの内容をオブジェクトとして要求することもできます。このメソッドは、リソース内にイメージデータが含まれている場合などに便利です。イメージの場合、結果は Image
オブジェクトではなく、awt.image.ImageProducer
オブジェクトになります。
getResource
および getResourceAsStream
メソッドは、指定された名前のリソースを検索します。これらのメソッドは、指定された名前のリソースが見つからないと null を返します。指定されたクラスに関連するリソースを検索するための規則は、そのクラスの ClassLoader によって実装されます。Class
のメソッドは、命名規則の適用後に ClassLoader
メソッドに委譲されます。リソース名が「/」で始まる場合は、その名前がそのまま使用されます。そうでない場合は、すべてのピリオド (.) がスラッシュ (/) に変換されてから、パッケージ名が先頭に付加されます。
public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResourceAsStream(name); // A system class. } return cl.getResourceAsStream(name); } public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResource(name); // A system class. } return cl.getResource(name); }
resolveName
メソッドによって、名前が絶対パスでない場合はパッケージ名の接頭辞が追加され、絶対パスの場合は先頭の「/」が削除されます。一般的ではありませんが、同じリソースを共有するクラスを別々のパッケージに含めることもできます。
private String resolveName(String name) { if (name == null) { return name; } if (!name.startsWith("/")) { Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { name = baseName.substring(0, index).replace('.', '/') + "/" + name; } } else { name = name.substring(1); } return name; }
ClassLoader
クラスには、2 組の、リソースにアクセスするメソッドがあります。このうちの一方は、リソースの InputStream
を返します。もう一方は、URL を返します。InputStream
を返す各メソッドは、使いやすく、用途も多くあります。もう一方の URL を返す各メソッドを使うと、Image オブジェクトや AudioClip オブジェクトなど、さらに複雑な情報にアクセスできます。
ClassLoader
は、クラスを管理するのと同様の方法で、リソースを管理します。ClassLoader
はリソース名とその内容のマップ方法を制御します。またシステムクラスの場合と同じように、ClassLoader
はシステムリソースにアクセスするためのメソッドも提供します。Class
クラスには、機能を ClassLoader
クラスの各メソッドに任せる簡易メソッドがあります。
多くの Java プログラムは、I18N (ローカリゼーション) API を使用してこれらのメソッドに間接的にアクセスします。Class
のメソッドを介してアクセスするプログラムもあります。ClassLoader
クラスのメソッドを、直接呼び出すことはほとんどありません。
ClassLoader
のメソッドは、受け取った String をリソース名として使用します。絶対名と相対名との変換は行いません (Class のメソッドを参照)。名前の先頭には「/」を付ける必要はありません。
システムリソースは、ホスト実装によって直接処理されるリソースです。たとえば、CLASSPATH に配置されている場合があります。
リソースの名前は、一連の識別子を「/」で区切った形式です。Class
クラスでは、リソースへアクセスする簡易メソッドを提供しています。これらのメソッドでは、パッケージ名をリソースのショート名に接頭辞として追加するための規則を実装しています。
リソースは InputStream
または URL としてアクセスできます。
getSystemResourceAsStream
メソッドは、指定されたシステムリソースに対する InputStream を返します。そのリソースが見つからない場合は、null を返します。リソース名は、任意のシステムリソースです。
getSystemResource
メソッドは、指定された名前を持つシステムリソースを検索します。このメソッドは、リソースへの URL を返します。リソースが見つからない場合は null を返します。その URL を使用して java.net.URL.getContent()
を呼び出すと、ImageProducer
、AudioClip
、InputStream
などのオブジェクトが返されます。
getResourceAsStream
メソッドは、指定されたリソースに対する InputStream
を返します。そのリソースが見つからない場合は、null を返します。
getResource
メソッドは、指定された名前を持つリソースを検索します。このメソッドは、リソースへの URL を返します。リソースが見つからない場合は null を返します。その URL を使用して java.net.URL.getContent()
を呼び出すと、ImageProducer
、AudioClip
、InputStream
などのオブジェクトが返されます。
getResource()
は、情報へのアクセスを提供するため、セキュリティーの規則が適切に定義および構築されている必要があります。セキュリティー上の配慮により、あるセキュリティーコンテキストであるリソースへのアクセスが許可されていない場合はgetResource()
メソッドは、あたかもそのリソースが存在しないかのように失敗する (null を返す) ようになっています。これは、存在性攻撃に対する配慮です。
クラスローダーは、セキュリティーおよびパフォーマンス上の理由から、.class ファイルの内容へのアクセスは提供しません。.class ファイルの URL を取得できるかどうかは、次に示す詳細によって異なります。
システム以外のクラスローダーによって検索されるリソースに関する、セキュリティーの問題または制限は指定されていません。AppletClassLoader
は、あるソースの場所からロードされた情報への個々のアクセスまたは JAR ファイルによるグループでのアクセスを提供します。このため、AppletClassLoader
は、getResource()
を使って複数の URL を扱うときは、同じ checkConnect()
を適用する必要があります。
システムの ClassLoader
は、CLASSPATH の情報へのアクセスを提供します。CLASSPATH には、ディレクトリや JAR ファイルがあります。JAR ファイルは意図的に作成されるので、意図せずに生成されるディレクトリとは重要性が異なるといえます。特に、ディレクトリから情報を取得する場合は、JAR ファイルから取得する場合よりも厳密に行います。
リソースがディレクトリ内にある場合は、次のようになります。
getResource()
の呼び出しで、該当するファイルをユーザーからアクセス可能にするかどうかを決定するために File.exists()
を使います。File.exists()
では、セキュリティーマネージャーの checkRead()
メソッドが使われます。getResourceAsStream()
の場合も同じです。リソースが JAR ファイル内にある場合は、次のようになります。
getResource()
の呼び出しが成功します。getResourceAsStream()
の呼び出しは成功します。対応する URL に対する java.net.URL.getContent()
についても同じです。このセクションでは、クライアントコードの例を提供します。1 番目の例では「絶対リソース」名と従来のメカニズムを使用して Class
クラスのオブジェクトを取得しています。
package pkg; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; class Test { private static final String absName = "/pkg/mumble.baf"; public static void test1() { Class c=null; try { c = Class.forName("pkg.Test"); } catch (Exception ex) { // This should not happen. } InputStream s = c.getResourceAsStream(absName); // do something with it. } public void test2() { InputStream s = this.getClass().getResourceAsStream(absName); // do something with it. } }
この例では「相対リソース」名とメカニズムを使用して Class
オブジェクトを取得しています。このメカニズムは、コンパイル時に -experimental
フラグを使用すると利用できます。
package pkg; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; class Test { private static final String relName = "mumble.baf"; public static void test1() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. } public void test2() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. }