この記事では、Java の動的クラスのロードとリロードについて詳しく説明します。必要な方は参考にしていただければ幸いです。
Java では実行時にクラスをロードおよび再ロードできますが、それは私たちが考えているほど単純ではありません。この記事では、Java でクラスをロードおよびリロードするタイミングと方法について説明します。
クラスの動的ロードが Java リフレクションの一部であるか、Java コアの一部であるかについて議論することができます。とにかく、他に置く場所がなかったので、Java Reflection に置きました。
クラス ローダー
Java プログラム内のすべてのクラスは、java.lang.ClassLoader のいくつかのサブクラスを使用してロードされます。したがって、動的にロードされるクラスも java.lang.ClassLoader のサブクラスを使用する必要があります。
クラスがロードされると、そのクラスが参照するクラスもロードされます。クラス ロード モードでは、必要なクラスがすべてロードされるまで再帰的にロードされます。これはアプリケーションのすべてのクラスではない可能性があります。参照されていないクラスは、参照されるまでロードされません。
クラス読み込み階層
Java のクラス読み込みは階層に編成されます。スタンドアロン ClassLoader を作成する場合は、親 ClassLoader を提供する必要があります。 ClassLoader がクラスのロードを要求されると、その親 ClassLoader にクラスのロードを要求します。親クラス ローダーがクラスを見つけられない場合、子クラス ローダーはそれ自体をロードしようとします。
クラスのロード
クラスローダーがクラスをロードする手順は次のとおりです。
-
クラスがロードされているかどうかを確認します
クラスがロードされていない場合は、親クラス ローダーにロードを要求します。
- ##親クラス ローダーがクラスをロードできない場合は、現在のクラス ローダーをロードする
クラスをオーバーロードできるクラス ローダーを実装する場合は、このシーケンスから少し逸脱する必要があります。親クラスローダーは、再ロードされるクラスをロードするように要求されるべきではありません。それについては後で詳しく説明します。
クラスの動的ロード
クラスの動的ロードは非常に簡単です。必要なのは、ClassLoader を取得し、そのloadClass() メソッドを呼び出すことだけです。例は次のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class MainClass {
public static void main(String[] args){
ClassLoader classLoader = MainClass. class .getClassLoader();
try {
Class aClass = classLoader.loadClass( "com.jenkov.MyClass" );
System.out.println( "aClass.getName() = " + aClass.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
|
ログイン後にコピー
動的クラスの再ロード
動的クラスの再ロードにはいくつかの課題があります。 Java の組み込みクラス ローダーは、クラスをロードする前に、クラスがロードされているかどうかを常にチェックします。したがって、Java の組み込みクラス ローダーを使用してクラスをリロードすることはできません。クラスをリロードするには、独自の ClassLoader サブクラスを実装する必要があります。
クラスローダーのカスタムサブクラスにも課題があります。ロードされたすべてのクラスをリンクする必要があります。このメソッドは最終的なメソッドであるため、ClassLoader サブクラスによってオーバーライドすることはできません。 solve() メソッドでは、ClassLoader インスタンスがクラスに 2 回リンクすることはできません。したがって、クラスを再ロードする必要がある場合は常に、ClassLoader クラスのインスタンスを再作成する必要があります。不可能ではありませんが、リロードするクラスをいつ設計するかを知っておく必要があります。
クラスのオーバーロード コード設計
前述のとおり、指定されたクラスをロードする ClassLoader を使用してこのクラスをリロードすることはできません。したがって、このクラスをロードするには別の ClassLoader を使用する必要があります。ただし、これにより新たな問題が発生します。
Java プログラムにロードされる各クラスは、完全修飾名 (パッケージ名クラス名) によって識別され、ClassLoader インスタンスによってロードされます。これは、クラス ローダー A によってロードされたクラス MyObject が、クラス ローダー B によってロードされた同じクラス MyObject とは異なることを意味します。シミュレーション コードは次のとおりです。
1 2 | MyObject object = (MyObject)
myClassReloadingFactory.newInstance( "com.jenkov.MyObject" );
|
ログイン後にコピー
コード内でクラス MyObject がオブジェクト型の変数としてどのように参照されているかに注目してください。これにより、MyObject クラスは、このクラスの常駐コードを既にロードしたクラス ローダーによってロードされます。
myClassReloadingFactory オブジェクト ファクトリが MyObject をロードするために常駐コードとは異なるクラス ローダーを使用する場合、リロードされたオブジェクト型変数 MyObject を MyObject 型にキャストすることはできません。 2 つの MyObject は異なるクラス ローダーによってロードされるため、同じ完全修飾名であっても、異なるクラスとみなされます。オブジェクトのクラスを別のクラスへの参照としてキャストしようとすると、ClassCastException がスローされます。
この制限を回避することは可能ですが、次の 2 つの方法でコードを変更する必要があります:
- インターフェイスを変数タイプとして使用し、実装クラスのみをリロードする
- スーパークラスを変数タイプとして使用し、サブクラスのみをリロードします
サンプル コードは次のとおりです:
1 2 | MyObjectInterface object = (MyObjectInterface)
myClassReloadingFactory.newInstance( "com.jenkov.MyObject" );
|
ログイン後にコピー
1 2 | MyObjectSuperclass object = (MyObjectSuperclass)
myClassReloadingFactory.newInstance( "com.jenkov.MyObject" );
|
ログイン後にコピー
変数タイプがインターフェイスまたはスーパークラスの場合、上記のコードは正常に実行され、実装またはサブクラスがリロードされるときにインターフェイスまたはスーパークラスはリロードされません。
上記のコードが正常に実行されるようにするには、インターフェイスまたはスーパー クラスがその親クラスによってロードされるように、独自のクラス ローダーを実装する必要があります。クラス ローダーが MyObject をロードするように要求されると、MyObjectInterface インターフェイスまたは MyObjectSuperclass クラスをロードするように要求されます。これらは MyObject クラスによって内部的に参照されるためです。クラス ローダーは、インターフェイスまたはスーパークラスをロードしたのと同じクラス ローダーにクラスのロードを委任する必要があります。
类加载器加载/重新加载示例
上文包含了很多内容。让我们看一下简单的示例。下面是一个简单的ClassLoader子类。注意它如何将类加载委托给它的父类,除了它想要重装的一个类之外。如果类加载被委派给了它的父类,它以后将不能被重新加载。记住,一个类只能被同一个ClassLoader实例加载。
如前所述,这只是一个示例,它显示了类加载器的行为的基本知识。这并不是一个你的类加载器的生产就绪的模板。你的类加载器可能并不仅限于一个类,可能是一个你想要重新加载的类的集合。此外,你也不能硬编码class path。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class MyClassLoader extends ClassLoader{
public MyClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if (! "reflection.MyObject" .equals(name))
return super.loadClass(name);
try {
String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +
"classes/reflection/MyObject.class" ;
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while (data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass( "reflection.MyObject" ,
classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
|
ログイン後にコピー
下面是使用MyClassLoader的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static void main(String[] args) throws
ClassNotFoundException,
IllegalAccessException,
InstantiationException {
ClassLoader parentClassLoader = MyClassLoader. class .getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass( "reflection.MyObject" );
AnInterface2 object1 =
(AnInterface2) myObjectClass.newInstance();
MyObjectSuperClass object2 =
(MyObjectSuperClass) myObjectClass.newInstance();
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass( "reflection.MyObject" );
object1 = (AnInterface2) myObjectClass.newInstance();
object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}
|
ログイン後にコピー
reflection.MyObject类是由自定义类加载器加载的。注意,它是如何继承一个超类、实现一个接口的。这只是为了这个例子。在你的代码中,只需要两个中的一个,继承超类或实现接口。
1 2 3 4 | public class MyObject extends MyObjectSuperClass implements AnInterface2{
}
|
ログイン後にコピー
以上がJava 動的クラスのロードとリロードの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。