Class.forName杂谈

随声附和 提交于 2019-12-10 03:22:36

既然是杂谈,那么肯定会比较杂乱,都是点到为止,不回做过多的原理分析,如果对于原理,可能需要深入了解JVM内部的东西了。<br/> 首先,我来展示一下几个现象,然后对这几个现象进行分析。<br/> 相关几个类的定义

<!-- lang:java-->
package com.bieber.gc;

public class Super {

static{
	System.out.println("super static block");
}
public Super(){
	System.out.println("super init");
}

public void test(){
	System.out.println("invoke test");
}
}

public class Main extends Super{

 
public Main() {
	System.out.println("main init");
}

static{
 System.out.println("sub static block");
}
public void test(){
	System.out.println("main test");
}

}

这里涉及到两个类,下面针对这两个类来写几个测试用例。<br/> ####用例一

用例代码

<!-- lang:java-->
public static void main(String[] args) throws Exception {
	try {
		System.out.println("#########start load class##########");
		 Thread.currentThread().getContextClassLoader().loadClass("com.bieber.gc.Main");
		System.out.println();
		System.out.println("#########start init class#########");
		Main main = new Main();
		System.out.println();
		System.out.println("#########invoke#########");
		main.test();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
}

#####输出结果

<!-- lang:java -->
#########start load class##########

#########start init class#########
super static block
sub static block
super init
main init

#########invoke#########
main test

#####结果分析 可以看到通过Classloader加载类的时候,并没有执行static代码块,而static代码块却放到了调用构造函数的时候执行了,并且static代码块是在构造函数之前执行的。同时在调用在子类重写的方法时候,父类方法没有执行(这点是毫无疑问的)。 ####用例二 #####用例代码

<!-- lang:java -->
public static void main(String[] args) throws Exception {
	try {
		System.out.println("#########start load class##########");
		Class.forName("com.bieber.gc.Main", true, Thread.currentThread().getContextClassLoader());
		System.out.println();
		System.out.println("#########start init class#########");
		Main main = new Main();
		System.out.println();
		System.out.println("#########invoke#########");
		main.test();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
}

#####执行结果

<!-- lang:java -->
#########start load class##########
super static block
sub static block

#########start init class#########
super init
main init

#########invoke#########
main test

#####结果分析 发现这个时候子类和父类的static代码块在调用构造函数之前执行的,这点说明此处的Class.forNameClassLoader执行效果一样。其他部分和上面基本一致。 ####用例三 #####用例代码

<!-- lang:java -->
public static void main(String[] args) throws Exception {
	try {
		System.out.println("#########start load class##########");
		Class.forName("com.bieber.gc.Main", false, Thread.currentThread().getContextClassLoader());
		System.out.println();
		System.out.println("#########start init class#########");
		Main main = new Main();
		System.out.println();
		System.out.println("#########invoke#########");
		main.test();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}

#####执行结果

<!-- lang:java -->
#########start load class##########

#########start init class#########
super static block
sub static block
super init
main init

#########invoke#########
main test

#####结果分析 这个执行结果和和用例一的结果一样。

####总结 上面列举了三个测试用例,用例二和用例三的却别就是 Class.forName("com.bieber.gc.Main", true, Thread.currentThread().getContextClassLoader());Class.forName("com.bieber.gc.Main", false, Thread.currentThread().getContextClassLoader());的区别,这个很容易理解,其实第一个是在加载类的时候是初始化了类(Class)的其他信息,而第二个是延迟初始化。如果是延迟初始化从执行结果上来看和hread.currentThread().getContextClassLoader().loadClass("com.bieber.gc.Main");是一样的。<br/> 通过上面可以理解为,类的加载分为两个步骤:<br/>

  1. 只是装载类,类似将类的字节码加载到JVM中<br/>
  2. 解析类的结构,并初始化类的相关信息,比如static代码块<br/>

并且可以得到父类的加载总是在子类之前,不管是static代码执行,还是构造函数的调用。这也很容易理解,因为子类中可能会调用父类的信息,所以先需要将父类准备好了,子类调用的时候,才没问题。<br/>

此处给一个建议:谨慎在父类中定义protected(public) static的属性,因为这个时候,是所有子类共用一份内存,很容导致读写冲突。<br/>

针对上面的测试,下面列举出几个其他的测试,大家看看会得到什么结果?

<!-- lang:java -->
package com.bieber.gc;

public class Field {

static{
	System.out.println("field static block");
}
}

public class Main extends Super{


private static Field field=new Field();
public Main() {
	System.out.println("main init");
}

static{
 System.out.println("sub static block");
}
public void test(){
	System.out.println("main test");
}

}

或者

public class Main extends Super{


private static Field field;
public Main() {
	System.out.println("main init");
}

static{
 System.out.println("sub static block");
}
public void test(){
	System.out.println("main test");
}

}

我把Main类做了上面的调整,看看执行上面的测试用例,会得到什么结果。欢迎讨论交流,也希望大家也能分享一下你们在开发过程中的一些东西。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!