この記事では、Java の動的クラスのロードとリロードについて詳しく説明します。必要な方は参考にしていただければ幸いです。
Java では実行時にクラスをロードおよび再ロードできますが、それは私たちが考えているほど単純ではありません。この記事では、Java でクラスをロードおよびリロードするタイミングと方法について説明します。
クラスの動的ロードが Java リフレクションの一部であるか、Java コアの一部であるかについて議論することができます。とにかく、他に置く場所がなかったので、Java Reflection に置きました。
Java プログラム内のすべてのクラスは、java.lang.ClassLoader のいくつかのサブクラスを使用してロードされます。したがって、動的にロードされるクラスも java.lang.ClassLoader のサブクラスを使用する必要があります。
クラスがロードされると、そのクラスが参照するクラスもロードされます。クラス ロード モードでは、必要なクラスがすべてロードされるまで再帰的にロードされます。これはアプリケーションのすべてのクラスではない可能性があります。参照されていないクラスは、参照されるまでロードされません。
Java のクラス読み込みは階層に編成されます。スタンドアロン ClassLoader を作成する場合は、親 ClassLoader を提供する必要があります。 ClassLoader がクラスのロードを要求されると、その親 ClassLoader にクラスのロードを要求します。親クラス ローダーがクラスを見つけられない場合、子クラス ローダーはそれ自体をロードしようとします。
クラスローダーがクラスをロードする手順は次のとおりです。
クラスがロードされているかどうかを確認します
クラスがロードされていない場合は、親クラス ローダーにロードを要求します。
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(); } }
クラスローダーのカスタムサブクラスにも課題があります。ロードされたすべてのクラスをリンクする必要があります。このメソッドは最終的なメソッドであるため、ClassLoader サブクラスによってオーバーライドすることはできません。 solve() メソッドでは、ClassLoader インスタンスがクラスに 2 回リンクすることはできません。したがって、クラスを再ロードする必要がある場合は常に、ClassLoader クラスのインスタンスを再作成する必要があります。不可能ではありませんが、リロードするクラスをいつ設計するかを知っておく必要があります。
Java プログラムにロードされる各クラスは、完全修飾名 (パッケージ名クラス名) によって識別され、ClassLoader インスタンスによってロードされます。これは、クラス ローダー A によってロードされたクラス MyObject が、クラス ローダー B によってロードされた同じクラス MyObject とは異なることを意味します。シミュレーション コードは次のとおりです。
MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
myClassReloadingFactory オブジェクト ファクトリが MyObject をロードするために常駐コードとは異なるクラス ローダーを使用する場合、リロードされたオブジェクト型変数 MyObject を MyObject 型にキャストすることはできません。 2 つの MyObject は異なるクラス ローダーによってロードされるため、同じ完全修飾名であっても、異なるクラスとみなされます。オブジェクトのクラスを別のクラスへの参照としてキャストしようとすると、ClassCastException がスローされます。
この制限を回避することは可能ですが、次の 2 つの方法でコードを変更する必要があります:
MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
上記のコードが正常に実行されるようにするには、インターフェイスまたはスーパー クラスがその親クラスによってロードされるように、独自のクラス ローダーを実装する必要があります。クラス ローダーが MyObject をロードするように要求されると、MyObjectInterface インターフェイスまたは MyObjectSuperclass クラスをロードするように要求されます。これらは MyObject クラスによって内部的に参照されるためです。クラス ローダーは、インターフェイスまたはスーパークラスをロードしたのと同じクラス ローダーにクラスのロードを委任する必要があります。
上文包含了很多内容。让我们看一下简单的示例。下面是一个简单的ClassLoader子类。注意它如何将类加载委托给它的父类,除了它想要重装的一个类之外。如果类加载被委派给了它的父类,它以后将不能被重新加载。记住,一个类只能被同一个ClassLoader实例加载。
如前所述,这只是一个示例,它显示了类加载器的行为的基本知识。这并不是一个你的类加载器的生产就绪的模板。你的类加载器可能并不仅限于一个类,可能是一个你想要重新加载的类的集合。此外,你也不能硬编码class path。
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的示例:
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(); //create new class loader so classes can be reloaded. classLoader = new MyClassLoader(parentClassLoader); myObjectClass = classLoader.loadClass("reflection.MyObject"); object1 = (AnInterface2) myObjectClass.newInstance(); object2 = (MyObjectSuperClass) myObjectClass.newInstance(); }
reflection.MyObject类是由自定义类加载器加载的。注意,它是如何继承一个超类、实现一个接口的。这只是为了这个例子。在你的代码中,只需要两个中的一个,继承超类或实现接口。
public class MyObject extends MyObjectSuperClass implements AnInterface2{ //... body of class ... override superclass methods // or implement interface methods }
以上がJava 動的クラスのロードとリロードの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。