Class.forName和classloader的区别分析

class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象。

先了解下类加载机制,

5个过程。
a.加载:java类运行时候会生成一个class字节码文件,加载的过程就是去我们的操作系统寻找这个class文件。而获取.class文件的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。

b.链接:又分为3个小过程。
    1.验证:验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求,确保不会危害虚拟机安全。
    2.准备:为类的静态变量(static filed)在方法区分配内存,并赋默认初值(0值或null值)。如static int maxNum = 10;
静态变量maxNum 就会在准备阶段被赋默认值0;而对于一般的成员变量是在类实例化时候,随对象一起分配在堆内存中。静态常量(static final filed)会在准备阶段赋程序设定的初值,如static final int maxNum  = 50; 静态常量a就会在准备阶段被直接赋值为50,对于静态变量,这个操作是在初始化阶段进行的。
    3.解析:解析是将符号引用替换为直接引用,解析动作针对类或接口,字段,类或接口的方法进行解析。
首先是用类加载器加载这个类。在加载的过程中逐步解析类中的字段和方法。
符号引用是以字面量的实形式明确定义在常量池中,直接引用是指向目标的指针,或者相对偏移量。

c.初始化:类初始化是类加载的最后一步,除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。主要工作是为静态变量赋程序设定的初值。如static int maxNum  = 10;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为10。
当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。

d.使用:在需要使用的地方调用

e.卸载:使用完了,java虚拟机进行清理。

类的加载的时机,有以下5中情况
1  遇到new、getstatic、putstatic或invokestatic这四条字节码指令
2  使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
4 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
5当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果 REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则会先触发其初始化。

上面介绍了类加载的几个过程,再具体看下Class.forName和classloader的区别。用代码演示。
先定义一个类

package com.test.a;

public class User {

	private static int a = 5;
	private static int b;
	private static int c = testC();
	
	static {
		System.out.println("执行静态代码块");
		System.out.println("执行静态变量a="+a);
	}
	
	public static void testA() {
		System.out.println("执行静态代方法testA");
	}
	
	public static void testB() {
		b = 10;
		System.out.println("输出静态变量b="+b);
		System.out.println("执行静态代方法testB");
	}
	
	public static int testC() {
		System.out.println("执行静态代方法testC");
		return 20;
	}
	
	
}

分别使用Class.forName和classloader加载,查看结果

package com.test.a;

public class TestClassLoad {

	public static void main(String[] args) {
		try {
			//Class.forName加载
			String userPath = "com.test.a.User";
			Class<?> forName = Class.forName(userPath);
			System.out.println("-------------------forName加载--------------------"+forName);
			//classLoader加载
			ClassLoader classLoader = ClassLoader.getSystemClassLoader();
			classLoader.loadClass(userPath);
			System.out.println("-------------------classLoader加载--------------------");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}



可以看到classloader只是做了加载,没有对类初始化,而Class.forName加载了静态变量和静态代码块,但是并没有加载静态方法。只有在静态变量调用了静态方法赋值的情况下才会加载静态方法。

看下Class.forName源码分析,他实际上也是调用了classloader去加载,但因为他的第二个参数指定了true,所以会去初始化类

要想不初始化,调用他的重载方法Class.forName(userPath,false,ClassLoader.getSystemClassLoader());将第二个参数设置为false,指Class被加载后是不是必须被初始化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值