1. Final variable
(1) 只能在定义final变量的时候,或者在构造器中完成初始化
(2) 对于基本类型,一旦初始化完成了,就不能改变
(3) 对于引用类型,一旦初始化完成了,引用只能指向这个对象;引用不能变,但是对象中的状态能变
(4) 常量,用 public static final修饰的变量代表是常量。常量只能在定义的时候完成初始化,也就是说在class文件编译的时候完成初始化工作的;public表示在全局访问,static表示变量只有一份,final表示一旦初始化后值不能改变
(5) 对于field用public final定义的时候,只能在定义的时候,或者在构造器中初始化,
一旦初始化完成后就不能改变了。
例如:
package finalkeyword;
public class FinalVariable {
//常量
public static final int f1 = 2;
//final reference
private final Value v;
//
private final int i;
public FinalVariable(int i,Value v) {
this.i = i;
this.v = v;
}
public static void main(String[]args){
Value v1 = new Value(1);
FinalVariable f= new FinalVariable(2,v1);
//一旦构造器完成对final基本类型i完成初始化,在其他地方就不能改变了
//f.i = 9;
System.out.println(v1.getV1());
v1.setV1(66);
//final 引用指向的对象状态可以被改变
System.out.println(v1.getV1());
//常量不能动态改变,在class文件编译的时候值已经有了
//f.f1 = 3;
}
}
2. Final作为方法的参数
Final variable作为方法的参数,表示方法参数不能再方法中重新赋值
例如:
public void method1(final int j){
//不能给参数重新赋值
j = 2;
}
3. Final fields在聚集和组合中的运用
(1)聚集
聚集表示了对象之间有一种整体和部分的关系,整体不能脱离部分。具体上来说整体对象和部分对象的生命周期是一样的。不能有整体,而没部分。比如说Car类的对象,一定有Engine对象,它们之间的关系是整体与部分的关系。
Engine类的对象可以在定义的时候,就完成初始化工作;或者必须在Car的构造器中完成对Engine类初始化工作。
所以这时候用final关键字来修饰Engine fields就比较合适了(因为final fields能保证在定义的时候完成初始化,或者在构造器中完成初始化)。
public class Car {
private final Engine engine;
public Car() {
engine = new Engine();
}
}
(2)组合
组合是一种弱关联的关系,对象之间的关系在构造器中建立关联,也可以通过方法参数的形式建立关联。
(3)Final fields的局限性
Final 引用如果指向的是引用类型,表示这个引用只能只向这个对象,不能指向其他对象;但是引用指向的对象的状态还是可以改变的。如果,我们想构建一个线程安全的不可变类,我们必须保证final引用指向的数组或者可变对象不能从我们的类中逸出。
例如:
package finalkeyword;
/**
*
* @author Administrator
* 试图构建不可变对象,但是因为没有正确发布states
* 导致线程可以去改变states中的元素
*/
public class DangerousStates {
private final String[] states = {"hua","zhang","liu"};
//没有正确发布states,导致states数组中的元素可以被改变。
public String[] getStates() {
return states;
}
public void printStates() {
for(String s : states)
System.out.println(s);
}
public static void main(String[] args){
DangerousStates d = new DangerousStates();
String[] s1 = d.getStates();
//可以改变states[0]指向的字符串。
s1[0] = "dd";
d.printStates();
}
}
我们可以克隆数组对象或者用一个不可修改的list来修正这类错误。
package finalkeyword;
import java.util.AbstractList;
import java.util.List;
public class SafeStates {
private final String[] states = {"hua","zhang","liu"};
//将数组转为list
private final List<String> aList = new AbstractList<String>(){
@Override
public String get(int index) {
return states[index];
}
@Override
public int size() {
// TODO Auto-generatedmethod stub
return states.length;
}
};
public List<String> getStateList() {
return aList;
}
public void printStates() {
for(String s : states)
System.out.println(s);
}
//1.用clone方法修正,不正确发布states
public String[] getStates1() {
return states.clone();
}
public static void main(String[] args){
SafeStates s = new SafeStates();
String[] s1 = s.getStates1();
s1[0] = "dd";
s.printStates();
List<String> l = s.getStateList();
l.add("d");
System.out.println(l);
}
}
4. Final Method
Final修饰在method上,那么子类就不能override这个方法。
(1)Final method在模版模式中的使用
publicabstract class AbstractBase
{
publicfinal void performOperation() // cannot be overridden
{
prepareForOperation();
doPerformOperation();
}
protectedabstract void doPerformOperation(); // must override
}
模版模式允许基类把部分的算法委托给子类去实现。定义一个final的方法,同时定义一个abstract的方法,定义了基类和子类之间的一种规则,明确的告诉子类去到底哪个方法需要实现。