关于类的初始化时机的小结

看了下 臧圩人 的 JAVA面试题解惑系列(一)——类的初始化顺序,链接在这里:[url]http://zangweiren.iteye.com/blog/208122[/url]
感觉总结得不错.我也补充几点.

就是关于类的初始化顺序的问题,其实当java程序需要使用某个类时,java虚拟机会确保这个类已经被加载,连接,和初始化,其中连接过程又会执行验证,准备和解析这三个子步骤.这里不进行详细的讲解.有关资料可以去google下.
那么当类或者接口被加载和连接时,JVM严格定义了初始化的时机,所有的JVM实现必须在每个类或接口被java程序"首次调用"时才初始化它们.很正常,要是你的应用有N多个类,有些类可能一时用不到而JVM统统帮你加载的话,你的机子能受得了吗?
因此,java程序对类的使用方式可分为有两种:[color=red]主动使用和被动使用[/color]那么参照前面的说法,JVM只有在程序需要主动使用一个类或接口时才会初始化它, 那什么时侯是程序对类的主动使用呢?有以下六种情况:
[list]
[*]使用new创建类的实例时,或者通过反射,克隆,和反序列化等方法进行创建类的实例时.
[*]调用类的静态方法时.
[*][color=red]访问某个类/接口的静态变量[其中这里的静态变量的定义又有区别,将会在以下进行讲解],[/color]或对该静态变量进行赋值操作
[*]初始化一个类的子类
[*]JVM启动时被标注为启动类的类.
[/list]

较为特殊的是访问某个类/接口的静态变量这个操作它分为两种情况:
[list]
[*]对于final类型的静态变量,如果在编译期间就能够计算出变量的值,那么访问该静态变量时,将被看作是对类的被动使用,而不会导致类的初始化.
[*]而不能在编译期间确定值的静态变量,访问该静态变量时,将被看作是对类的主动使用,会导致类的初始化.
[/list]

示例1:访问编译常量时,不会导致类的初始化

/**
* 测试类Tester
*/
class Tester{
public final static int number=100;
static{
System.out.println("Tester类被初始化!number="+number);
}
}

/**
* 在测试类Demo中访问编译时常量
*/
public class Demo1 {

/**
* main方法 程序入口
* @param args
*/
public static void main(String[] args) {
System.out.println(Tester.number); //仅打印出100而静态代码块中的内容不会被输出
}
}



示例2:访问不能在编译期间确定的静态常量值时,导致类的初始化

/**
* 测试类Tester
*/
class Tester{
public final static int number=(int)(Math.random()*100)/10+2;
static{
System.out.println("Tester类被初始化!number="+number);
}
}

/**
* 在测试类Demo中访问编译时常量
*/
public class Demo1 {

/**
* main方法 程序入口
* @param args
*/
public static void main(String[] args) {
System.out.println(Tester.number); //先会求出number的值然后执行类的静态代码块.打印静态代码块内的内容后显示number的结果.
}
}



较为特殊的情况是当JVM初始化一个类时,要求它的所有父类都已经被初始化.而只有当程序访问的静态变量或静态方法在当前类或接口中定义时,才视作对类的主动使用.参看以下示例:


/**
* 父类Base
*/
class Base{
public static final int number_base=88;
static{
System.out.println("Base类被初始化!");
}

public static void sayHello(){
System.out.println("在Base类的sayHello()方法中!");
}
}

/**
* 子类Sub
*/
class Sub extends Base{
public static final int number_sub=66;
static{
System.out.println("Sub类被实例化!");
}
}


/**
* 测试类
*/
public class Demo2 {
/**
* main 方法,程序唯一入口
* @param args
*/
public static void main(String args[]){
//访问父类的静态变量number_base
System.out.println(Sub.number_base);

//正确打印结果为88
}
}

这时侯可能有人会说:怎么是88呢?那父类不是会被初始化吗?静态块中的打印语句怎么没有执行?这是因为Sub.number_base这个静态的常量是编译期确定的静态常量,所以它不会对Base类进行初始化操作.所以只打印出常量的值.如果在main方法中使用Sub.sayHello()那么将会对Base类进行初始化.同时打印出静态代码块中的输出语句.但是不会对Sub进行初始化操作,参看示例:

/**
* 父类Base
*/
class Base{
public static final int number_base=88;
static{
System.out.println("Base类被初始化!");
}

public static void sayHello(){
System.out.println("在Base类的sayHello()方法中!");
}
}

/**
* 子类Sub
*/
class Sub extends Base{
public static final int number_sub=66;
static{
System.out.println("Sub类被实例化!");
}
}


/**
* 测试类
*/
public class Demo2 {
/**
* main 方法,程序唯一入口
* @param args
*/
public static void main(String args[]){
//访问父类的静态变量number_base
System.out.println(Sub.number_base);//正确打印结果为88
//这里调用了继承自父类的sayHello()方法,将会导致父类初始化而子类不会被初始化
Sub.sayHello();
//打印:
//88
//Base类被初始化!
//在Base类的sayHello()方法中!

}
}



而且,最后提醒大家的是,当类存在有继承关系时,类的初始化顺序也会有所不同,看以下的例子,估计你能猜到为何打印结果是这样啦~

package cn.com.wlf.classdemo.src;

/**
* 父类Base
*/
class Base{
public static final int number_base=88;
static{
System.out.println("Base类被初始化!");
}

public static void sayHello(){
System.out.println("在Base类的sayHello()方法中!");
}
}

/**
* 子类Sub
*/
class Sub extends Base{
public static final int number_sub=66;
static{
System.out.println("Sub类被实例化!");
}
/**
* 子类的静态方法sayHello_Sub
*/
public static void sayHello_Sub(){
System.out.println("这是子类Sub中的方法中打印的.");
}
}


/**
* 测试类
*/
public class Demo2 {
/**
* main 方法,程序唯一入口
* @param args
*/
public static void main(String args[]){
Sub.sayHello_Sub();
//打印结果是:
//Base类被初始化!
//Sub类被实例化!
//这是子类Sub中的方法中打印的.
}
}




第一次发表文章,请大家多多包涵 :wink:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值