在Java中,我们用到的关键字很多,其中之一就是final,final有哪些用法,每种用法有怎样的特性呢?在这一篇文章中,我们就将细数final的用法。
根据上下文环境,Java的关键字final的含义存在着细微的区别,但是通常它指的是“这是无法改变的。”不想做改变可能出于两种理由:设计和效率。在平常的使用过程中我们可能会对其误用。
一般而言,final分为以下几类用法:
- final数据
- 空白final
- final参数
- final方法
1.final数据
有时我们需要告知编译器一块数据是恒定不变的,这有时候是很有用的,比如在下列情况时:
1.一个永不改变的编译时常量
2.一个在运行时被初始化的值,而你不希望它被改变
对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另外一个对象。然而,对象其自身却是可以修改的。
对于一个既是static又是final的域只占据一段不能改变的存储空间
以下是对于一般类型final域和static final域的测试代码:
import java.util.Random;
class FinalData{
private static Random random=new Random();
private int valueOne=10;
private final int valueTwo=20;
private final int valueThree=random.nextInt(50);
static final int valueFour=random.nextInt(50);
public void setValueOne(int valueOne) {
this.valueOne = valueOne;
}
public void show(){
System.out.println("valueOne="+valueOne+" valueTwo="+valueTwo+" valueThree="+valueThree+" valueFour="+valueFour);
}
}
public class TestFinal {
public static void main(String[] args){
FinalData data1=new FinalData();
data1.show();
data1.setValueOne(22);
data1.show();
//在这里,由于其他数值都是final修饰,所以不可变,无法使用set方法对它们进行设置值
//在这里,由于使用了random随机数,下面将演示用final修饰和static final修饰的区别
FinalData data2=new FinalData();
data2.show();
data1.show();
}
}
测试结果如下所示:
valueOne=10 valueTwo=20 valueThree=5 valueFour=19
valueOne=22 valueTwo=20 valueThree=5 valueFour=19
valueOne=10 valueTwo=20 valueThree=0 valueFour=19
valueOne=22 valueTwo=20 valueThree=5 valueFour=19
从结果我们能够知道对于 “final” 基本类型,它是不可变的,然而对于重写 “new” 的时候,可以改变,但是如果是使用的是 “static final” ,那么它的地址数据块都不变,即使用 “new” 都没有改变。
那么对于 “final” 的数据引用呢?下面我们也会通过代码注释方式讲解。
代码如下:
class FinalObject{
int i;
void FinalObject(int i){
this.i=i;
}
public void show(String s) {
System.out.println(s+i);
}
}
public class TestFinalObject {
public static void main(String[] args){
final FinalObject finalObject=new FinalObject();
finalObject.FinalObject(10);
finalObject.show("finalObject 输出为:");
FinalObject finalObject_0=new FinalObject();
finalObject_0.FinalObject(30);
finalObject_0.show("finalObject_0 输出为:");
FinalObject finalObject_1=new FinalObject();
finalObject_1.FinalObject(20);
finalObject_1.show("finalObject_1 输出为:");
//finalObject=finalObject_1; finalObject使用了final关键字,无法让它指向另一个对象
finalObject_0=finalObject_1;
finalObject_0.show("finalObject_0指向finalObject_1后的输出为:");
}
}
测试输出结果为:
finalObject 输出为:10
finalObject_0 输出为:30
finalObject_1 输出为:20
finalObject_0指向finalObject_1后的输出为:20
我们能发现,对于 “final” 的对象引用,我们不能改变它的原有引用。
2.空白final
在Java中,允许生成“空白 “final” ,所谓空白final是指被声明为 “final” 但又未给定初值的域。无论什么情况,编译器都确保空白 “final” 在使用前必须被初始化。
“空白final” 在关键字 “final” 的使用上提供了更大的灵活性。
下面是代码示例:
class Poppet{
private int i;
Poppet(int i1){i=i1; }
public String toString(){
return " Poppet i="+i;
}
}
class BlankFinal{
private final int i=10;
private final int final_0;
private final Poppet poppet;
public BlankFinal(){
this.final_0 = 10;
poppet=new Poppet(10);
}
public BlankFinal(int final_0) {
this.final_0 = final_0;
poppet=new Poppet(final_0);
}
public void show(){
System.out.println("i="+i+" final_0="+final_0+" "+poppet);
}
}
public class TestBlankFinal {
public static void main(String[] args){
BlankFinal blankFinal=new BlankFinal();
blankFinal.show();
BlankFinal blankFinal_1=new BlankFinal(50);
blankFinal_1.show();
}
}
测试输出结果为:
i=10 final_0=10 Poppet i=10
i=10 final_0=50 Poppet i=50
3.final参数
在Java中允许在参数列表中以声明的方式将参数指明为 “final” 。这意味着你无法在方法中更改参数引用所指向的对象。
具体代码测试如下:
class First{
public void show(){
System.out.println("first class!");
}
}
public class TestFinalParam {
First g;
void with(final First first){
//first=new First(); 报错,不能对传入的first进行更改
g=first;//这种允许,这个操作没有对first进行更改
}
int f(final int i){
//i++; final修饰的值不能改变
return i+1;//在这里没有修改i的值,所以被允许
}
public static void main(String[] args){
TestFinalParam testFinalParam=new TestFinalParam();
First first=new First();
System.out.println(testFinalParam.f(36));
testFinalParam.with(first);
testFinalParam.g.show();
}
}
代码测试结果为:
37
first class!
对于 “final” 参数,你可以读,但是不能修改它。
4.final方法
一般使用 “final” 方法的原因有两个:
1.把方法锁定,以防任何继承类修改它的含义
2.是由于效率,在过去,使用final能在一定程度上提高效率,但是现在都不那么做了
对于 “final” 和上面讲的其他 “final” 基本类似,所以这里不再进行代码示例,如果有意向,可以自己去试试。