この記事では、主に Java のクラスローダーの関連情報を詳しく紹介します。興味のある方は参考にしてください
Java の動的性質からクラスローディングメカニズムまで
Java は動的です。言語。では、この「ダイナミック」をどうやって理解すればよいのでしょうか?言い換えれば、言語はどのような特徴を持っていれば動的言語と呼ばれるのでしょうか? Javaの場合、これが私がそれを理解する方法です。
JVM (Java 仮想マシン) は、ローカルのマシンコード命令を実行しませんが、バイトコードと呼ばれるタイプの命令 (クラス ファイルに存在します) を実行します。これには、実際にバイトコードを実行する前に、仮想マシンが関連するクラス ファイルをメモリにロードする必要があります。仮想マシンは、実行時に将来どのクラス ファイルが使用されるかわからないため、必要なすべてのクラス ファイルを一度にロードしません。クラスが使用されるたびに、このクラスに関連するクラス ファイルが実行時に「動的」にロードされます。これが、Java が動的言語と呼ばれる根本的な理由です。クラスを動的にロードするだけでなく、クラスも動的に初期化され、動的にリンクされます。動的初期化と動的リンクについては、他の記事で紹介されています。この記事ではクラスの読み込みについてのみ説明します。
この記事で紹介するクラスローダー(ClassLoader)は、JVMにクラスをロードする役割を担うため、クラスローダーはJVMに欠かせない重要なコンポーネントです。
Javaのクラスローダーとクラスローダーの動作原理
Javaには3つのクラスローダーがあります(javaseを参照)。各クラスローダーは作成時に対応するディレクトリを指定しているので、各クラスローダーがクラスをロードする場所が決定されるということは、ClassLoaderクラスにgetTargetPath()などのメソッドがあるはずだと思います。 jdk ドキュメントを調べたところ、それらは利用できないことがわかりました。これら 3 つのクラス ローダーとそれらに対応するパスは次のとおりです:
* AppClassLoader -- クラスパスで指定されたパスにクラスをロードします
* ExtClassLoader -- jre/lib/ext ディレクトリまたは java.ext.dirs システムに定義されているクラスをロードしますproperty ディレクトリ内のクラス
* BootStrapそれでは、クラスローダーはどのように機能しますか? ? jdk内のClassLoaderクラスのソースコードを参照できます。このクラスの実装では、まず、loadClass メソッドがクラスをロードします。このメソッドは、クラス ファイルのデータを読み取って返します。その後、loadClass メソッドが続行されます。返されたデータは、仮想マシンの実行時に認識できる型情報に処理されます。したがって、独自のクラス ローダーを開発する場合は、jdk で ClassLoader クラスを継承し、findClass メソッドをオーバーライドするだけで済みます。残りの作業は親クラスが行います。他の Java プラットフォームには、Javaee プラットフォームの Tomcat サーバーなど、独自のニーズに応じて独自のクラス ローダーが実装されており、Android プラットフォームの Dalvik 仮想マシンも独自のクラス ローダーを定義しています。
仮想マシンがクラスをロードするには 2 つの方法があります。1 つは前述の ClassLoader.loadClass() メソッドで、もう 1 つはリフレクション API の Class.forName() メソッドを使用する方法です。 Class.forName() メソッドも ClassLoader として使用されます。 Class クラスの for
Name メソッド1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
クラス ローダーには、委譲、可視性、および単一性という 3 つの特性があり、これらについて説明します。これら 3 つの機能の紹介は次のとおりです:
* 委任メカニズムとは、クラスをロードするリクエストを親クラス ローダーに渡すことを指します。親クラス ローダーがクラスを見つけられない場合、またはクラスをロードできない場合は、クラスを再度ロードします。 。
* 可視性の原則は、子クラスローダーは親クラスローダーによってロードされたすべてのクラスを見ることができますが、親クラスローダーは子クラスローダーによってロードされたクラスを見ることができないということです。 * 統一の原則は、クラスが 1 回だけロードされることを意味します。これは、委任メカニズムにより、親クラス ローダーによってロードされたクラスが子クラス ローダーによって再度ロードされないことが保証されるためです。
その中で、委任メカニズムは、他の資料では、クラスローダーの親委任モデルとも呼ばれます。実際には、同じ意味です。加法性と統一性は委任メカニズムに依存します。
次のコードは、クラス ローダーの委任メカニズムをテストします:
1 2 3 4 5 6 7 8 9 10 |
|
系统类加载器和线程上下文类加载器
在java中,还存在两个概念,分别是系统类加载器和线程上下文类加载器。
其实系统类加载器就是AppClassLoader应用程序类加载器,它两个值得是同一个加载器,以下代码可以验证:
1 2 3 4 5 6 |
|
这两个类加载器对应的输出,不仅类名相同,连对象的哈希值都是一样的,这充分说明系统类加载器和应用程序类加载器不仅是同一个类,更是同一个类的同一个对象。
每个线程都会有一个上下文类加载器,由于在线程执行时加载用到的类,默认情况下是父线程的上下文类加载器, 也就是AppClassLoader。
1 2 3 4 5 6 7 8 |
|
这个子线程在执行时打印的信息为sun.misc.Launcher$AppClassLoader@19821f,可以看到和主线程中的AppClassLoader是同一个对象(哈希值相同)。
也可以为线程设置特定的类加载器,这样的话,线程在执行时就会使用这个特定的类加载器来加载使用到的类。如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
在线程运行之前,为它设置了一个匿名内部类的类加载器对象,线程运行时,输出的信息为:jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74,也就是我们设置的那个类加载器对象。
类加载器的可见性
下面验证类加载器的可见性,也就是 子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
以下代码使用父加载器ExtClassLoader加载子加载器AppClassLoader路径下的类,由输出可知,是不可能实现的。
1 2 3 4 5 6 7 8 |
|
输出为 :1 -- 未找到类 。说明抛出了ClassNotFoundException异常。原因是让ExtClassLoader加载 jg.zhang.java.testConcurrent.Person这个类因为这个类不在jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下,所以抛出ClassNotFoundException。所以父加载器不能加载应该被子加载器加载的类。也就是说这个类在父加载器中不可见。这种机制依赖于委派机制。
下面代码使用子加载器AppClassLoader 加载父加载器BootStrap中的类,这是可以实现的。
1 2 3 4 5 6 7 8 |
|
输出为:2 -- 类被加载。说明成功加载了String类。是因为在指定由AppClassLoader加载String类时,由AppClassLoader一直委派到BootStrap加载。虽然是由子加载器的父加载器加载的,但是也可以说,父加载器加载的类对于子加载器来说是可见的。这同样依赖于委派机制。其实在虚拟机启动初期,java.lang.String已经被BootStrap预加载了,这时再次加载,虚拟机发现已经加载,不会再重复加载。这同时也证明了类加载器的单一性。
测试代码
到此为止,类加载器的知识就全部讲完了。以下是整个测试代码:
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
|
以上がJavaのクラスローダーコードの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。