类加载器[2]父类委托机制[1]

 

下面通过一系列实验来加深对父类委托机制的理解。(首先新建一个Java工程ClassLoaderOrder

 

有如下两个Hello.java文件,分别生成.class文件。app版本放到ClassLoaderOrder工程的bin目录下,boot版本放到D:\temp目录下

 Java Code 

package com.bjsxt.test;

public class HelloLoader {
    
    
public static void sayLoader(){
        System.out.println(
"I am in app classPath");
    }
}

 Java Code 

package com.bjsxt.test;

public class HelloLoader {
    
    
public static void sayLoader(){
        System.out.println(
"I am in boot classPath");
    }
}

类加载器[2]父类委托机制[1]_第1张图片

类加载器[2]父类委托机制[1]_第2张图片

 

1

 Java Code 

 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test0 {

    
public static void main(String[] args) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        
        
try {
            
            
//根据类名HelloLoader去加载类
            Class clazz = cl.loadClass("com.bjsxt.test.HelloLoader");
            System.out.println(clazz.getClassLoader());
            
            
//反射调用静态方法
            Method declaredMethod = clazz.getDeclaredMethod("sayLoader", null);
            declaredMethod.invoke(null, null);
            
        } 
catch (ClassNotFoundException e) {
            e.printStackTrace();
        } 
catch (NoSuchMethodException e) {
            e.printStackTrace();
        } 
catch (SecurityException e) {
            e.printStackTrace();
        } 
catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
catch (IllegalArgumentException e) {
            e.printStackTrace();
        } 
catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        
//      //如果.java源代码已经在工程中,可直接通过如下方式使用
//      Hello.sayLoader();
    }
}

 

类加载器[2]父类委托机制[1]_第3张图片

从应用类加载器开始判断是否加载了Hello,一直到引导类加载器,都没有加载,然后从引导类加载器开始尝试加载,直到应用类加载器加载上。

 

2

跟上面代码相同,不过运行的时候配置JVM运行参数:-Xbootclasspath/a:D:/temp/,表示将D:/temp/路径添加到引导类加载器的classpath中。

其中,-Xbootclasspath/a:表示在系统class加载后加载,-Xbootclasspath/p:表示在系统class加载前加载。

类加载器[2]父类委托机制[1]_第4张图片

类加载器[2]父类委托机制[1]_第5张图片

从应用类加载器开始判断是否加载了Hello,一直到引导类加载器,都没有加载,然后从引导类加载器开始尝试加载,加载上。

 

3

修改代码如下,依然使用-Xbootclasspath/a:D:/temp/运行

 Java Code 

 

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test2 {

    
public static void main(String[] args) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        
        
try {
            
            
//读取.class文件字节数组
            byte[] classBytes = loadClassBytes("com.bjsxt.test.HelloLoader");
            
            
//调用应用类加载器ClassLoaderdefineClass()方法
            //由于非public所以通过反射来
            Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass"byte[].classint.classint.class);
            defineClassMethod.setAccessible(true);
            defineClassMethod.invoke(cl, classBytes, 
0, classBytes.length);
            
            
//
            //根据类名HelloLoader去加载类
            Class clazz = cl.loadClass("com.bjsxt.test.HelloLoader");
            System.out.println(clazz.getClassLoader());
            
            
//反射调用静态方法
            Method declaredMethod = clazz.getDeclaredMethod("sayLoader", null);
            declaredMethod.invoke(null, null);
            
        } 
catch (ClassNotFoundException e) {
            e.printStackTrace();
        } 
catch (NoSuchMethodException e) {
            e.printStackTrace();
        } 
catch (SecurityException e) {
            e.printStackTrace();
        } 
catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
catch (IllegalArgumentException e) {
            e.printStackTrace();
        } 
catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        
//      //如果.java源代码已经在工程中,可直接通过如下方式使用
//      Hello.sayLoader();
    }
    
    
/**
     * 
将包名中的.替换成路径分隔符,从D:/temp/目录下查找这个文件,如果存在的话,将其读取到字节数组
     * @param name
     * @return
     */

    
public static byte[] loadClassBytes(java.lang.String name) {
        
        
String path="D:/temp/"+name.replace(".""/")+".class";
        
        InputStream is=null;
        ByteArrayOutputStream baos=null;
        
        
try {
            is=
new FileInputStream(path);
            
            baos=
new ByteArrayOutputStream();
            
            
byte[] buffer=new byte[1024];
            
            
int ret=0;
            
while((ret=is.read(buffer))!=-1){
                baos.write(buffer, 
0, ret);
            }
            
            
return baos.toByteArray();
            
        } 
catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
catch (IOException e) {
            e.printStackTrace();
        }
finally{
            
try {
                
if(is!=null)
                    is.close();
                
                
if(baos!=null)
                    baos.close();
            } 
catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        
return null;
    }
}

类加载器[2]父类委托机制[1]_第6张图片

先用应用类加载器加载了D:/temp下的Hello类(bootclasspath Hello),然后再次从应用类加载器开始判断是否已经加载Hello,已经加载了直接返回,所以虽然输出的是boot class path,但是ClassLoaderAppClassLoader

 

4

修改如下,继续使用-Xbootclasspath/a:D:/temp/运行

类加载器[2]父类委托机制[1]_第7张图片

类加载器[2]父类委托机制[1]_第8张图片

 

虽然AppClassLoader已经加载了Hello,但是getParent()扩展类加载器loadClass()的时候,往上开始判断是否已经加载,知道引导类加载器,都没有加载,则引导类加载器开始往下尝试加载,加载上。

 

 

你可能感兴趣的:(Java)