ダークホースプログラマー_クラスローダ
10530 ワード
--------androidトレーニング、JAvaトレーニング、java学習型技術ブログ、あなたとの交流を楽しみにしています!---------
クラスローダとは
クラスローダはクラスをロードするツールで、私たちがプログラムでクラスを使用するとき、仮想マシンはクラスローダを通じてclassファイルをメモリにロードし、一連の処理を行った後、バイトコードになって私たちのために使用します.
Javaでデフォルトのクラスローダ
Java仮想マシンには複数のクラスローダをインストールできます.システムのデフォルトでは3つの主要クラスローダがあります.各クラスは特定の場所のクラスをロードします.BootStrap、ExtClassLoader、AppClassLoaderです.
クラス・ローダもjavaクラスです.他のjavaクラス・ローダ自体もクラス・ローダにロードされるため、Javaクラスではないクラス・ローダが必要であることは明らかです.これがBootStrapです.
コード例:各種のローダを表示する
package cn.itcast.classloader;
/**
* テストクラスローダのクラス
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) {
//TODO Auto-generated method stub
//ClassLoaderTestクラスのローダ名を取得して印刷
String classLoaderName = ClassLoaderTest.class.getClassLoader().getClass().getName();
System.out.println(classLoaderName);
//Systemクラスのローダを取得して印刷
ClassLoader classLoader2 = System.class.getClassLoader();
System.out.println(classLoader2);
//classLoaderTestクラスのローダとローダの親ローダの取得
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
クラスローダの委任メカニズム
Java仮想マシンのすべてのクラス・ローダは、次の図のように親子関係のあるツリー構造で構成されています.各クラス・ローダ・オブジェクトをインスタンス化する場合は、親ローダ・オブジェクトを指定するか、デフォルトでシステム・ローダを使用して親ローダをロードする必要があります.
Java仮想マシンがクラスをロードする場合、いったいどのクラスローダによってロードされますか?
1. まず、現在のスレッドのクラスローダは、スレッドの最初のクラスをロードします.
2. AクラスでBクラスが参照されている場合、java仮想マシンはAクラスをロードするローダを使用してBクラスをロードします.
3. クラスローダを指定してクラスをロードするには、クラスローダを直接呼び出すこともできます.
各クラス・ローダは、クラスをロードするときに、上位クラス・ローダに委任します.
1. すべての上位クラスのローダがクラスにロードされていない場合、発起者クラスのローダに戻り、クラスにロードされていないとClassNotFoundExceptionが投げ出され、発起者クラスのローダの下位ローダを探すことはありません.getChildメソッドがないので、あっても、複数の息子がいるので、どれを探すべきですか.
2. クラス・ローダの階層図と委任ローダの原理について、以前ClassLoaderTestをjre/lib/extディレクトリのitcast.jarパッケージに出力した後、実行結果がExtClassLoaderになった理由を説明します.
カスタムクラスローダ
1. カスタムクラスローダはClassLoaderを継承する必要があります
ClassLoaderを継承してインスタンスオブジェクトを作成すると、カスタムクラスローダになります.
2. findClassメソッドの書き換え
ClassLoaderクラスのloadClassメソッドは、委任メカニズムでクラスをロードします.ロードされていない場合はfindClassメソッドを呼び出してロードを続行します.したがって、カスタムローダは、loadClassメソッドを書き換えずにfindClassメソッドを書き換える必要があります.
3. findClassメソッド
手順:
1. ファイルの内容を暗号化して復号するプログラムを作成します.
2. 暗号化されたクラスのロードと復号を実現する独自のクラスローダを作成します.
3. コンパイラがクラスを認識できないため、ソースプログラムではクラス名で参照変数を定義できないプログラム呼び出しクラスローダを作成します.プログラムではClassLoader.load()メソッドのほか、スレッドを設定するコンテキストクラスローダまたはシステムクラスローダを使用して、Class.forName.を使用することもできます.
実験手順:
1. パッケージ名のないclassファイルを暗号化し、暗号化結果を別のディレクトリに保存します.たとえばjava MyClassLoader MyTest.class F:itcast
2. クラスをロードするプログラムを実行し、結果は正常にロードできますが、印刷されたクラスローダの名前はAppClassLoader:java MyClassLoader MyTest.class F:itcast
3. classpath環境のクラスファイルを暗号化されたクラスファイルに置き換えると、前の操作で問題が発生し、AppClassLoaderクラスローダのロードに失敗したことをエラーで示します.
4. classpath環境のクラスファイルを削除すると、実行前のステップで問題ありません.
コード例:カスタムクラスローダでclassファイルを暗号化し、カスタムローダでclassファイルをロードして復号します.
暗号化されたクラスClassLoaderAttachmentは、カスタムローダでロードする必要があります.
package cn.itcast.classloader;
import java.util.Date;
/**
* 暗号化されたクラスClassLoaderAttachmentは、カスタムローダでロードする必要があります.
* @author hezhudong
*
*/
publicclass ClassLoaderAttachment extends Date {
/**
* 複写toStringメソッド
*/
@Override
public String toString() {
return"hello,itheima";
}
}
カスタムクラスローダクラスMyClassLoaderは、メイン関数classファイルを暗号化し、ローダクラスインスタンスを呼び出してclassファイルをロードして復号します.
package cn.itcast.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* クラス・ローダ・クラスをカスタマイズし、メイン関数classファイルを暗号化し、ローダ・クラス・インスタンスを呼び出してclassファイルをロードし、復号します.
* @author hezhudong
*
*/
publicclass MyClassLoader extends ClassLoader{
//classファイルパス
private String classDir ;
/**
* コンストラクタ
*/
MyClassLoader() {}
MyClassLoader(String classDir ) {
this.classDir = classDir;
}
/**
* しゅかんすう
*/
publicstaticvoid main(String[] args) throws Exception{
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destPath = destDir+"\\"+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
/*
* ファイルを暗号化し、復号化することもできます.
*/
privatestaticvoid cypher(InputStream ips,OutputStream ops) throws Exception {
int b =-1;
while((b=ips.read())!=-1) {
ops.write(b^0xff);
}
}
/**
* ClassLoaderクラスのfindCLassメソッドを複写し、クラスの検索方法をカスタマイズします.
*/
@Override
protected Class findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + "\\"+ name + ".class";
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
fis = new FileInputStream(classFileName);
bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] loadData = bos.toByteArray();
return defineClass(null,loadData,0,loadData.length);
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
if(fis!=null)
fis.close();
}catch(Exception ex) {
ex.printStackTrace();
}
try{
if(bos!=null)
bos.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
returnsuper.findClass(name);
}
}
テストクラスカスタムローダクラスClassLoaderTest
package cn.itcast.classloader;
import java.util.Date;
/**
* テストクラスカスタムローダクラスClassLoaderTest
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) throws Exception{
//ClassLoaderAttachmentは、暗号化されているため、カスタムクラスローダで指定された場所にロードされます.
Class clazz =new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
Date d =(Date)clazz.newInstance();
//カスタムクラスローダで取得したオブジェクトを印刷し、どのローダでロードするかを印刷します.
System.out.println(d);
System.out.println(d.getClass().getClassLoader().getClass().getName());
}
}
--------androidトレーニング、JAvaトレーニング、java学習型技術ブログ、あなたとの交流を楽しみにしています!---------
クラスローダとは
クラスローダはクラスをロードするツールで、私たちがプログラムでクラスを使用するとき、仮想マシンはクラスローダを通じてclassファイルをメモリにロードし、一連の処理を行った後、バイトコードになって私たちのために使用します.
Javaでデフォルトのクラスローダ
Java仮想マシンには複数のクラスローダをインストールできます.システムのデフォルトでは3つの主要クラスローダがあります.各クラスは特定の場所のクラスをロードします.BootStrap、ExtClassLoader、AppClassLoaderです.
クラス・ローダもjavaクラスです.他のjavaクラス・ローダ自体もクラス・ローダにロードされるため、Javaクラスではないクラス・ローダが必要であることは明らかです.これがBootStrapです.
コード例:各種のローダを表示する
package cn.itcast.classloader;
/**
* テストクラスローダのクラス
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) {
//TODO Auto-generated method stub
//ClassLoaderTestクラスのローダ名を取得して印刷
String classLoaderName = ClassLoaderTest.class.getClassLoader().getClass().getName();
System.out.println(classLoaderName);
//Systemクラスのローダを取得して印刷
ClassLoader classLoader2 = System.class.getClassLoader();
System.out.println(classLoader2);
//classLoaderTestクラスのローダとローダの親ローダの取得
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
クラスローダの委任メカニズム
Java仮想マシンのすべてのクラス・ローダは、次の図のように親子関係のあるツリー構造で構成されています.各クラス・ローダ・オブジェクトをインスタンス化する場合は、親ローダ・オブジェクトを指定するか、デフォルトでシステム・ローダを使用して親ローダをロードする必要があります.
Java仮想マシンがクラスをロードする場合、いったいどのクラスローダによってロードされますか?
1. まず、現在のスレッドのクラスローダは、スレッドの最初のクラスをロードします.
2. AクラスでBクラスが参照されている場合、java仮想マシンはAクラスをロードするローダを使用してBクラスをロードします.
3. クラスローダを指定してクラスをロードするには、クラスローダを直接呼び出すこともできます.
各クラス・ローダは、クラスをロードするときに、上位クラス・ローダに委任します.
1. すべての上位クラスのローダがクラスにロードされていない場合、発起者クラスのローダに戻り、クラスにロードされていないとClassNotFoundExceptionが投げ出され、発起者クラスのローダの下位ローダを探すことはありません.getChildメソッドがないので、あっても、複数の息子がいるので、どれを探すべきですか.
2. クラス・ローダの階層図と委任ローダの原理について、以前ClassLoaderTestをjre/lib/extディレクトリのitcast.jarパッケージに出力した後、実行結果がExtClassLoaderになった理由を説明します.
カスタムクラスローダ
1. カスタムクラスローダはClassLoaderを継承する必要があります
ClassLoaderを継承してインスタンスオブジェクトを作成すると、カスタムクラスローダになります.
2. findClassメソッドの書き換え
ClassLoaderクラスのloadClassメソッドは、委任メカニズムでクラスをロードします.ロードされていない場合はfindClassメソッドを呼び出してロードを続行します.したがって、カスタムローダは、loadClassメソッドを書き換えずにfindClassメソッドを書き換える必要があります.
3. findClassメソッド
findClass Class 。
:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
class
手順:
1. ファイルの内容を暗号化して復号するプログラムを作成します.
2. 暗号化されたクラスのロードと復号を実現する独自のクラスローダを作成します.
3. コンパイラがクラスを認識できないため、ソースプログラムではクラス名で参照変数を定義できないプログラム呼び出しクラスローダを作成します.プログラムではClassLoader.load()メソッドのほか、スレッドを設定するコンテキストクラスローダまたはシステムクラスローダを使用して、Class.forName.を使用することもできます.
実験手順:
1. パッケージ名のないclassファイルを暗号化し、暗号化結果を別のディレクトリに保存します.たとえばjava MyClassLoader MyTest.class F:itcast
2. クラスをロードするプログラムを実行し、結果は正常にロードできますが、印刷されたクラスローダの名前はAppClassLoader:java MyClassLoader MyTest.class F:itcast
3. classpath環境のクラスファイルを暗号化されたクラスファイルに置き換えると、前の操作で問題が発生し、AppClassLoaderクラスローダのロードに失敗したことをエラーで示します.
4. classpath環境のクラスファイルを削除すると、実行前のステップで問題ありません.
コード例:カスタムクラスローダでclassファイルを暗号化し、カスタムローダでclassファイルをロードして復号します.
暗号化されたクラスClassLoaderAttachmentは、カスタムローダでロードする必要があります.
package cn.itcast.classloader;
import java.util.Date;
/**
* 暗号化されたクラスClassLoaderAttachmentは、カスタムローダでロードする必要があります.
* @author hezhudong
*
*/
publicclass ClassLoaderAttachment extends Date {
/**
* 複写toStringメソッド
*/
@Override
public String toString() {
return"hello,itheima";
}
}
カスタムクラスローダクラスMyClassLoaderは、メイン関数classファイルを暗号化し、ローダクラスインスタンスを呼び出してclassファイルをロードして復号します.
package cn.itcast.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* クラス・ローダ・クラスをカスタマイズし、メイン関数classファイルを暗号化し、ローダ・クラス・インスタンスを呼び出してclassファイルをロードし、復号します.
* @author hezhudong
*
*/
publicclass MyClassLoader extends ClassLoader{
//classファイルパス
private String classDir ;
/**
* コンストラクタ
*/
MyClassLoader() {}
MyClassLoader(String classDir ) {
this.classDir = classDir;
}
/**
* しゅかんすう
*/
publicstaticvoid main(String[] args) throws Exception{
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destPath = destDir+"\\"+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
/*
* ファイルを暗号化し、復号化することもできます.
*/
privatestaticvoid cypher(InputStream ips,OutputStream ops) throws Exception {
int b =-1;
while((b=ips.read())!=-1) {
ops.write(b^0xff);
}
}
/**
* ClassLoaderクラスのfindCLassメソッドを複写し、クラスの検索方法をカスタマイズします.
*/
@Override
protected Class findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + "\\"+ name + ".class";
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
fis = new FileInputStream(classFileName);
bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] loadData = bos.toByteArray();
return defineClass(null,loadData,0,loadData.length);
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
if(fis!=null)
fis.close();
}catch(Exception ex) {
ex.printStackTrace();
}
try{
if(bos!=null)
bos.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
returnsuper.findClass(name);
}
}
テストクラスカスタムローダクラスClassLoaderTest
package cn.itcast.classloader;
import java.util.Date;
/**
* テストクラスカスタムローダクラスClassLoaderTest
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) throws Exception{
//ClassLoaderAttachmentは、暗号化されているため、カスタムクラスローダで指定された場所にロードされます.
Class clazz =new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
Date d =(Date)clazz.newInstance();
//カスタムクラスローダで取得したオブジェクトを印刷し、どのローダでロードするかを印刷します.
System.out.println(d);
System.out.println(d.getClass().getClassLoader().getClass().getName());
}
}
--------androidトレーニング、JAvaトレーニング、java学習型技術ブログ、あなたとの交流を楽しみにしています!---------