声明:本博客为原创博客,未经允许,不得转载!小伙伴们如果是在别的地方看到的话,建议还是来csdn上看吧(原文链接为http://blog.csdn.net/bettarwang/article/details/26744661),看代码和提问、讨论都更方便。
Java中final的作用主要表现在三方面:修饰变量、修饰方法和修饰类。下面就从这两个方面来讲解final的作用。在文末从final及类的设计安全性出发,论述了Java中String为何要被设计成不可变类。
1.final修饰变量
final修饰变量时与C++中的const作用类似,即如果是基本类型变量,则表示其值不能改变;如果是引用类型变量,则表示其一旦初始化,就不能再指向别的对象,但是注意它指向的对象本身的值可以改变(其实这一点也跟C++中的常指针很像)。看下面一个例子即知。
import java.util.*;
class Apple
{
private float weight;
public Apple(float weight)
{
setWeight(weight);
}
public void setWeight(float weight)
{
this.weight=weight;
}
public void print()
{
System.out.println("Weight is "+String.valueOf(weight));
}
public void printHashCode()
{
System.out.println("HashCode:"+String.valueOf(hashCode()));
}
}
public class FinalSample
{
public static void main(String[]args)
{
final int a=10;
//对于final修饰的基本类型变量,一旦初始化后就不能再被修改
//a=20;
final Apple apple=new Apple(300f);
apple.print();
apple.printHashCode();
//对于final修饰的引用类型变量,只是其指向不能变,但是其指向的对象本身可以改变。
apple.setWeight(350f);
apple.print();
apple.printHashCode();
}
}
输出结果如下图所示:

显然,final修饰的基本类型变量a是不能被改变;由输出的哈希码不变可知apple始终指向同一个对象,但是它指向的这个对象的成员weight却发生了改变。
也正是由于final对于引用类型变量“只能保证指向不变,却不能保证指向的对象本身不变”,所以在构造类时要特别小心,否则很容易被黑客利用。看下面一个例子即知。
import java.util.*;
class AppleTag
{
private float weight;
private float size;
public AppleTag(float weight,float size)
{
setWeight(weight);
setSize(size);
}
public void setWeight(float weight)
{
this.weight=weight;
}
public float getWeight()
{
return weight;
}
public void setSize(float size)
{
this.size=size;
}
public float getSize()
{
return size;
}
}
public class Apple
{
private final AppleTag appleTag;
public Apple(AppleTag appleTag)
{
this.appleTag=appleTag;
}
public AppleTag getAppleTag()
{
return appleTag;
}
public void print()
{
System.out.println("Weight:"+String.valueOf(appleTag.getWeight())+
" Size:"+String.valueOf(appleTag.getSize()));
}
public static void main(String[]args)
{
AppleTag appleTag=new AppleTag(300f,10.3f);
Apple apple=new Apple(appleTag);
apple.print();
appleTag.setWeight(13000f);
apple.print();
AppleTag tag=apple.getAppleTag();
tag.setSize(100.3f);
apple.print();
}
}
输出结果如下图所示:

类的设计者本意是想使appleTag一旦被初始化即不被修改,并且刻意不提供set函数以防止其被篡改。但实际上类的使用者却可以从两个地方修改appleTag从而达到改变apple的目的,其中一个地方是利用构造器中的实参进行修改,另一个就是利用getAppleTag()函数的返回值对其进行修改。
显然,本例是由于类的设计不当而释放出过大的权限,使类的使用者将apple的重量和大小修改成了极不合理的值,从而得到错误的结果。那要如何避免这种错误呢?
很简单,就是让final变量与外界充分隔离开,如本例中使类的使用者能获取相应的值,但是无法获取appleTag,代码如下所示:
import java.util.*;
class AppleTag
{
private float weight;
private float size;
public AppleTag(float weight,float size)
{
setWeight(weight);
setSize(size);
}
public void setWeight(float weight)
{
this.weight=weight;
}
public float getWeight()
{
return weight;
}
public void setSize(float size)
{
this.size=size;
}
public float getSize()
{
return size;
}
}
public class Apple
{
private final AppleTag appleTag;
public Apple(AppleTag appleTag)
{
this.appleTag=new AppleTag(appleTag.getWeight(),appleTag.getSize());
}
public AppleTag getAppleTag()
{
return new AppleTag(appleTag.getWeight(),appleTag.getSize());
}
public void print()
{
System.out.println("Weight:"+String.valueOf(appleTag.getWeig