博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
你知道java反射机制中class.forName和classloader的区别吗?
阅读量:4036 次
发布时间:2019-05-24

本文共 4135 字,大约阅读时间需要 13 分钟。

前两天头条有朋友留言说使用class.forName找不到类,可以使用classloader加载。趁此机会总结一下,正好看到面试中还经常问到。

一、类加载机制

上面两种加载类的方式说到底还是为了加载一个java类,因此需要先对类加载的过程进行一个简单的了解。我们写好的程序,然后run运行,过程可以直接看下面这张图:

在这里插入图片描述

往细了看大致分为5个阶段:

(1)加载:java类运行时候会生成一个class字节码文件,加载的过程就是去我们的操作系统寻找这个class文件。

(2)链接:这个过程就是把class文件加载到java虚拟机。

(3)初始化:在虚拟机中根据class文件进行初始化。

(4)使用:这个过程大家都明白。

(5)卸载:使用完了,java虚拟机进行清理。

对于class.forName和classloader来说针对的就是第一个过程,也就是加载过程。不过这俩虽然有一定的相似性,但是区别还是挺大的。

二、使用举例

我们使用代码,先看看如何使用。注意包的范围,避免加载不了。

第一步:定义User类

public class User {
private static int a = 10; {
System.out.println("普通代码块"); } static{
System.out.println("静态变量a:"+a); System.out.println("静态代码块"); }}

第二步:测试

public class FDDTest {
public static void main(String[] args) {
//注意,我在com.fdd.reflect包下建的类 String user = "com.fdd.reflect.User"; test(user); } public static void test(String user) {
try {
ClassLoader loader = ClassLoader.getSystemClassLoader(); System.out.println("classloader testing..."); Class
loaderUser = loader.loadClass(user); System.out.println("user " + loaderUser.getName()); System.out.println("---------------------------------------"); Class forNameUser = Class.forName(user); System.out.println("Class.forName testing..."); System.out.println("user " + forNameUser.getName()); } catch (ClassNotFoundException e){
e.printStackTrace(); } }}

我们在上面的test方法中,使用了两个加载方法。现在我们测试一下:

classloader testing...user com.fdd.reflect.User---------------------------------------静态变量a:10静态代码块Class.forName testing...user com.fdd.reflect.User

是不感觉有点区别。现在是先给出一个大体的使用,下面我们分析一下他们的区别。

三、区别

1、class.forName

class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。注意这里的静态块指的是在类初始化时的一些数据。但是classloader却没有,想要弄清楚这个原因,还是直接到源码中看看。

@CallerSensitivepublic static Class
forName(String className) throws ClassNotFoundException {
Class
caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}

在这个源码中我们会发现,其实底层真正实现的是forName0方法,那这几个参数又是什么意思呢?

(1)className:表示我们要加载的类名

(2)true:指Class被加载后是不是必须被初始化。 不初始化就是不执行static的代码即静态代码,在这里默认为true,也就是默认实现类的初始化。

(3)ClassLoader.getClassLoader(caller):表示类加载器,到这你会发现forNanme其实也是使用的ClassLoader类加载器加载的。

(4)caller:指定类加载器。

所以,在这里你可以指定是否在class加载后被初始化。而且底层还是使用的classloader加载的。

2、classloader

在上面的案例中我们发现,classloader并没有初始化静态块,原因最好还是到源码中看。

首先我们先进入到loadclass方法中的源码。

public Class
loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);}

这一步看起来还看不明白,没关系这里真正实现的是内部的loadclass,我们再跟进去看看。

protected Class
loadClass(String name, boolean resolve) throws ClassNotFoundException{
synchronized (getClassLoadingLock(name)) {
// 首先检查这个类是否已经被加载 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) {
} if (c == null) {
long t1 = System.nanoTime(); c = findClass(name); sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } //如果已经加载了,那就重新加载 if (resolve) {
resolveClass(c); } return c; }}

这个才是真正实现的方法,在这里的步骤其实很简单,大致流程是先判断class是否已经被加载,如果被加载了那就重新加载,如果没有加载那就使用双亲委派原则加载。加载的时候并没有指定是否要进行初始化。

所以现在他们的区别基本上很少,总结一下:

(1)class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。当然还可以指定是否执行静态块。

(2)classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

有一个小问题需要注意:我在网上看了几篇文章,亲测有错误,那就是class.forName其实是不会执行静态方法的,但是会初始化静态变量。错误的例子是使用了静态方法为静态变量赋值了。

ok,一个小知识点。如有问题,还请批评指正。

喜欢的可以关注我的公众号:java的架构师技术栈,获取更多文章和教程资源

转载地址:http://esbdi.baihongyu.com/

你可能感兴趣的文章
QT打开项目提示no valid settings file could be found
查看>>
Win10+VS+ESP32环境搭建
查看>>
Ubuntu+win10远程桌面
查看>>
flutter-实现圆角带边框的view(android无效)
查看>>
android 代码实现圆角
查看>>
flutter-解析json
查看>>
android中shader的使用
查看>>
java LinkedList与ArrayList迭代器遍历和for遍历对比
查看>>
drat中构造方法
查看>>
JavaScript的一些基础-数据类型
查看>>
JavaScript基础知识(2)
查看>>
转载一个webview开车指南以及实际项目中的使用
查看>>
android中对于非属性动画的整理
查看>>
一个简单的TabLayout的使用
查看>>
ReactNative使用Redux例子
查看>>
Promise的基本使用
查看>>
android给文字加边框(修改不能居中的问题)
查看>>
coursesa课程 Python 3 programming 统计文件有多少单词
查看>>
coursesa课程 Python 3 programming 输出每一行句子的第三个单词
查看>>
coursesa课程 Python 3 programming Dictionary methods 字典的方法
查看>>