首页 / JAVA / Java-Class Loader
Java-Class Loader
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java-Class Loader,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含7716字,纯文字阅读大概需要12分钟。
内容图文
![Java-Class Loader](/upload/InfoBanner/zyjiaocheng/755/adc8a5f57986496a8614fd6228986606.jpg)
文章目录
1. 类加载的三个阶段
- 三个阶段:
- 加载: 查找并加载类的二进制数据。
- 链接:
- 验证:确保被加载类的正确性。
- 准备:为类的静态变量分配内存,并将静态变量初始化为默认值。
- 解析:将类中符号引用变为直接引用。
- 初始化:为类的静态变量,赋予正确的初始值。
2. java 类主动使用
- 在java规范中, 只有类被首次主动使用的时候,才会被加载。
- 主动使用的情形:
- new 直接使用
- 访问某个类或是接口的静态变量,或者对该静态变量进行赋值操作。
- 调用了静态方法。
- 反射某个类。
- 初始化一个子类。
- 启动类;eg:java HelloWorld
- 特殊的情形:
- 使用子类去调用父类的静态变量,只会初始化父类,而不会初始化子类。
- new Obj[]: 定义数组也不会初始化类。
- 访问被final修饰的属性也不会被初始化。
- public static final long salary = 1000L; ====> 不会被初始化。 因为编译时就能算出结果。
- public static final int random = new Random().nextInt(100); ===> 会被初始化。 需要到运行时才能得到random产生的值。
- 除此之外,其余的都是被动调用,不会导致类的初始化。
- 栈中存放的是对象的引用,类的数据结构存放在本地方法区中,堆中存放类的数据以及对类数据结构的指向句柄。
3. 类初始化
- 静态语句块只能访问以及修改之前的静态变量;之后定义的静态变量,只能修改,不能访问。
public class Demo1 {
public static int x = 10;
static { x = 20;
System.out.println("x: " + x);
y = 30;
// System.out.println("y: " + y); Error
}
public static int y = 20;
}
class Test {
public static void main(String[] args) {
System.out.println(Demo1.x); // 20
System.out.println(Demo1.y); // 20 因为顺序初始化时, static中赋值的30, 被后面的赋值给覆盖.
}
}
- 静态代码块,JVM已经保证了线程的安全性。
4. 类加载器以及自定义ClassLoader
- 自定义ClassLoader
public class MyClassLoader extends ClassLoader {
private final static String DEFAULT_DIR = "E:\\classLoader\\"; // 存放需要加载class的路径
private String dir = DEFAULT_DIR;
private String classLoaderName;
public MyClassLoader() { super();
}
public MyClassLoader(String classLoaderName) {
super();
this.classLoaderName = classLoaderName;
}
public MyClassLoader(ClassLoader parent, String classLoaderName) {
super(parent);
this.classLoaderName = classLoaderName;
}
/**
* name传入的是classPath, 格式为: xxx.xxx.xxx.AAA.class
* 所以, 为了能够读取到类文件中的数据, 需要转换为xxx/xxx/xxx/AAA.class
*
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = name.replace(".", "/");
File classFile = new File(dir, classPath + ".class");
System.out.println("ClassFileName: " + classFile.getAbsolutePath());
if (!classFile.exists()) {
throw new ClassNotFoundException("The class " + name + " not found in " + dir);
}
byte[] classBytes = loadClassBytes(classFile);
if (classBytes == null || classBytes.length == 0) {
throw new ClassNotFoundException("Load class file " + name + " failed");
}
return this.defineClass(name, classBytes, 0, classBytes.length);
}
private byte[] loadClassBytes(File classFile) {
try (ByteArrayOutputStream bais = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(classFile)) {
// 设置的缓存区大小, 需要小于10, 否则会报错java.lang.ClassFormatError, 网上帖子说是因为class文件和JVM的
// class验证冲突了. 可能class文件格式存在问题.
// byte[] buffer = new byte[2014];
byte[] buffer = new byte[1];
while (fis.read(buffer) != -1) {
bais.write(buffer);
}
bais.flush();
return bais.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
public class ClassLoaderObject {
static {
System.out.println("Init ClassLoaderObject!");
}
public String hello() {
System.out.println("Print Hello world!");
return "Hello World!";
}
}
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException {
MyClassLoader classLoader = new MyClassLoader("MyClassLoader");
// 加载class, 不能出现在当前的classPath下, 否则会使用ApplicationClassLoader, 而不会使用我们自己定义的
// classLoader. 因为, JVM使用的是双亲委托机制; 当加载一个类, 会先让父classLoader加载此class; 若在父的classPath
// 下找不到class, 才不断往子classLoader中传递; 最后若未找到则直接抛出ClassNotFoundException.
Class<?> loadClass = classLoader.loadClass("com.alex.classLoader.ClassLoaderObject");
System.out.println(loadClass);
System.out.println(loadClass.getClassLoader());
}
}
5. 打破父委托机制
- 自定义ClassLoader,并且重写loadClass方法。先使用我们自定义的classLoader加载class;若自定义的classLoader无法加载class,则使用系统classLoader,也就是使用父委托机制。但是对于
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = null;
if (name.startsWith("java.")) { // java.开头的使用系统classLoader, 加载jdk的class.
try {
ClassLoader system = ClassLoader.getSystemClassLoader();
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
}
try {
clazz = findClass(name); // 使用自定义classLoader
} catch (Exception e) {
e.printStackTrace();
}
if (clazz == null && getParent() != null) { // 自定义的classLoader中没有此class, 使用父classLoader去加载.
getParent().loadClass(name);
}
return clazz;
}
- 使用自定义的java.lang.String类。 结论:打破父委托机制,但是无法加载java.lang.*类,因为java对于java.lang包的类有安全校验, 不允许开发者定义一样的类。 eg:自定义一个java.lang.String是无法加载到classLoader中的;只能是使用jdk自带的。
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = null;
/* 忽略这段代码, 不然还是会使用jdk的String类. 这样我们就可以使用自定义的classLoader去加载java.lang.String类。
if (name.startsWith("java.")) { // java.开头的使用系统classLoader, 加载jdk的class.
try {
ClassLoader system = ClassLoader.getSystemClassLoader();
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
}*/
try {
clazz = findClass(name); // 使用自定义classLoader
} catch (Exception e) {
e.printStackTrace();
}
if (clazz == null && getParent() != null) { // 自定义的classLoader中没有此class, 使用父classLoader去加载.
getParent().loadClass(name);
}
return clazz;
}
6. 类的卸载和classLoader卸载
- JVM中只有class满足以下三个条件才能被GC.
- 该类所有的实例都已经被GC。
- 加载该类的classLoader已经被GC。
- 该类的java.lang.Class在任何地方都没有被引用。
7. 线程上下文
- 这里使用Mysql作为案列,进行分析;
Thread.currentThread().getContextClassLoader();可以得到当前线程中所处的ClassLoader;也可以设置当前线程的上下文,以及其中的classLoader
Thread.currentThread().getContextClassLoader(); // 线程上下文中获取classLoader.
Class.forName("com.jdbc.mysql"); // 反射获取mysql.
/**
* private static Connection getConnection(
* String url, java.util.Properties info, Class<?> caller) throws SQLException {
*
* * When callerCl is null, we should check the application's
* * (which is invoking this class indirectly)
* * classloader, so that the JDBC driver class outside rt.jar
* * can be loaded from here.
*
* ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
* synchronized (DriverManager.class) {
* // synchronize loading of the correct classloader.
* if (callerCL == null) {
*
* // 从线程上下文中获取ApplicationClassLoader; 若是使用默认的classLoader为BootClassLoader;
* // 这样会导致无法注入mysql实例. 使用ApplicationClassLoader就可以从classPath中获取mysql的实现类;
* // 从而产生mysql实例.
* callerCL = Thread.currentThread().getContextClassLoader();
* }
*}
*
* if (url == null) {
* throw new SQLException("The url cannot be null", "08001");
* }
*/
Connection contection = DriverManager.getConnection("");
内容总结
以上是互联网集市为您收集整理的Java-Class Loader全部内容,希望文章能够帮你解决Java-Class Loader所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。
来源:【匿名】