【Java从入门到放弃 之 类加载器】
类加载器类加载器负责将Java字节码文件(.class文件)动态加载到内存中,并将其转化为JVM可以实行的类对象。它是Java运行时系统的一部门,支持Java的动态特性,使得Java程序可以在运行时加载类和接口。
动态加载类的两种方式
public class ClassLoaderTest {
static {
System.out.println("ClassLoaderTest");
}
}
public class Test24 {
public static void main(String[] args) throws ClassNotFoundException {
loadClass1();
}
// 使用ClassLoader.loadClass
public static void loadClass1() throws ClassNotFoundException {
Class<?> testClass = Test24.class.getClassLoader()
.loadClass("com.example.mydemo.test.ClassLoaderTest
");
System.out.println(testClass.getName());
}
// 使用Class.forName
public static void loadClass2() throws ClassNotFoundException {
Class<?> testClass = Class.forName(
"com.example.mydemo.test.ClassLoaderTest
");
System.out.println(testClass.getName());
}
}
输出:
com.example.mydemo.test.ClassLoaderTest
public class Test24 { public static void main(String[] args) throws ClassNotFoundException { loadClass2(); } // 使用ClassLoader.loadClass public static void loadClass1() throws ClassNotFoundException { Class<?> testClass = Test24.class.getClassLoader() .loadClass("com.example.mydemo.test.ClassLoaderTest
"); System.out.println(testClass.getName()); } // 使用Class.forName public static void loadClass2() throws ClassNotFoundException { Class<?> testClass = Class.forName( "com.example.mydemo.test.ClassLoaderTest
"); System.out.println(testClass.getName()); }} 输出:
ClassLoaderTest
com.example.mydemo.test.ClassLoaderTest
差别之处
很显着,class.forname方法除法了静态方法,而getClassLoader().loadclass方法未出发静态方法。
类加载过程
类加载过程可以分为以下几个步骤:
[*]加载(Loading):在加载阶段,类加载器通过类的全限定名(如java.lang.String)查找对应的字节码文件,并将其加载到内存中。这一过程通常从文件系统、JAR包或网络等资源中读取字节码文件。
[*]链接(Linking):链接过程将类的字节码归并到JVM的运行时情况中。这一步包罗验证、准备息争析。
[*]验证(Verification):验证字节码文件的正确性,确保其符合JVM规范。
[*]准备(Preparation):为类的静态变量分配内存,并初始化默认值(留意,这里只是初始化为数据类型对应的零值,而不是代码中设置的默认值)。
[*]解析(Resolution):将符号引用转换为直接引用,即将类中的符号(如方法、字段)解析为实际内存地点。
[*]初始化(Initialization):实行类的静态初始化块和静态变量的赋值操作。比方,当类的静态变量或静态代码块被首次使用时,初始化将自动发生。
双亲委派模子
双亲委派模子(Parent Delegation Model):当一个类加载器试图加载某个类时,它会先将这个请求委托给父类加载器,而不是自己直接加载。只有当父类加载器无法找到该类时,才由当前类加载器实行加载。
特点
[*]这种机制确保了Java核心类库不会被用户自界说的类加载器替换或覆盖。
[*]子类的加载器可以望见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
[*]仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。
类加载器层次结构
https://i-blog.csdnimg.cn/direct/2d299162c0c64a0dbe9649f11fe230e0.png
假如你有英文底子的话,可以阅读一下官方对类加载器的文章
自界说类加载器
除了使用JVM提供的默认类加载器外,开发人员还可以根据需要自界说类加载器。自界说类加载器通常需要继续java.lang.ClassLoader类,并重写其中的findClass()方法或loadClass()方法。通过自界说类加载器,可以实现一些特殊的功能,如从网络加载类、从数据库加载类等。
** 为啥有系统类加载器还需要自界说类加载器呢**
我们通过创建自界说类加载器,可以实现Tomcat应用隔离 支持jsp OSGI实现动态模块化。这些听起来比较晕是吧。 Java同一个类被差别的类加载器加载会得到同一个类的差别Class对象。
public class Test26 {
public static void main(String[] args) throws ClassNotFoundException {
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> aClass = myClassLoader.loadClass("com.example.mydemo.test.Test1");
MyClassLoader myClassLoader1 = new MyClassLoader();
Class<?> bClass = myClassLoader1.loadClass("com.example.mydemo.test.Test1");
if (aClass != bClass) {
System.out.println("aClass is different from bClass");
}
}
}
aClass 跟 bClass 是差别的对象。
** 这有啥用呢**
[*]一个程序大概有多个模块,模块之间的大概使用的同一个类,但是差别的版本。假如我们都使用系统自带的加载器,就无法到达这个效果。通过自界说类加载器,我们就可以得到差别的类。实现隔离。
[*]实现热摆设,同一个类只会被classloader加载一次,所以当我们更改完代码,即使class变了,也不会被原来的加载器在重新加载。而通过自界说类加载器,我们每一次都是new一个新的自界说类加载器,这样我们就每次都可以得到一个新的Class对象,从而实现动态更新。
(所谓热摆设,就是在不重启应用的情况下,当类的界说即字节码文件修改后,能够更换该Class创建的对象)
总结
我们主要先容了类加载机制与双亲委派模子,另外重点先容了自界说类加载器。盼望能给你带去帮助。也欢迎大家留言,评论,私信。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]