类的初始化的时机的研究(实例学习讲解)

package com.easyway.commons.ispace.dev.lang.classloader;
/**
 * 关于类的初始化的时机的研究:
 * 
 * java虚拟机只有在程序首次主动使用一个类或者接口时,才会初始化。
 * 只有6种活动看作是程序对类或者接口的主动使用。
 * (1) 创建类的实例,创建实例类的途径包括:用new 语句创建实例或者通过反射,克隆以及序列化
 *   手段来创建实例。
 * (2) 调用类的静态方法
 * (3) 访问某一个类或者接口的常量或者对类的静态变量赋值。
 * (4) 调用java API 中某些反射方法。如:调用Class.forName()时,类没有被初始化,
 *    那么调用方法就会初始化这个类,然后返回类的实例。备注:forName()方法是java.lang.Class的静态方法。
 * (5) 初始化一个类的子类。例如对Sub的初始化,相等于对它父类Base类的主动使用,因此会先初始化Base类。
 * (6) java虚拟机启动时被标明为启动类的类。例如对于 java ClassCircleLine 命令,ClassCircleLine
 *    类就是启动类, java虚拟机会先初始化它。
 *    除了上述6中情况之外,其他使用java类的方式都被看作是被动使用。都不会导致类的初始化。
 * @author longgangbai
 * @date 2010-5-7
 * @version 1.0
 * @since JDK6.0
 */
class shape{
	static int a=1;
	static{
		System.out.println("init shape  static ");
	}
	/**
	 * (4)只有当程序访问的静态变量或者静态方法的确在当前类或者接口中定义是,才可看作是对类或者接口的主动调用。
	 * 例如Range.a 和Range.method(),由于静态变量a 和静态方法method()在shape父类中定义,
	 * 因此java虚拟机仅仅初始化父类shape,而没有初始化Range。
	 */
	static void method(){
		System.out.println("static void method");
	}
}
/**
 * (3) 当java 虚拟机初始化一个类时,要求他的所有父类都被初始化。但是这个规则并不适用接口
 * A. 在初始化一个类时,并不会先初始化它所实现的接口。
 * B. 在初始化一个接口时,并不会先初始化它的父类接口。
 * 因此一个父类接口并不会因为它的子即可或者实现类的初始化而初始化。
 * 只有当程序首次使用特定的静态变量时,才会导致该接口的初始化。
 */
class Range extends shape{
	static int b=1;
	
	static{
		System.out.println("init Range  static");
		System.out.println("now init Range");
		
	}
}
class ClassLoad{
	static{
		System.out.println("init ClassCircleLine");
	}
	/**
	 * 输入结果如下:
	 * init ClassLoad  首先启动连接ClassLoad 
	 *init shape  static   调用  Range.a 开始执行父类的初始化语句
     * a =1    //在初始化之后才执行代码 
	 * static void method
	 * 
	 * 
	 * @param args
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) {
		System.out.println(" a ="+Range.a);
		Range.method();
	}
}
public class ClassCircleLine {
	/**
	 *  (1)对于final 类型的静态变量,如果在编译时 不能计算出变量的值。那么这种变量使用看作是对类的主动调用,会导致初始化。
	 *  由于编译器不会计算变量m的值,因此变量m不是编译时常量。当被调用时,java虚拟机会初始化该类,是的变量a在方法区拥有特定的内存和初始值。
	 */
	private static final double m=Math.random()*5; //变量m不是编译时的常量
	/**
	 *  (2)当java虚拟机加载并连接类是,不会再方法区内为它的编译时常量分配内存。
	 * 对于final 类型的静态变量,如果在编译时就能计算出变量的取值。那么这种变量看作编译时的常量。
	 * java程序中队类中的编译时常量的使用,被看作是对类的被动使用,不会导致类的初始化。
	 */
	private static final int a=2*3;  //变量a是 编译时的常量
	
	static{
		System.out.println("init ClassCircleLine");
	}
	/**
	 * 输入结果如下:
	 * after loader ClassLoad 
     * before init ClassLoad 
     * init ClassCircleLine
     * (5) 调用ClassLoader类的loadClass()方法加载一个雷,并不是对类的主动使用,不会导致类的初始化,
	 * 在main方法中,系统类加载器加载ClassLoad,尽管ClassLoad被加载,但是没有被初始化,当程序调用
	 * Class的静态方法forName(“ClassLoad”)显示的初始化ClassLoad时,才是对ClassLoad的主动使用,
	 * 将导致ClassLoad被初始化,他的静态代码块被执行。
	 * @param args
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws ClassNotFoundException {
		ClassLoader loader=ClassLoader.getSystemClassLoader();
		Class objClass=loader.loadClass("com.easyway.commons.ispace.dev.lang.classloader.ClassLoad");
		System.out.println("after loader ClassLoad ");
		System.out.println("before init ClassLoad ");
		objClass=Class.forName("com.easyway.commons.ispace.dev.lang.classloader.ClassLoad");
		
	}

}

 

 

 

package com;

/**
 * 对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是
 * (静态变量、静态初始化块)>(变量、初始化块)>构造器。
 * 运行以上代码,我们会得到如下的输出结果:
 *   1、静态变量
 *   2、静态初始化块 
 *   3、变量
 *   4、初始化块 
 *   5、构造器
 * 
 * @author longgangbai
 * 
 * 
 */
public class InitialOrderTest {

	// 静态变量
	public static String staticField = "静态变量";
	// 变量
	public String field = "变量";

	// 静态初始化块
	static {
		System.out.println(staticField);
		System.out.println("静态初始化块");
	}

	// 初始化块
	{
		System.out.println(field);
		System.out.println("初始化块");
	}

	// 构造器
	public InitialOrderTest() {
		System.out.println("构造器");
	}

	public static void main(String[] args) {
		new InitialOrderTest();
	}
}

 

 

package com;

/**
 * 运行一下上面的代码,结果马上呈现在我们的眼前:
 * 
 * 1、父类--静态变量 
 * 2、父类--静态初始化块 
 * 
 * 3、子类--静态变量 
 * 4、子类--静态初始化块 
 * 
 * 5、父类--变量 
 * 6、父类--初始化块 
 * 7、父类--构造器
 * 
 * 8、子类--变量 
 * 9、子类--初始化块 
 * 10、子类--构造器
 * 
 * 现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,
 * 实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。
 * 
 * @author longgangbai
 * 
 */
class Parent {
	// 静态变量
	public static String p_StaticField = "父类--静态变量";
	// 变量
	public String p_Field = "父类--变量";

	// 静态初始化块
	static {
		System.out.println(p_StaticField);
		System.out.println("父类--静态初始化块");
	}

	// 初始化块
	{
		System.out.println(p_Field);
		System.out.println("父类--初始化块");
	}

	// 构造器
	public Parent() {
		System.out.println("父类--构造器");
	}
}

public class SubClass extends Parent {
	// 静态变量
	public static String s_StaticField = "子类--静态变量";
	// 变量
	public String s_Field = "子类--变量";
	// 静态初始化块
	static {
		System.out.println(s_StaticField);
		System.out.println("子类--静态初始化块");
	}
	// 初始化块
	{
		System.out.println(s_Field);
		System.out.println("子类--初始化块");
	}

	// 构造器
	public SubClass() {
		System.out.println("子类--构造器");
	}

	// 程序入口
	public static void main(String[] args) {
		new SubClass();
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值