classloader java 424_Java ClassLoader总结

Java ClassLoader总结

从一个题目开始

public class StaticTest {

public static void main(String[] args)

{

staticFunction();

}

static StaticTest st = new StaticTest();// 1

static

{

System.out.println("1");

}

{

System.out.println("2");

}

StaticTest()

{

System.out.println("3");

System.out.println("a=" + a + ",b=" + b);

}

public static void staticFunction() {

System.out.println("4");

}

int a = 110;

static int b = 112;

}

这段结果的输出如下:

2

3

a=11,b=0

1

4

原因在于:

StaticTest中的静态方法时,首先要加载StaticTest类,然后依次初始化类中的静态变量并执行静态代码块。因此首先会创建一个StaticTest实例。创建示例前会执行{}代码块,并执行构造函数。狗仔函数执行时,尚未执行static变量的初始化,但是a=100已经被编译器生成的init函数执行。构造函数执行完成后执行Static代码块,最后执行静态函数。

从这个例子引出一个问题,类是如何加载的?

Classloader是什么

Java源码首先被编译成.class文件,当需要用到一个类时,虚拟机会把.class文件读取到内存中,编程class对象。用于读取class文件到内存的类就是ClassLoader。

JDK中有三种内置的Classloader:

Bootstrap Class Loader - Java最顶层的Classloader,主要是用于加载JDK核心类,包括%JRE_HOME%\lib下的rt.jar等。

Extensions Class Loader - 用于加载JDK相关的扩展类,主要是$JAVA_HOME/lib/ext这个目录下的类。

System Class Loader - 用于加载当前应用classpath下的所有类,也被称作AppClassLoader

下面是一个例子:

public class TestClassloader {

public static void main(String[] args) {

System.out.print("Class Loader of " + String.class + " is "

+ String.class.getClassLoader() + "\r\n");

System.out.print("Class Loader of " + ECDHKeyAgreement.class + " is "

+ ECDHKeyAgreement.class.getClassLoader() + "\r\n");

System.out.print("Class Loader of " + MyClass.class + " is "

+ MyClass.class.getClassLoader() + "\r\n");

}

static class MyClass {

private String mName = "MyClass";

String getName() {

return mName;

}

}

}

输出结果如下:

Class Loader of class java.lang.String is null

Class Loader of class sun.security.ec.ECDHKeyAgreement is sun.misc.Launcher$ExtClassLoader@232204a1

Class Loader of class com.minghui.test.TestClassloader$MyClass is sun.misc.Launcher$AppClassLoader@659e0bfd

从结果可以看出:

String类是由Bootstrap Class Loader读取的,Bootstrap Class Loader并不是一个Java类,它是通过本地代码实现的。因此返回null。

ECDHKeyAgreement类是在$JAVA_HOME/lib/ext/sunec.jar中因此返回的类加载器为ExtClassLoader。

MyClass是自定义的类,类加载器为AppClassLoader

双亲委托模型

分析双亲委托模型可以直接从Classloader的源码入手:

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// First, check if the class has already been loaded

Class c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

if (c == null) {

// If still not found, then invoke findClass in order

// to find the class.

long t1 = System.nanoTime();

c = findClass(name);

// this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

loadClass的流程大致如下:

首先从缓存中读取Class

如果1没有读到,尝试使用父ClassLoader读取

如果2没有读到,尝试通过BootstrapClassLoader读取

如果3没有读到,则通过findClass方法读取,findClass是一个抽下函数需要子类实现

双亲委托模型的意义在于安全,如果不使用双亲委托模型则可能存在安全风险,例如可以编写一个java.lang.string类,并读取到内存中代替JDK中的String类。

需要注意的一点是,例子中的父ClassLoader在这里都是指包含关系。

自定义Classloader

直接上代码:

public class TestClassloader {

public static void main(String[] args) {

FileClassLoader classLoader = new FileClassLoader("D:/MyClass.class");

try {

Class stringClass = classLoader.loadClass("java.lang.String");

System.out.println("stringClass loader: " + stringClass.getClassLoader());

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

try {

Class stringClass = classLoader.loadClass("com.minghui.test.MyClass");

System.out.println("MyClass loader: " + stringClass.getClassLoader());

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

static class FileClassLoader extends ClassLoader {

String mClassFileName;

public FileClassLoader(String fileName) {

mClassFileName = fileName;

}

public Class loadClass(String name) throws ClassNotFoundException {

System.out.println("loadClass name is " + name);

return super.loadClass(name);

}

protected Class findClass(String name) throws ClassNotFoundException {

System.out.println("findClass name is " + name);

try {

byte[] data = loadClassFileData(name);

Class myClass = defineClass(name, data, 0, data.length);

return myClass;

} catch (IOException e) {

e.printStackTrace();

throw new ClassNotFoundException();

}

}

private byte[] loadClassFileData(String name) throws IOException {

InputStream inputStream = null;

try {

File file = new File(mClassFileName);

inputStream = new FileInputStream(file);

byte buff[] = new byte[(int) file.length()];

inputStream.read(buff);

return buff;

} finally {

if (inputStream != null) {

inputStream.close();

}

}

}

}

}

输出结果如下:

loadClass name is java.lang.String

stringClass loader: null

loadClass name is com.minghui.test.MyClass

findClass name is com.minghui.test.MyClass

loadClass name is java.lang.Object

MyClass loader: com.minghui.test.TestClassloader$FileClassLoader@70dea4e

可以看到:

String类是由父加载器(BootupClassloader)完成加载

由于MyClass不存在于Classpath中,因此父加载器都无法加载,由FileClassLoader加载

注意到一种的一行打印是** "loadClass name is java.lang.Object

"**,通过打印调用栈看在defineClass方法的内部会调用loadclass读取需要定义的类的父类。调用栈如下:

java.lang.Exception: loadClass name is java.lang.Object

at com.minghui.test.TestClassloader$FileClassLoader.loadClass(TestClassloader.java:43)

at java.lang.ClassLoader.defineClass1(Native Method)

at java.lang.ClassLoader.defineClass(ClassLoader.java:760)

at java.lang.ClassLoader.defineClass(ClassLoader.java:642)

at com.minghui.test.TestClassloader$FileClassLoader.findClass(TestClassloader.java:52)

at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

at com.minghui.test.TestClassloader$FileClassLoader.loadClass(TestClassloader.java:45)

at com.minghui.test.TestClassloader.main(TestClassloader.java:25)

参考文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值