1.被final修饰而没有被static修饰的类成员变量只能在三种情况下初始化:
/*
1.创建的时候立即初始化
2.创建之后由构造方法初始化
3.创建之后由代码块初始化
*/
class Test {
final int b = 2;
final int c;
public Test() {
c = 3;
}
final int a;
{
a = 1;
}
}
public class TestMode {
static Test abc = new Test();
public static void main(String[] args) {
System.out.println(abc.a);
System.out.println(abc.b);
System.out.println(abc.c);
}
}
2.被final修饰的类属性会被作为编译期常量加入常量池,以后访问对应类的常量池,不会在常量池中保存一个指向Test类a字段的符号引用,不触发类的初始化:
class Test {
final static int a = 1;
static {
System.out.println("访问编译期常量不触发类初始化");
}
}
public class TestMode {
public static void main(String[] args) {
System.out.println(Test.a);
}
}
查看 TestMode 类的常量池,如下:

查看 Test 类的常量池如下:

我们将代码修改一下如下:
class Test {
final static int a = 1;
static int b = 2;
static {
System.out.println("访问编译期常量不触发类初始化");
}
}
public class TestMode {
public static void main(String[] args) {
System.out.println(Test.a);
System.out.println(Test.b);
}
}
查看 Test 类的常量池,如下:

此时我们发现只出现了1没有出现2,也就是说非final是不存在常量池中的
查看 TestMode 类的常量池,如下:

我们发现常量池多了28~33这段内容:
=================================================================================
CONSTANT_Class_info: 类或接口的符号引用,其中的 index 指向字符串字面量的索引。
CONSTANT_Fieldref:字段的符号引用。
CONSTANT_NameAndType_info:字段或方法的部分符号引用。
这样看来的确是触发了类的初始化。
注意:33行的 Utf8 I 表示该值的类型:
其中 ACC_STATIC 是访问标志。
而在 Test 类中,final static int a = 1 会存在 CONSTANT_Integer_info(该表中不存在符号引用,只有具体的值)中
=================================================================================
再看如下:
/*
这段代码去掉final之后结果完全不一样
*/
public class TestMode {
public static void main(String[] args) {
final String a = "1";
final String b = "2";
String c = a + b;
String d = "12";
System.out.println(c == d);//返回true,证明a,b的确作为编译期常量
//加入了常量池,而c也在编译期确定其值
}
}
以上即为 final 所修饰属性特点。
3.看一个面试题加深理解
class Price {
static Price P = new Price(2.7);
static double apple = 20;//加上final后 输出结果为17.3
double Price;
public Price(double orange) {
Price = apple - orange;
}
}
public class PriceTest {
public static void main(String[] args) {
System.out.println(Price.P.Price);//结果为-2.7
}
}
这个程序中,在类加载阶段的准备阶段p和apple会被编译器赋予对应类型的默认初值(null和0.0),在随后的类加载的初始化阶段,由于static字段执行顺序是由字段在源文件中出现的顺序决定的,所以会先执行new Price(2.7),分配对象空间并对其做初始化,在这个时候apple的值还是0.0,所以最终结果为-2.7。
当使用final时,字面量20会在编译期加入Price类的常量池中的CONSTANT_Double_info,在遇到final字段时,在编译时编译器将会为该静态属性赋予ConstantValue属性,ConstantValue屬性的作用是通知虚拟机自动为静态变量赋值 ,对于类变量,有两种方式赋值:在类构造器<clinit>方法中或者使用ConstantValue属性,具有该属性的静态字段将会在类加载的准备阶段被赋予所指定的的值(这也是final static 字段必须手动赋值的原因)。所以最终结果为17.3。






被折叠的 条评论
为什么被折叠?



