`
瘋叻ハ.兩
  • 浏览: 80632 次
  • 性别: Icon_minigender_1
  • 来自: 宁德
社区版块
存档分类
最新评论

Java起航 ---- 类的初始化历程

阅读更多

       既然谈到类的初始化,那就不得不把VM(Virtual Machine,虚拟机)先做个简单的介绍...

      VM,是Java程序运行的核心。它是不可视的,也就是说你在你的机器上根本找不到它。那么它到底是怎么产生的呢?其实它只是一个dll格式文件(全称是jvm.dll,该文件隐藏在jre目录下)。初始化VM的过程:java命令解释*.class文件时 ----> 通过环境变量找到JRE目录并产生JRE ----> 在JRE目录下,寻找jvm.dll文件,并初始化一个jvm (VM的更详细介绍请自行下载观看JVM工作原理

 

      当我们需要运行一个类时,过程且看图片“类的生命周期”。 本文只着重介绍到“初始化”一步,并尽可能的用代码证明得出的结论。     重申:类的连接分为验证、准备、解析

 

      1、装载

           也称类的加载。它是指将类的class文件(本地磁盘或者网络上)加载至VM内存中,并为之创建一个对应的java.lang.Class对象。(这部分内容本人正在消化中,代码消化完贴出...)2011.5.29 补上

        

           加载类的加载器有三种:

           1、Bootstrap ClassLoader:根类加载器,也称引导类加载器。它是VM自动实现的,代码是用c语言实现的,所以你在IDE中是找不到它的源代码滴。  它负责加载jdk中的系统类,加载Java的核心类如String。

 

           2、Extension ClassLoader:扩展类加载器。它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统指定的目录)中JAR的类包。

       

           3、System ClassLoader:系统类加载器。它负责在JVM启动时,加载来自命令java -classpath或者java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。一般都认为系统类加载器是加载应用程序第一个类的加载器

         

           代码如下

 

package com.ClassTests;

/**************************************************
 *                                                
 * @author:      瘋叻ハ.兩                                                                 
 * @create-time: 2011-5-22 下午07:13:14             
 * @revision:    1.0                                
 * @purpose:     类的加载                   
 *                                                
 **************************************************/
public class LoadClass {

	
	public static void main(String[] args){
		
		// 查看根加载器加载的类的路径
//		URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		
//		for(int i = 0; i < urls.length; i++){
//			System.out.println(urls[i].toExternalForm());
//		}
		
		// 获取JVM的名称
		System.out.println("JVM名称是:"+System.getProperty("java.vm.name"));
//		System.out.println("Java 类路径:"+System.getProperty("java.class.path"));
//		System.out.println("加载库时搜索的路径列表:"+System.getProperty("java.library.path"));
//		System.out.println("用户的主目录:"+System.getProperty("user.home"));
//		System.out.println("用户的当前工作目录:"+System.getProperty("user.dir"));
		
		
		// 获取系统类的加载器
		ClassLoader cl = ClassLoader.getSystemClassLoader();
		System.out.println("系统的类加载器是:"+cl);	
		// 获取加载器的类名
		System.out.println("类加载器的类名是:"+cl.getClass());
		
		
		// 获取扩展类加载器,因为是系统类加载器的父类
		ClassLoader clp =  cl.getParent();
		System.out.println("系统的类加载器的父类加载器是:"+clp);	
		// 获取扩展类加载器的类名
		System.out.println("系统的类加载器的父类加载器的类名是:"+clp.getClass());	
		
		
		// 获取根类加载器(结果是null,所以如果进一步获取类名,报NullPoint异常)
		System.out.println("根类加载器是:"+clp.getParent());
	}
}

 

 

 

  

           运行结果:JVM名称是:Java HotSpot(TM) Client VM
                         系统的类加载器是:sun.misc.Launcher$AppClassLoader@19821f
                         类加载器的类名是:class sun.misc.Launcher$AppClassLoader
                         系统的类加载器的父类加载器是:sun.misc.Launcher$ExtClassLoader@addbf1
                         系统的类加载器的父类加载器的类名是:class sun.misc.Launcher$ExtClassLoader
                         根类加载器是:null

 

            分   析: Java程序执行的流程:*.class --> 通过环境变量找到JRE目录 --> 在该目录找到jvm.dll -->  JVM初始化 -->  产生BootstrapLoader --> 载入ExtClassLoader --> 载入AppClassLoader --> 装载*.class --> 字节码校验 --> 解析器执行

 

 

      2、验证

           验证阶段是用于检验被加载的类是否有正确的内部结构,并和其他类协调一致(毫无代码可言)

 

 

 

      3、准备

       准备阶段是负责给类的静态属性分配内存,并设置默认初始值,而不是初始化(注意区别)。

 

     

代码如下

 

 

 

package com.ClassTests;

/**************************************************
 *                                                
 * @author:      瘋叻ハ.兩                                                                 
 * @create-time: 2011-5-21 下午08:21:25             
 * @revision:    1.0                                
 * @purpose:     探秘类的准备阶段发生的事情                  
 *                                                
 ***************************************************/
public class ClassReady {
	static{
		b = 4;
	}
	static int b;
	
	public static void main(String[] args) {
		System.out.println(b);
	}
}

  

     运行结果:4

 

       分  析 :首先第一个问题,为什么在静态初始块中b没有定义类型不报错?其次,结果为什么是4,而不是int的默认值0? 其实正是在类的准备阶段,VM为类的静态属性分配内存,使得内存中已然存在了b变量,所以在类的初始化阶段,虽未给b定义类型,但系统也不报错。接下来进行类的初始化,只有静态代码块的初始化,所以,因此结果为4,证明了类在准备阶段为类的静态属性分配内存,而并没有执行它的成员动作

 

 

 

      4、解析

        解析阶段是将类的二进制数据中的符号引用替换成直接引用。(也无代码可言)

 

 

 

      5、类的初始化

        类的初始化阶段,VM负责对类进行初始化,主要是对类属性(静态属性)进行初始化,方式有2:(1)声明静态属性时指定初始值;(2)使用静态初始化块为静态属性指定初始值。

        

      代码如下

 

 

package com.ClassTests;

/**************************************************
 *                                                
 * @author:      瘋叻ハ.兩                                                                 
 * @create-time: 2011-5-21 下午08:40:58             
 * @revision:    1.0                                
 * @purpose:     类的初始化方式                    
 *                                                
 ***************************************************/
public class ClassInition {

	static int a = 5;  // 声明时指定初始值
	static int b;
	static int c = 7;
	static{
		b = 6;         // 声明时未指定,可以在静态初始块中补上
		
		c = 8;         // 如果两种都有,结果取后一个
	}
	
	public static void main(String[] args) {
		System.out.println("a的初始化值是 "+a);
		System.out.println("b的初始化值是 "+b);
		System.out.println("c的初始化值是 "+c);
	}
}

 

       运行结果:a的初始化值是 5
                     b的初始化值是 6
                     c的初始化值是 8

 

        分    析 :一切都按部就班初始化着

 

 

        

         5、实例初始化

              在进行玩类的初始化,就会进行实例初始化。过程与类的初始化不相上下。

 

      代码如下

package com.ClassTests;

/**************************************************
 *                                                
 * @author:      瘋叻ハ.兩                                                                 
 * @create-time: 2011-5-21 下午01:27:04             
 * @revision:    1.0                                
 * @purpose:     简析类的生命周期及类的初始化                    
 *                                                
 ***************************************************/
/**************************************************
 *                                                
 * @author:      瘋叻ハ.兩                                                                 
 * @create-time: 2011-5-21 下午02:25:46             
 * @revision:    1.0                                
 * @purpose:     类的实例化                    
 *                                                
***************************************************/
class Parents{
	public Parents(){
		System.out.println("---此时调用了子类的构造方法,父类的初始化开始...");
	}
}

public class ClassLife extends Parents {
	private static int c;
	
	public ClassLife(){
		//super();  //有无super()结果一致,说明程序默认优先初始化直接父类,然后再子类。如果需要显示调用,super()必须放在第一句
		System.out.println("---此时调用了子类构造方法,子类的初始化开始...");
	}
	// 静态初始化块
	static {
		System.out.println("---执行了类的初始化...");
		System.out.println(c);
	}
	

	// 初始化块
	{
		System.out.println("---执行了对象的初始化块...");
		
		// 问题1: 为什么没有声明b,而可以使用4。
		b = 4; 
	}
	
	int b;

	public static void main(String[] args) {
		ClassLife cl =  new ClassLife();
		System.out.println(cl.b); // 对象属性的初始化与类的属性初始化一致

	}
}     

      运行结果:---执行了类的初始化...
                     ---此时调用了子类的构造方法,父类的初始化开始...
                     ---执行了对象的初始化块...
                     ---此时调用了子类构造方法,子类的初始化开始...
                     4

   

        分  析 : 认真看上面的类的初始化

    

 

     

     

     最后归纳下对象创建过程发生的事

       给类的属性在分配内存 ----> 初始化类的属性  ---->  给对象属性分配内存 ---->  在初始块中初始化对象属性  ---->如果有父类,优先构造初始化父类  ---->   调用子类的构造器,创建对象。

      

 

 

 

  

  • 大小: 67.2 KB
1
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics