- 1.
public class FinalClass {
public static String str = "aaa";
static {
System.out.println("hello world");
}
}
public class FinalTest {
public static void main(String[] args) {
String str = FinalClass.str;
System.out.println(str);
}
}
运行时,打印 hello world
- 2.
public class FinalClass {
public static final String str = "aaa";
static {
System.out.println("hello world");
}
}
public class FinalTest {
public static void main(String[] args) {
String str = FinalClass.str;
System.out.println(str);
}
}
运行时,不打印 hello world.
原因: final修饰的变量在编译时就确定,编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。
可以通过字节码来比较:
- 有final修饰的时候的 FinalTest.class:
[root@localhost foo]# javap -c FinalTest.class
Compiled from "FinalTest.java"
public class foo.FinalTest {
public foo.FinalTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String aaa --此处已经是明确值了
2: astore_1
3: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
}
2. 没有final修饰的时候的 FinalTest.class:
[root@localhost foo]# javap -c FinalTest.class
Compiled from "FinalTest.java"
public class foo.FinalTest {
public foo.FinalTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #16 // Field foo/FinalClass.str:Ljava/lang/String;
3: astore_1
4: getstatic #22 // Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #28 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
11: return
}
可以看到1中的字节码,
0: ldc #16 // String aaa
已经明确字符串为‘aaa’.
回过头来解释上面的现象,final修饰的变量在编译期就决定确定值了,与具体的Class没有联系(可能还有藕
断丝连的联系,具体不清楚),所以在执行String str = FinalClass.str;
的时候看似是加载了FinalClass类,其实
不然,也就不会触发静态代码块中的语句。