別々の ClassLoader にロードされたクラスのフィールドは別になる
うんまぁ、元記事がひどいのには異存は無いんだけどね。
「クラス」メソッドじゃないっ!「静的」メソッドなんだ!!
はぁ、で、そのこころは。
つまり C で言う関数内の static な変数とは、関数インスタンスを作らなくても生成されていて、インスタンスの寿命と関係なしにある変数だから、同様にクラスインスタンスを作らなくても生成されていて、クラスインスタンスの寿命と関係なしにあるメソッド・フィールドのことだよ、と言う立ち位置です。
なるほど。って、「クラスインスタンスを作らなくても生成されていて」って、んなわけない。Javaのクラスにはロードと初期化のステップがあるわけだが、すくなくともロードされるまではクラスフィールド用の領域は確保されていないはずだし、ロードされたクラスごとにクラスフィールドを持っている。つまり、クラスメソッドもクラスフィールドもちゃんとクラスに関連付けられているわけであるが、普通に使っている場合はクラスインスタンスはクラスインスタンスは一度ロードされたインスタンスを使いまわすようになっているから、そうではないように見えるだけの話。
実際、Tomcat のような Servlet Container などでは Webapp ごとに異なる ClassLoader を使っているので、同じクラスでも別の Webapp でロードされたクラスとはまったく異なるクラスとして扱われ、それぞれでクラスフィールドを持つことになる。
ということで、クラスフィールドがクラスオブジェクトに関連付けられていることを示すサンプルでも。
下記のソースを ClassLoaderTest.java、 Target.java として保存して、
% mkdir classes ext % javac -d ext Target.java % javac -d classes ClassLoaderTest.java % java -cp classes ClassLoaderTest
として実行する。
import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Method; public class ClassLoaderTest { public static void main(String[] args) throws Exception { File classpath = new File("ext"); URL[] urls = new URL[] { classpath.toURL() }; ClassLoader base = ClassLoaderTest.class.getClassLoader(); ClassLoader cl1 = new URLClassLoader(urls, base); ClassLoader cl2 = new URLClassLoader(urls, base); Class c1 = Class.forName("Target", true, cl1); Class c2 = Class.forName("Target", true, cl2); invoke(c1); invoke(c2); } private static void invoke(Class cls) throws Exception { Method show = cls.getMethod("show"); show.invoke(null); Object obj = cls.newInstance(); Method get = cls.getMethod("get"); System.out.println(get.invoke(obj)); } }
public class Target { private static final double r; static { r = Math.random(); System.out.println("Target initialized"); } public static void show() { System.out.println(r); } public double get() { return r; } }