ClassLoader:
用来加载 Class 的。负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。
JVM 中内置了三个重要的 ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。
AppClassLoader 是直接面向我们用户的加载器 。
AppClassLoader 可以由 ClassLoader 类提供的静态方法 getSystemClassLoader() 得到,它就是我们所说的「系统类加载器」
当调用函数getClassLoader()函数返回一个null,说明它调用了一个BootstrapClassLoader(由c++语言编写)。
ClassLoader
类有如下核心方法:
loadClass
(加载指定的Java类)findClass
(查找指定的Java类)findLoadedClass
(查找JVM已经加载过的类)defineClass
(定义一个Java类)resolveClass
(链接指定的Java类)
常见类动态加载的方式:Class.forName,ClassLoader.loadClass
class A{
public A(){
System.out.println("A...");
}
public int add(int a,int b){
return a+b;
}
}
public class example {
public static void main(String[] args) {
try {
//反射加载
Class aclass = Class.forName("dtjz.A",;
System.out.println(aclass);
example a = new example();
//classloader加载
Class<?> bclass = a.getClass().getClassLoader().loadClass("dtjz.A");
System.out.println(bclass);
}catch(Exception e){
e.printStackTrace();
}
}
}
双亲委派
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载 器。不过这里类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用 组合关系来复用父加载器的代码。
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加 载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的 加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类 加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一 个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类 在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个 类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的 ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱。如果读者有兴趣的话,可以尝试去写一个与rt.jar类库中已有类重名的Java 类,将会发现它可以正常编译,但永远无法被加载运行
双亲委派模型的实现 :
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载时
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
自定义ClassLoader:
java.lang.ClassLoader
是所有的类加载器的父类,我们来写一个类加载器来实现加载自定义的字节码 ,重写findClass方法从而实现了加载目录class文件甚至是远程资源文件。
这里我自己写了一个类加载器,我将加载文件放在了D盘,TestHello的hello方法就是返回hello字符串
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
public class MyClassLoader extends ClassLoader{
private static String testClassName= "hello.TestHello";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] testClassBytes = getDataFromFile(name);
return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
}
private byte[] getDataFromFile(String name){
InputStream ins = null;
ByteArrayOutputStream ous = null;
byte[] result = null;
int num = -1;
try {
ins = new FileInputStream("D:/" +name.substring(6) + ".class");
ous = new ByteArrayOutputStream();
byte[] buffer = new byte[10];
while((num = ins.read(buffer))!= -1) {
ous.write(buffer, 0, num);
}
result = ous.toByteArray();
}catch(Exception e){
e.printStackTrace();
}finally {
try {
if(ins != null) ins.close();
if(ous != null) ous.close();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
}
public static void main(String[] args) {
MyClassLoader loader = new MyClassLoader();
try{
Class testClass = loader.loadClass(testClassName);
Object obj = testClass.newInstance();
Method method = obj.getClass().getMethod("hello");
String str = (String)method.invoke(obj);
System.out.println(str);
}catch(Exception e){
e.printStackTrace();
}
}
}
这里就成功实现了我们自己的类加载器
这里我用
System.out.println(testClass.getClassLoader());
可以看到用的就是我们自己的类加载器。
类加载隔离
创建类加载器的时候可以指定该类加载的父类加载器,ClassLoader是有隔离机制的,不同的ClassLoader可以加载相同的Class(两则必须是非继承关系),同级ClassLoader跨类加载器调用方法时必须使用反射。
这里以ClassLoaderA和ClassLoaderB两个不同的加载器加载同一个class文件,他们
的class对象却不是同一个对象,只能通过反射相互调用
这里我根据前面的代码写了两个类加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
class ClassLoaderA extends ClassLoader{
private String testClassName= "hello.TestHello";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] testClassBytes = getDataFromFile(name);
return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
}
private byte[] getDataFromFile(String name){
InputStream ins = null;
ByteArrayOutputStream ous = null;
byte[] result = null;
int num = -1;
try {
ins = new FileInputStream("D:/" +name.substring(6) + ".class");
ous = new ByteArrayOutputStream();
byte[] buffer = new byte[10];
while((num = ins.read(buffer))!= -1) {
ous.write(buffer, 0, num);
}
result = ous.toByteArray();
}catch(Exception e){
e.printStackTrace();
}finally {
try {
if(ins != null) ins.close();
if(ous != null) ous.close();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
}
}
class ClassLoaderB extends ClassLoader{
private String testClassName= "hello.TestHello";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] testClassBytes = getDataFromFile(name);
return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
}
private byte[] getDataFromFile(String name){
InputStream ins = null;
ByteArrayOutputStream ous = null;
byte[] result = null;
int num = -1;
try {
ins = new FileInputStream("D:/" +name.substring(6) + ".class");
ous = new ByteArrayOutputStream();
byte[] buffer = new byte[10];
while((num = ins.read(buffer))!= -1) {
ous.write(buffer, 0, num);
}
result = ous.toByteArray();
}catch(Exception e){
e.printStackTrace();
}finally {
try {
if(ins != null) ins.close();
if(ous != null) ous.close();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
}
}
public class gl {
public static void main(String[] args) {
try {
ClassLoaderA classLoaderA = new ClassLoaderA();
ClassLoaderB classLoaderB = new ClassLoaderB();
Class<?> aclass = Class.forName("hello.TestHello",true,classLoaderA);
Class<?> aaclass = Class.forName("hello.TestHello",true,classLoaderA);
Class<?> bclass = Class.forName("hello.TestHello",true,classLoaderB);
System.out.println(aclass == aaclass);
System.out.println(aaclass == bclass);
Object instanceA = aclass.newInstance();
Method helloMethod = aclass.getMethod("hello");
String result = (String)helloMethod.invoke(instanceA);
System.out.println(result);
}catch(Exception e){
e.printStackTrace();
}
}
}
从结果中可以看到,不同加载器加载同一个class文件生成的类对象不同。
标签:name,ClassLoader,String,ous,null,Class,加载 From: https://www.cnblogs.com/lscnnn/p/16936584.html