当前位置: 首页 > Java > 正文

java static的初始化注意事项

1 星2 星3 星4 星5 星 (2 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

类的load/link/initialization是Java中最为基础的知识,细节的信息在JVM规范中有描述,说下关于static的初始化(有些时候还是挺容易写错)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class A {
 
    public static void main(String[] args) {
        System.out.println(B.NICK);
        System.out.println(B.NAME);
    }
 
}
 
public class B{
 
    public static final String NICK = "nick";
 
    public static final String NAME = getName();
 
    private static String getName(){
        return "bluedavy";
    }
    static{
        System.out.println("hellojava");
    }
}

上面这段代码执行的输出为:
nick
hellojava
bluedavy

不按规范里那么学术化的来解释上面的原因,感兴趣的可以对上面编译后的A、B两个类用javap -c -verbose -private来查看里面的具体信息,javap -c -verbose B后可以看到如下的关键信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static final java.lang.String NICK;
  Constant value: String nick
 
public static final java.lang.String NAME;
 
static {};
  Code:
   Stack=2, Locals=0, Args_size=0
   0:   invokestatic    #14; //Method getName:()Ljava/lang/String;
   3:   putstatic       #18; //Field NAME:Ljava/lang/String;
   6:   getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:   ldc     #26; //String hellojava
   11:  invokevirtual   #28; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
   14:  return

可以看到NICK后面标为Constant value,而NAME的赋值则放在了static block中,java在将源码编译为字节码时,会将static对应的赋值动作或源码中的多个static block(例如{…} static{…})按照顺序全部放到static block中,如用btrace在运行时要跟踪这个static block的执行,可跟踪方法,就类似构造器在运行时对应的方法名为。
在编译为字节码后,对于B的代码而言,其实相当于变成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class B{
 
    public static final String NICK = "nick";
 
    public static final String NAME;
 
    private static String getName(){
        return "bluedavy";
    }
    static{
        NAME = getName();
        System.out.println("hellojava");
    }
}

至于为什么在调用B.NICK的时候没触发static block的执行,javap看下A的字节码就懂了,查看时可看到如下关键信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #22; //String nick
   5:   invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
   8:   getstatic       #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  getstatic       #30; //Field com/bluedavy/spike/B.NAME:Ljava/lang/String
;
   14:  invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
   17:  return

可看到System.out.println(B.NICK)这行相当于被直接改为了System.out.println(“nick”),仔细看过JVM规范的同学会知道对于constant类型的static final变量,在编译为字节码阶段就会直接被替换为对应的值,这里有个小小的坑是可能会碰到的,就是编译的时候只编译了对应的static final变量的代码,但没重编译相应引用的代码,那就悲催了,运行的时候还会是之前的值。

本文固定链接: http://www.chepoo.com/java-static-initialization-considerations.html | IT技术精华网

java static的初始化注意事项:等您坐沙发呢!

发表评论