web-dev-qa-db-ja.com

Java:Class.forNameとClassLoader.loadClassの違い

最近、私に考えさせられたコードに出会いました。次の違いは何ですか?

Class theClass = Class.forName("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();

そして:

Class theClass = ClassLoader.loadClass("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();

彼らは同義ですか?特定の状況では、一方が他方よりも望ましいですか?これらの2つの方法を使用することの利点と禁止事項は何ですか?

前もって感謝します。

40
IAmYourFaja

Class.forName()は常に呼び出し元のClassLoaderを使用しますが、ClassLoader.loadClass()は異なるClassLoaderを指定できます。 Class.forNameはロードされたクラスも初期化すると信じていますが、ClassLoader.loadClass()アプローチはすぐにはそれを行いません(初めて使用するまで初期化されません)。

初期化動作の概要を確認するときにこの記事を見つけました。これはあなたが探しているほとんどの情報を持っているようです:

http://www.javaworld.com/javaworld/javaqa/2003-03/01-qa-0314-forname.html

この使用法はかなりクールですが、私はそれを使用したことがありません。

Class.forName(String, boolean, ClassLoader)

ClassLoaderを指定することができ、booleanパラメーターは、クラスがロードされたときに初期化するかどうかを定義します。

18
Shaun

ショーンの答えは、いくつかの省略/小さなエラーを除いて、多かれ少なかれ正しいです。

  • Class.forNameはクラスをClassLoaderに関連付けます(他の親が実際にそれをロードするかどうかに関係なく)。したがって、ClassLoader.findLoadedClassは次回成功します。これは非常に重要なポイントです。ほとんどのClassLoaderは、検索/検索部分全体をバイパスする最初のステートメントとしてClass c = findLoadedClass(name); if (c!=null) return c;を試みます。 ClassLoader.loadを直接呼び出しても、ロードされたクラスにクラスは追加されません。

ClassLoaderのグラフのような構造を介してロードされる場合、つまり最初にルックアップするためだけに親を使用しない場合、このケースは意味を持ちます。

  • クラスの初期化は、ClassLoaderのloadClassで実行されます(if (resolve) resolveClass(c);などのコード)。ClassLoaderは、推奨されていないが可能だと思われる解決を実際にスキップできます。

これら2つの方法を使用する場合の利点と禁止事項は何ですか?

ClassLoader.loadClass(String)が必要な理由が非常に明確でない限り、直接使用しないでください。他のすべての場合、常にClass.forName(name, true, classLoader)に依存します。

全体的なクラスの読み込みはアートの隣にあり、簡単な答えでカバーすることはできません(アート部分について冗談を言うことはありません)

11
bestsss

使用時にClass.forName("SomeImpl")を使用すると、現在のクラスローダー(つまり、メソッドを呼び出しているクラスのローダー)を介してクラスを取得します。また、 初期化 クラス。 Class.forName("SomeImpl", true, currentLoader)の呼び出しと実質的に同じです。ここで、currentLoaderは呼び出し元のクラスローダーです。詳細を参照してください こちら

2番目の方法では、最初にクラスローダーを選択する必要があります。静的メソッドであるnotであるため、ClassLoader.loadClass("SomeImpl")のように記述しないでください。次のようなものが必要です

_final ClassLoader cl = this.getClass().getClassLoader();
Class theClass = cl.loadClass("SomeImpl");
_

ClassLoaderのサブクラスは、loadClassではなく findClass メソッドをオーバーライドする必要があることに注意してください。これは、(保護された)メソッドloadClass("SomeImpl", false)を呼び出すのと同じです。2番目の引数は、リンクを行うかどうかを示します。

さらに微妙な違いがあります... loadClassメソッドは、Java Language Specificationで指定されているバイナリクラス名を想定していますが、forNameはプリミティブ型または配列クラスを表す文字列。

全般的には、必要に応じて_Class.forName_を使用し、必要に応じて特定のクラスローダーを指定し、初期化する必要があるかどうかを指定してから、残りの部分を実装に任せることをお勧めします。クラスローダを直接使用すると、jarまたはクラスパスでリソースを見つけるのに適しています。

3
G_H

この行はコンパイルされません:

Class theClass = ClassLoader.loadClass("SomeImpl");

loadClassはClassLoaderの静的メソッドではないためです。

この問題を解決するには、次の3つの方法のいずれかでClassLoaderオブジェクトを作成します。

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = Main.class.getClassLoader();      // Assuming in class Main
ClassLoader classLoader = getClass().getClassLoader();      // works in any class

次に呼び出します:

Class theClass = classLoader.loadClass("SomeImpl");

-dbednar

1
joe

loadClass()メソッドをstaticメソッドとして呼び出すことはできません。 ClassLoaderのサブクラスを作成し、操作を行うための追加のメソッドをいくつか用意します。 extends ClassLoaderクラスによって独自のクラスローダーを作成できます。機能的には両方の方法は同じです。

0
Hari