Java中final的进一步理解
final修饰符的作用
在JMM中要求final域(属性)的初始化动作必须在构造方法return之前完成。换言之,一个对象创建以及将其赋值给一个引用是两个动作,对象创建还需要经历分配空间和属性初始化的过程,普通的属性初始化允许发生在构造方法return之后(指令重排序)。
普通变量和final变量到底又有什么区别呢?看看下面这段代码的输出结果
public class FinalConstructorTest {
static abstract class A {
public A() {
display();
}
public abstract void display();
}
static class B extends A {
private int INT = 100;
private final int FINAL_INT = 100;
private final Integer FINAL_INTEGER = 100;
private String STR1 = "abc";
private final String FINAL_STR1 = "abc";
private final String FINAL_STR2 = new String("abc");
private final List<String> FINAL_LIST = new ArrayList<>();
private B(){
super();
System.out.println("abc");
}
public void display() {
System.out.println(INT);
System.out.println(FINAL_INT);
System.out.println(FINAL_INTEGER);
System.out.println(STR1);
System.out.println(FINAL_STR1);
System.out.println(FINAL_STR2);
System.out.println(FINAL_LIST);
}
}
public static void main(String[] args) {
new B();
}
}
输出结果如下:
0
100
null
null
abc
null
null
abc
匿名内部类使用外部变量,这个变量必须用final来声明才可以被使用
伪代码如下:
public void test() {
final int a = 100;
new A() {
public void display() {
System.out.println(a);
}
}
其他操作
}
这其实是对一个语法的疑问,本身没什么好解释的,但如果非要解释,可以从这个角度来理解:在编译时,这个地方会自动生成一个匿名内部类,而本地变量a的作用域是在方法test()中,它如何能用到另一个内部类中呢?
其中一种方式是参数传递,另一种方式就是作为一个属性存在于匿名内部类对象中。无论哪一种方式都会在匿名内部类对象中有一份数据拷贝。
JVM在设计时并不知道我们的代码会怎么写,或者说不明确在所创建的匿名内部类中到底会做什么,例如在代码中完全可以在内部创建一个线程来使用这个变量,或者创建一个任务提交给线程池来使用这个变量。如果是这样,匿名内部类的运行与该方法本身的运行处于两个线程中,当外部方法可能已经结束时,那么相应的局部变量的作用域已经结束,自动会被回收,要保证匿名内部类一直可以使用该变量,就只能用拷贝的方法。
如果这个属性不是final修饰的,在匿名内部类中使用“同名”的变量操作,并且可以对它做任意修改,自然外部也应当能感知到。但是事实上不能感知到,因为这是一份数据拷贝。这种语法的设计手段是为了避免误解,语法上强制约束它为final修饰符。