2007-01-08 | java的final关键字【转】
上一篇 / 下一篇 2007-04-27 18:07:49 / 个人分类:编程基础
由于语境(应用环境)不同,final关键字的含义可能会稍微产生一些差异。但它最一般的意思就是声明“这个东西不能改变”。之所以要禁止改变,可能是考虑到两方面的因素:设计或效率。由于这两个原因颇有些区别,所以也许会造成final关键字的误用。51Testing软件测试网:\ P'j^'N7F0W#@-FT
在接下去的小节里,我们将讨论final关键字的三种应用场合:数据、方法以及类。51Testing软件测试网+V/e@A2PpX
一 final数据51Testing软件测试网$RR.X+VnE
pY
许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:
r0fX7fH9w,\MW0(1) 编译期常数,它永远不会改变
Tui!x lY0(2) 在运行期初始化的一个值,我们不希望它发生变化
(FM/s*s
X9R`#n`5X0对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程里。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在Java中,这些形式的常数必须属于基本数据类型(Primitives),而且要用final关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值。
-S)i"FQRyy
eC&H0无论static还是final字段,都只能存储一个数据,而且不得改变。51Testing软件测试网O#b_Pk_'emN
若随同对象句柄使用final,而不是基本数据类型,它的含义就稍微让人有点儿迷糊了。对于基本数据类型,final会将值变成一个常数;但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有“常数”效果)。这一限制也适用于数组,它也属于对象。51Testing软件测试网
s,o7`aX,t,Q
下面是演示final字段用法的一个例子:51Testing软件测试网Dbp?(ECO9W?
I-hWn2e(@G9E0//: FinalData.java
s:T%M9b7nza2c|f0// The effect of final on fields51Testing软件测试网0sd%Mb,V^b~U
"CK7OZ;T%`V0class Value {51Testing软件测试网:Dj,Vaj6H` Bi
int i = 1;51Testing软件测试网V|WR2E$yx*w
}51Testing软件测试网&w.n/XxXo6`E2g
public class FinalData {51Testing软件测试网7rv qN {;l
// Can be compile-time constants51Testing软件测试网9J&\w_$^iRVT(B
final int i1 = 9;
Siy]~*N_bA0 static final int I2 = 99;51Testing软件测试网-C5ai-CO1H+\V
// Typical public constant:51Testing软件测试网E7jW2\+~s*r ^
public static final int I3 = 39;
L0b.J,Z$m0 // Cannot be compile-time constants:
ATG)a e,_5G%hj*M0 final int i4 = (int)(Math.random()*20);
s'{
A2E }0 static final int i5 = (int)(Math.random()*20);51Testing软件测试网9K1L1Gh^?V
51Testing软件测试网F#QOxxB2~|
Value v1 = new Value();51Testing软件测试网&PBwWO$Bl
final Value v2 = new Value();51Testing软件测试网t,M5EW*w5[
static final Value v3 = new Value();
_c;y7[%h^wE0 //! final Value v4; // Pre-Java 1.1 Error:51Testing软件测试网 M:m%M8yb-}j%wS
// no initializer51Testing软件测试网/C8Npm6D/~(F
// Arrays:
&m.C~-}~'i_T0 final int[] a = { 1, 2, 3, 4, 5, 6 };
DL8KXQ]3E'd0 public void print(String id) {
1bc6v^[-L6R0 System.out.println(
m5}:h{`#Fk,U0 id + ": " + "i4 = " + i4 +
0I$lu-d2f2^Bl!I0 ", i5 = " + i5);51Testing软件测试网{t0W1wk:u
T
}
!gq']+xQ?|5h1N-Qn0 public static void main(String[] args) {
xp]ee~0 FinalData fd1 = new FinalData();
A3@Q'i dC:L0 //! fd1.i1++; // Error: can't change value
la!X$qq0 fd1.v2.i++; // Object isn't constant!51Testing软件测试网'u2t*q#mf
fd1.v1 = new Value(); // OK -- not final51Testing软件测试网%r6h%_
{(@ rv5u
for(int i = 0; i < fd1.a.length; i++)
3}!v)[6R;a5O0 fd1.a[i]++; // Object isn't constant!
J9`vZ9`0 //! fd1.v2 = new Value(); // Error: Can't
b$?v"\9]&^vK0 //! fd1.v3 = new Value(); // change handle51Testing软件测试网]Gn
Qc$LI
//! fd1.a = new int[3];51Testing软件测试网A7ho9\:|*D*st'LBe
0o1r+C&T-E0 fd1.print("fd1");51Testing软件测试网 l+~T.ZZ+_#q V
W$n
System.out.println("Creating new FinalData");51Testing软件测试网?ZFE0aQ
FinalData fd2 = new FinalData();51Testing软件测试网&rp"W#R k d&f Q3B5l
B\R
fd1.print("fd1");
V
J1o$yj?0 fd2.print("fd2");
+uT4\k%},p
B#a0 }51Testing软件测试网Eh6HS-Jvq0D
} ///:~51Testing软件测试网9t$me/cLu(V
G
由于i1和I2都是具有final属性的基本数据类型,并含有编译期的值,所以它们除了能作为编译期的常数使用外,在任何导入方式中也不会出现任何不同。I3是我们体验此类常数定义时更典型的一种方式:public表示它们可在包外使用;Static强调它们只有一个;而final表明它是一个常数。注意对于含有固定初始化值(即编译期常数)的fianl static基本数据类型,它们的名字根据规则要全部采用大写。也要注意i5在编译期间是未知的,所以它没有大写。
z@4r?2j*J2A }0不能由于某样东西的属性是final,就认定它的值能在编译时期知道。i4和i5向大家证明了这一点。它们在运行期间使用随机生成的数字。例子的这一部分也向大家揭示出将final值设为static和非static之间的差异。只有当值在运行期间初始化的前提下,这种差异才会揭示出来。因为编译期间的值被编译器认为是相同的。这种差异可从输出结果中看出:51Testing软件测试网_
eg'W*`
mb E
Ei4|Du1V0fd1: i4 = 15, i5 = 9
C%|Vi au]WLj0Creating new FinalData51Testing软件测试网A)B0~7e OFA/z9|
fd1: i4 = 15, i5 = 951Testing软件测试网/\;U:b[;]6{
fd2: i4 = 10, i5 = 951Testing软件测试网6US4["P
D Fc!j.R,l
2KQ2h]8z m v&Hc[0注意对于fd1和fd2来说,i4的值是唯一的,但i5的值不会由于创建了另一个FinalData对象而发生改变。那是因为它的属性是static,而且在载入时初始化,而非每创建一个对象时初始化。
-W'S
iZ
F*B4x ^1Z0从v1到v4的变量向我们揭示出final句柄的含义。正如大家在main()中看到的那样,并不能认为由于v2属于final,所以就不能再改变它的值。然而,我们确实不能再将v2绑定到一个新对象,因为它的属性是final。这便是final对于一个句柄的确切含义。我们会发现同样的含义亦适用于数组,后者只不过是另一种类型的句柄而已。将句柄变成final看起来似乎不如将基本数据类型变成final那么有用。51Testing软件测试网}BGMC*Nt6B
2. 空白final
$D2x.bw/y-J/w(V(h0Java 1.1允许我们创建“空白final”,它们属于一些特殊的字段。尽管被声明成final,但却未得到一个初始值。无论在哪种情况下,空白final都必须在实际使用前得到正确的初始化。而且编译器会主动保证这一规定得以贯彻。然而,对于final关键字的各种应用,空白final具有最大的灵活性。举个例子来说,位于类内部的一个final字段现在对每个对象都可以有所不同,同时依然保持其“不变”的本质。下面列出一个例子:51Testing软件测试网'dX+T7PU,`
,mk,IB8@z w"ZO-f#m0//: BlankFinal.java
M.X*E/QF/B;FfU0// "Blank" final data members51Testing软件测试网 ^He1~\4|1L
`x
uJY1c~&i0class Poppet { }51Testing软件测试网ro] Kbl
t._:u]zwA9q0class BlankFinal {
.n%@Ix
B(a
K&px0 final int i = 0; // Initialized final51Testing软件测试网z)Az5u} E
final int j; // Blank final51Testing软件测试网d
n!N6Qz
final Poppet p; // Blank final handle51Testing软件测试网-K:o@r5Q.Sg6tEe[E
// Blank finals MUST be initialized
PWfc4m9c0 // in the constructor:51Testing软件测试网b%d:R8Y4F3C_9U
BlankFinal() {51Testing软件测试网0v;J8aB4V3Fh
j = 1; // Initialize blank final51Testing软件测试网/UL&i|xoO&{u3L YZ5o
p = new Poppet();
l"d*UC
zc0Y&s0 }51Testing软件测试网w+~bW:QYY
BlankFinal(int x) {