final变量的定义:变量一经初始化就不能指向别的对象了。
final用于定义基本类型时,数值将保持不变;final用于定义对象引用时,final使引用保持不变,一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象了,然而,对象其自身却是可以被修改的,例如:
final StringBuffer sb= new StringBuffer("abc"),则:
sb = new StringBuffer("def"); //错误操作,因为不能再将sb指向另一个对象
sb.append("def"); //正确操作,可以修改对象本身
当final类型作为参数传递时,其原理与平常定义基本无异,即参数为基本类型时,数据只可读不可改;为对象引用时,可改对象本身,但不能指向其他对象,这一特性主要用来向匿名内部类传递数据,其目的是为了保证调用的一致性,即保证变量在内部类和其外部的同步改变。下面对此作出解释:
假设方法test匿名调用ABSClass:
public static void test(final String s){
//或final String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
//其它代码.
}
从代码上看,内部类ABSClass访问了外部参数s,它是怎么获得s的呢,编译器是这样实现的:
public static void test(final String s){
//或final String s = "axman";
class OuterClass$1 extends ABSClass{
private final String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
ABSClass c = new OuterClass$1(s);
//其它代码.
}
那么,如果没有设定参数为final呢,此时s就可以指向其他对象了,即:
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
s = "other";
}
};
System.out.println(s);
}
就会编译成:
public static void test(String s){
//或String s = "axman";
class OuterClass$1 extends ABSClass{
private String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
s = "other"; //s指向另一个对象new String("other")
}
};
ABSClass c = new OuterClass$1 (s);
}
内部类的s重新指向"other",但并不影响test的参数或外部定义的那个s.同理如果外部的s重新赋值内部类的s也不会跟着改变。在语法上是一个s,在内部类中被改变了,但结果打印的出来的你认为是同一的s却还是原来的"axman",
总而言之,实际上是用final来阻止将变量指向另一个对象的操作,从而阻止变量在内外部不一致变化的现象。
上述内容参考:书籍《Thinking in Java》
博文 http://blog.csdn.net/axman/article/details/1460544#comments
为了进一步理解上面例子,我做了一个测试例子:
public class MyTest {
private final StringBuffer sb ;
//private StringBuffer sb;
public MyTest(StringBuffer sb) {
this.sb = sb;
}
public void m() {
sb.append("***");
//sb = new StringBuffer("def");
}
public StringBuffer getSb() {
return this.sb;
}
public static void main(String[] args) {
final StringBuffer sb = new StringBuffer("abc");
//StringBuffer sb = new StringBuffer("abc");
MyTest mt = new MyTest(sb);
mt.m();
System.out.println(sb);
System.out.println(mt.getSb());
}
}
若将变量sb都定义为final,输出结果都为abc****,类比前面例子,保证了前后的一致;若都去掉final修饰符,则在方法m中,sb可以指向另一个对象,使得输出结果为abc, def,即不再同步改变。