说说final

final 这个关键字从学Java开始就有提及。它可以用来修饰class, method, method parameter, variable等。这里主要说说最后这一种。

对于有final 修饰的variable, 无论是instance variable 还是local variable, 其含义是说(一般)一经初始化和赋值,其值就无法改变。这里,对于instance variable, 我们说,这“初始化和赋值”可能是在声明时就完成的,也可以是在构造函数中完成的。

然而,这个一般无法改变的值却是可以改变的。在JSR 133 中提到了用反射改变final 的instance field 的方法。这里截取的是那里面的例子:

class  A  {
final int x;
A() 
{
= 1;
}

int f() {
return d(this,this);
}

int d(A a1, A a2) {
int i = a1.x;
g(a1);
int j = a2.x;
return j - i;
}

static void g(A a) {
// uses reflection to change a.x to 2
}

}

在JDK1.4 以及它以前的版本中,这种方法是行不通的。而在JDK 5 中,这种方法却可行的。具体做法是分别调用Field 的setAccessible(true) 方法和setX 方法进行赋值。

但是,这种方法可能会因为reorder 导致问题。JSR133 中说到:

... Another problem is that the semantics is designed to allow aggressive optimization of final fields.
Within a thread, it is permissible to reorder reads of a final field with calls to methods that may
change final fields via reflection.

所以,在JSR 133 中,提出了Final Field Safe Context 的概念。这个名词的意思是,在这个context 中,可以保证不会发生reorder 的现象。但奇怪的是,现在JVM 的实现中,似乎没有为这个语义定义关键字。

 

说到final, 顺便说一下static final. static final 在初始化以后,几乎都不能改变。这里说“几乎” 的原因是有例外:

Static final fields may only be modified in the class initializer that defines them, with the exception
of the java.lang.System.in, java.lang.System.out, and java.lang.System.err static fields, which can be modified by the java.lang.System.setIn, java.lang.System.setOut, and java.lang.System.setErr methods.

另外,顺便说一下,对于有无static 修饰的final field, 其在JVM 内部的初始化函数是不同的。这个有兴趣的可以看一下后面的参考文档。

 

最后要说的是所谓值“不能改变”的意思。概括的说,对于primitive 类型的变量,一旦声明为final, 除了上面的情况,初始化后就不能改了。而对于对象类型的field, “不能改变”的是它的引用。要理解这句话的意思,请看一下这段代码:

package  com.dproxy;

import  java.util.ArrayList;

public   class  FinalChangeTest {
    
public   final   int  a  =   3 ;
    
    
public   final  Object b  =   null ;
    
    
public   final  String c  =   " abc " ;
    
    
public   final  StringBuffer d  =   new  StringBuffer( " a " );
    
    
public   final  ArrayList e  =   new  ArrayList();
    
    
public   final  TestClass f  =   new  TestClass();
    
    
public   final   int [] g  =   new   int [ 3 ];
    
    
public   static   void  main(String args[]){
        FinalChangeTest test 
=   new  FinalChangeTest ();
        
//         test.a = 4;  //  error
//         test.b = "";  // error
        
//         test.c = "";  // error
        
//         test.d = test.d.append("ttt");  // error
        test.d.setCharAt( 0 ' c ' );
        
        test.e.add(
"" );
    
//         System.out.println(test.f.i);  // 1
        test.f.i  =   2 ;
//         System.out.println(test.f.i);  // 2
        
        test.g[
0 =   3 ;
    }
    
    
class  TestClass{
        
public   int  i  =   1 ;
    }
    
    
public   void  finalVarTest(){
        
final   int  a  =   3 ;
        
        
final  Object b  =   null ;
        
        
final  String c  =   " abc " ;
        
        
final  StringBuffer d  =   new  StringBuffer( " a " );
        
        
final  ArrayList e  =   new  ArrayList();
        
        
final  TestClass f  =   new  TestClass();
        
        
final   int [] g  =   new   int [ 3 ];
        
//         a=2;     // error
//         b=""; // error
//         c=""; // error
        
        d.setCharAt(
0 ' c ' );
        
        e.add(
"" );
        
        f.i 
=   0 ;
        
        g[
1 =   2 ;
        
            
    }
}

上面代码里注释部分的句子都是无法通过编译的。对比这个,读者可以理解一下。

 

参考:

JSR133

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/2398.html

http://dev2dev.bea.com.cn/bbsdoc/20060704286.html

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值