Class.forName(String)类加载器分析

1.调用关系

    @CallerSensitive
    public static Class forName(String className)
                throws ClassNotFoundException {
        Class caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
    // Returns the class's class loader, or null if none.
    static ClassLoader getClassLoader(Class caller) {
        // This can be null if the VM is requesting it
        if (caller == null) {
            return null;
        }
        // Circumvent security check since this is package-private
        return caller.getClassLoader0();
    }
    // Package-private to allow ClassLoader access
    ClassLoader getClassLoader0() { return classLoader; }
    // Initialized in JVM not by private constructor
    // This field is filtered from reflection access, i.e. getDeclaredField
    // will throw NoSuchFieldException
    private final ClassLoader classLoader;

可知Class.forName()是获取调用方法所在的类的类加载器,然后使用该类加载器加载指定的类。

2.示例

自定义类加载器

public class MyClassLoader extends ClassLoader {
    private String path;
    private String name;

    public MyClassLoader(ClassLoader parent, String path, String name) {
        super(parent);
        this.path = path;
        this.name = name;

    }

    public MyClassLoader(String path, String name) {
        super();
        this.path = path;
        this.name = name;
    }

    static {
        ClassLoader.registerAsParallelCapable();
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        byte[] data = readClassFile2ByteArray(name);
        return this.defineClass(name, data, 0, data.length);
    }

    private byte[] readClassFile2ByteArray(String name) {
        InputStream is = null;
        byte[] returnData = null;

        name = name.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
        String filePath = this.path + name + ".class";
        File file = new File(filePath);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        try {
            is = new FileInputStream(file);
            int tmp = 0;
            while ((tmp = is.read()) != -1) {
                byteArrayOutputStream.write(tmp);
            }
            returnData = byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return returnData;
    }

    @Override
    public String toString() {
        return "MyClassLoader{" +
                "name='" + name + '\'' +
                '}';
    }
}

自定义类加载器需要加载的类:

public class Test {
    public static int a = 1;
    static {
        System.out.println(a);
    }

    public Test() {
        System.out.println("A ClassLoader:" + this.getClass().getClassLoader()
                + " from user");
    }

    public void sayHello() throws ClassNotFoundException {
        System.out.println("================");
        System.out.println("This is in Test");
        Class.forName("com.enjoy.learn.core.jvm.Hello");
        Hello hello = new Hello();
        System.out.println(hello.getClass().getClassLoader());
        System.out.println("================");
    }
}

在sayHello中需要加载的类:

public class Hello {
    public Hello() {
        System.out.println("say hello");
    }
}
public class TestClassLoader {
    public static final MyClassLoader WZ_LOADER = new MyClassLoader(null,"E:\\IdeaProjects\\javabasics\\target\\classes\\","wz");

    public static void main(String[] args) throws Exception {
        Class c = WZ_LOADER.loadClass("com.enjoy.learn.core.jvm.Test");
        Object obj = c.newInstance();
        Method method = c.getMethod("sayHello", null);
        method.invoke(obj, null);
    }
}

测试结果:

A ClassLoader:MyClassLoader{name='wz'} from user
================
This is in Test
say hello
MyClassLoader{name='wz'}
================

可以看出在Test.sayHello中调用Class.forName方法加载类,使用的是Test的类加载器。

3.注意事项

    public static void main(String[] args) throws Exception {
        Class c = WZ_LOADER.loadClass("com.enjoy.learn.core.jvm.Test");
        Object obj = c.newInstance();
        Method method = c.getMethod("sayHello", null);
        method.invoke(obj, null);
    }

由于c是Class,只能将c.newInstance()赋值给Object obj,而不能是Test。这里需要使用反射来实现调用,比较麻烦。

你可能感兴趣的:(Class.forName(String)类加载器分析)