getResourceメソッドに指定するリソース名
指定した名前を持つリソース(ファイルなど)のURLを取得するためのgetResourceメソッドがjava.lang.Classクラスとjava.lang.ClassLoaderクラスで定義されているが、パスの指定方法が時々わからなくなるので、ちょっと整理してみる。
まず、java.lang.ClassクラスのgetResourceメソッドのJavaDocを見ると、以下のように書いてある。
The rules for searching resources associated with a given class are implemented by the defining class loader of the class. This method delegates to this object's class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResource(java.lang.String).
つまり、基本的には、このメソッドはjava.lang.ClassLoaderクラスのgetResourceメソッドへ処理を委譲する。
java.lang.ClassクラスのJavaDocをさらに見ていくと、委譲する際のリソース名に対する処理について書いてある。
Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:
- If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
- Otherwise, the absolute name is of the following form:
modified_package_name/nameWhere the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').
つまり、以下の2つが同じリソースを取得するということ (この辺がいつも混乱する)。
Hoge.class.getResource("/hoge.txt"); Hoge.class.getClassLoader().getResource("hoge.txt");
以下の2つは異なる (厳密には、Hogeクラスのパッケージ名に依存する)。
Hoge.class.getResource("/hoge.txt"); Hoge.class.getResource("hoge.txt");
java.lang.ClassクラスのgetResourceメソッドの処理を単純化すると以下のような感じ。
if (name.startsWith("/")) { name = name.substring(1); } else { String clsName = this.getName(); // "this" is an instance of java.lang.Class class int index = clsName.lastIndexOf("."); if (index != -1) { name = clsName.substring(0, index).replaceAll("[.]", "/") + "/" + name; } } ClassLoader cl = getClassLoader(); if (cl != null) { return cl.getResource(name); } else { return ClassLoader.getSystemResource(name); }
java.lang.ClassLoaderクラスのgetResourceメソッドの方は少しだけ厄介で、シンプルに
public class Hoge { public static void main(String[] args) { String name1 = "foo.txt"; String name2 = "/" + name1; System.out.println("1: " + Hoge.class.getClassLoader().getResource(name1)); System.out.println("2: " + Hoge.class.getClassLoader().getResource(name2)); } }
とした場合には、2つめはnullが返されるが、java.lang.ClassLoaderクラスのgetResourceメソッドやfindResourceメソッドをオーバーライドしたクラスローダーを作ってしまえばこの辺の動作を変えることができてしまう‥