一,类的生命周期
编写一个Java源文件之后,经过编译生成可以在虚拟机中运行的字节码文件(.class文件),Java类的生命周期就是指一个class文件从加载到卸载的过程。
Java类的完整生命周期:加载-连接-初始化-使用-卸载
也有没有初始化就直接被使用的情况。
二,类加载
类加载机制:
字节码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象,类加载的作用就是将字节码加载到内存,并对数据进行校验,转换解析,初始化,最终形成能够在 JVM 中使用的java类型。
类加载包括类生命周期中:
- 加载(Loading):由类加载器执行,查找字节码,并在堆中创建(只创建)一个java.lang.Class对象。
- 连接(Lingking):验证字节码,为静态域分配存储空间(只是分配,并不初始化),解析该类创建所需的其他类。—即校验,准备,解析。
- 初始化(Initialization):首先执行静态块初始化static{},初始化静态变量,执行静态方法(如构造方法)
加载链接初始化都是在程序运行期间完成的,使Java具有动态可扩展的特性。
2.1 加载
加载是由类加载器(Class Loader)完成的。ClassLoader可以动态的加载字节码到内存中。
在加载阶段,类加载器找到需要加载的类,并把类的信息(编译后的代码)加载到方法区中,然后再内存中实例化一个java.lang.Class 的实例作为方法区中这个类信息的入口。
其中ClassLoader 自顶向下分为BootStrap ClassLoader ,Extention ClassLoader,System ClassLoader.
2.2 链接
链接就是将已经加载的字节码文件组合到JVM运行状态中去,连接前该类必须成功加载。
包括:
- 验证。保证字节码结构上的正确性。确认类型符合Java语言的语义,并且不会威胁到虚拟机的完整性。
- 准备。正式为类变量(not 实例变量)分配内存,设置类变量初始值的阶段。主要是创建静态域,分配空间,设置默认值。注意:这里没有执行任何代码,因此设置的默认值为0,null,0f 等。
public static int n =2;
这里赋值时也是赋值为0.因为代码根本没有执行。 - 解析。对类中的接口,类,方法,变量的符号引用进行解析并定位,解析成直接引用。
2.3 初始化
Java 类第一次被真正使用时,JVM会进行该类的初始化。为类变量赋予正确的初始值
到了此阶段,才真正开始执行类中定义的Java程序代码,在准备阶段,类变量已经被设置默认值,在初始化阶段,赋代码中的值。
初始化主要是执行静态代码块和初始化静态域。
Java虚拟机规范中,每个类或接口被Java程序首次主动使用时才会初始化。
- 创建类的实例 (new)
- 调用类的静态方法
- 访问类或接口的静态变量(如果访问编译时就可以确定的常量(static final修饰的常量)则不会实例化)
- 反射
- 初始化一个子类 初始化一个子类必初始化其父类!
- 被标明为启动类(包含main方法)
除了这6种,其他使用Java类型的方式都是被动使用,不会导致Java类型的初始化。
任何一个类的所有祖先必须在该类初始化之前被初始化。然而对于接口,这条规则并不适用。在Java语法中规定,接口中的变量默认自动隐含:public static final
,再有在接口所声明的非常量字段被使用时,接口才会初始化。
—例子—-
这里的例子
package myblog.classloader;
/**
* @project MyBlog
* @create 2013年6月18日 下午7:00:45
* @version 1.0.0
* @author 张广
*/
public class Toy {
private String name;
//编译时确定
public static final int price=10;
static int t =2;
static {
System.out.println("Initializing");
}
Toy() {
System.out.println("Building");
}
Toy(String name) {
this.setName(name);
}
public static String playToy(String player) {
String msg = buildMsg(player);
System.out.println(msg);
return msg;
}
private String buildMsg(String player) {
String msg = player + " plays " + name;
return msg;
}
}
// 对上面的类,执行下面的代码:
Class c = Class.forName("myblog.rtti.Toy");
// c.newInstance();
上面的例子看到,没有实例化时,只会执行static{}静态块。只有实例化才会执行构造方法。类方法呢–应该是执行的时候才会初始化(属于2)。
如果执行Toy.t,首先会引发Toy的初始化(属于3 中的访问类变量),
首先会输出Initializing
再执行一次Toy.t,此时Initializing
不会再输出,因为初始化已经完成。
如果执行Toy.price 则不会输出Initializing
,因为price在编译阶段被存入常量池,不会引起类的初始化。(3中括号部分)
Java 初始化顺序:
- 静态成员初始化(a,静态变量 b,静态代码块)
- 普通成员初始化(a,变量,b,初始化块 c,构造器)(先是构造块,最后是构造器)
- 执行构造函数
2.4 卸载
当某个类满足:
- 类的所有实例都被回收
- 加载该类的ClassLoader被回收
- 该类对应的java.lang.Class对象没有任何地方被引用,无法再其他地方通过反射访问该类的方法。???–不明白
这时JVM就可以在方法区垃圾回收的时候对类进行卸载了。即清空类在方法区中的信息。
三,类生命周期与对象的生命周期
之前只了解Java中对象的生命周期,现在对类的生命周期也有了了解。
创建对象之前首先会触发类加载(加载,连接,初始化),类初始化完成后根据类信息在堆区中对对象进行实例化,初始化非静态变量,非静态代码以及默认构造方法,当对象使用完之后被垃圾收集器回收。
类的生命周期要比对象的生命周期长很多,对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况(即实例化类对象)????
详解Java类的生命周期:http://blog.csdn.net/zhengzhb/article/details/7517213
Java系列笔记(1)-Java类记载与初始化:http://www.cnblogs.com/zhguang/p/3154584.html#top
Java深度历险(二)——Java类的加载、链接和初始化:http://www.infoq.com/cn/articles/cf-Java-class-loader#