Java-----类加载器,java-----类加载
Java-----类加载器,java-----类加载
android培训、java培训、期待与您交流!
类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。基本上所有的类加载器都是
java.lang.ClassLoader类的一个实例。
Java类加载器主要有两类,一类是开发人员自己编写的,另一类则是系统提供的;系统提供的主要有三种:
这个图用来表明这三种类加载器的关系是在好不过的了。这个图中的箭头表示方向表示上面的是下面的子类,即BootStrap是最上层的父类加载器了,这个加载器是它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader,它主要用来加载上图右边的文件夹下的jar文件的。ExtClassLoader加载器它用来加载 Java 的扩展库,Java 虚拟机的实现会提供一个扩展库目录,该类加载器在此目录里面查找并加载 Java 类。而至于AppClassLoader加载器它根据
Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的,可以通过 ClassLoader.getSystemClassLoader()来获取它。
这三种类加载器,在加载的时候使用了一种委托的机制。如果类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。如下代码:
public class Testx5 {
public static void main(String[] args) {
ClassLoader loader = Testx5.class.getClassLoader();
//返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该//类由引导类加载器加载,则此方法在这类实现中将返回 null。
while(loader != null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
//把该加载器的父类加载器拿到
}
System.out.println(loader);
}
}
上面这个代码在加载的时候类加载器会委托给它父类的加载器,父类的加载器并不能在它文件下找到这个jar文件那么就由它本身来加载,则输出的结果是:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
打印出的结果首先是加载当前文件的加载器,即系统类加载器,然后是它的父类加载器在然后就是引导类加载器了,因为它没有实例所以就返回的是null。
假如现在我们把这个jar文件加到JRE/lib/ext下,在看看输出的结果是什么:
sun.misc.Launcher$ExtClassLoader
null
由上面的输入我们就可以看出这个加载的过程虽说是由下层或者本层发出的,但是真正的加载器却是其父类或者本身加载器。这就是委托,总是先把任务往高处推,如果上面的不处理,在由本身来加载。
编写自己的类加载器:在视频中老师编写的类加载器是一个加密class加载器。首先实现.class文件可以通过MyClassLoader来进行加密,代码如下:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyLoaderClass {
public static void main(String[] args)throws Exception {
String srcPath = args[0];
//传入要进行加密的class绝对路径
String destDir = args[1];
//加密后的class要放到哪个目录中
String destPath = destDir + "\\" +srcPath.substring(srcPath.lastIndexOf('\\')+1);
//这个是用来得到最后目标路径包括了文件名
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
//调用加密函数
fis.close();
fos.close();
}
public static void cypher(InputStream is,OutputStream os)throws Exception{
int b = -1;
while((b = is.read())!=-1){
os.write(b ^ 0xff);
//读取的数据进行与0xff的异或
}
}
}
//用来加载的类
import java.util.Date;
public class ClassLoaderAttachment extends Date {
public String toString(){
return "hello world";
}
}
生成加密后的文件后,然后我们在测试下,看现在在别的类里面我们来调用这个文件,可不可以正常运行,实验证明这个是不能运行,比如我在别的文件中加入
System.out.println(new ClassLoaderAttachment().toString());
运行的结果是:
Exception in thread "main" java.lang.NoClassDefFoundError: ClassLoaderAttachment
at Testx5.main(Testx5.java:17)
Caused by: java.lang.ClassNotFoundException: ClassLoaderAttachment
就是说在三种类加载器中都没有找到相应字节码来加载,那么这种情况就需要我们自己的加载器来进行解密了。
在自己编写加载的时候,必须要继承ClassLoader类,并且复写findClass方法,老师讲在复写的时候我们不能复写loadClass方法,因为这个loadClass方法中有委托机制的一个过程运行,所以为了保存下来,所以覆盖findClass方法。代码:
private String classDir;
public MyLoaderClass(){}
public MyLoaderClass(String classDir){
this.classDir = classDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = classDir + "\\" + name + ".class";
//得到传进来class文件的绝对路径
try {
FileInputStream fis = new FileInputStream(className);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cypher(fis,baos);
fis.close();//解密
byte[] bytes = baos.toByteArray();
return defineClass(null, bytes, 0,bytes.length);
//解密后的数据通过这个方法来变为一个字节码
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
然后在进行测试,在另外一个文件中写如下代码:
Class<?> clazz = new MyLoaderClass("itcastlib").loadClass("ClassLoaderAttachment");
Date d = (Date)clazz.newInstance();
System.out.println(d);
最后可以正常的运行出结果,这个说明自己的类加载器已经把这个加密的文件解密了。则就是它进行了加载。
android培训、java培训、期待与您交流!
相关文章
- 暂无相关文章
用户点评