1.类加载器
a) 加载类的工具
b) JAVA虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类加载器负责加载特定位置的类
c) BootStrap(引导) ExtClassLoader(扩展) AppClassLoader(应用)
d) 类加载器也是JAVA类,因为其他是JAVA类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是JAVA类,此加载器正是BootStrap.
e) JAVA虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父类装载器对象或者默认采用系统类加载器为其父类加载器
2.
a)
b)
public static void main(String[] args) throws Exception
{
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader() ); //null 特殊的加载器 BootStrap
System.out.println("-------------------------------------------");
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(null!=loader)
{
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
}
3.类加载器的委托机制
a) 当JAVA虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
i. 首先,当前线程的类加载器去加载线程中的第一个类
ii. 如果类A中引用了类B,JAVA虚拟机将使用加载类A的类加载器来加载类B
iii. 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
b) 每个类加载器加载类时,又先委托给其上级类加载器
i.当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不到,则抛ClassNotFoundException,不是再去寻找发起者类加载器的子加载器。因为没有getChild()方法,即便有,如果有多个子加载器,该找哪一个呢?
4.自定义类加载器原理
a) 自定义类加载器必须继承ClassLoader
b) loadClass方法与findClass方法
c) defineClass方法 //将得到的class文件转换为字节码
5.自定义类加载器步骤
a) 编写一个对文件内容进行加密的程序
b) 由于加解密互逆,可在其基础上将其修改为类加载器
代码如下:
package com.itheima.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader
{
private String classDir="";
public MyClassLoader()
{
}
public MyClassLoader(String classDir)
{
this.classDir=classDir;
}
public static void main(String[] args) throws Exception
{
String srcPath=args[0];
String desDir=args[1];
FileInputStream fis=new FileInputStream(srcPath);
String desFileName=srcPath.substring(srcPath.lastIndexOf('\\')+1);
String desPath=desDir+"\\"+desFileName;
FileOutputStream fos=new FileOutputStream(desPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream is,OutputStream os) throws Exception
{
int b=-1;
while((b=is.read())!=-1)
{
os.write(b^0Xff);
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
String fileName=classDir+"\\"+name.substring(name.lastIndexOf(".")+1)+".class";
try
{
FileInputStream fis=new FileInputStream(fileName);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
cypher(fis,baos);
fis.close();
byte[] bytes=baos.toByteArray();
return defineClass(bytes, 0, bytes.length);
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
}
6.实验步骤
a) 编写类ClassLoaderAttachment
package com.itheima.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date
{
public String toString()
{
return "hello";
}
public static void main(String[] args) {
System.out.println("111111");
}
}
b)运行得到其class文件
c)通过MyClassLoader将其加密,并将加密后的文件置于工程下的itcastlib下
d)用该文件替换com.itheima.day2.MyClassLoader,运行。发现无法执行,说明 此时的字节码已经被替换。
e)通过自定义的类加载器MyClassLoader运行,代码如下:
Class clazz1
=new MyClassLoader("itcastlib").loadClass("com.itheima.day2.ClassLoaderAttachment");
Date d1=(Date)clazz1.newInstance();
System.out.println(d1);
f)结果报异常,因为此时父类加载器AppClassLoader在com.itheima.day2下发现了ClassLoaderAttachment的class文件并尝试执行,结果报错。
g)删除该class文件,再次执行。结果成功调用自定义类加载器,并得到预期结果。
7.视频中小问题汇总
a) Ext文件下的JAR文件会使得内存中的字节码始终是1份,修改后结果无变化
b) 删除该JAR文件后,需要重启JDK
8.小结
当将某功能的类的字节码进行加密后,将其置于指定目录下(或进行配置而得到该目录),可以通过提供自己的类加载器来重新加载其进行解密并得到执行。