fianl 关键字
在 java 中被 final 修饰的域通常指的是“这是无法改变的。不想做改变的可能出于两种理由:设计或效率。
- final 关键字一般使用的三种情况:数据、方法、类
final 数据
许多编程语言都有某种方法来向编译器告知这一块数据时恒定不变的,比如:
一个永不改变的编译时常量
一个在运行时被初始化的值,而你并不喜欢它被改变
在java 中常量必须是基本类型,并且以关键字 final 修饰,对常量进行定义的时候必须进行赋值。
用于非基本类型对象引用时,fianl 使其引用恒定不变。但是对象自身却是可以修改的。
一个既是 static 又是 final 的域只占据一段不能改变的储存空间,一般用大写的表示并用下划线分隔各个单词。
class Value{
int i;
public Value(int i) {
this.i = i;
}
}
public class Test {
private static Random rand = new Random(47);
private String id;
public Test(String id) {
this.id = id;
}
// 编译时常数
private final int valueOne = 11;
private static final int VALUE_TWO = 22;
public static final int VALUE_THREE = 33; //典型的 public 常量
//不是编译时的常数,而是运行时的常数
private final int valueFour = rand.nextInt(20);
private static final int VALUE_FIVE = rand.nextInt(20);
private Value value1 = new Value(11);
private final Value value2 = new Value(22); //定义一个final对象
private static final Value VALUE_3 = new Value(33);
@Override
public String toString() {
return id + " : " +"valueFour = " +valueFour +", VALUE_FIVE = " + VALUE_FIVE;
}
public static void main(String[] args) {
Test test1 = new Test("test1");
//test1.valueOne++; error 其值不能改变
test1.value1 = new Value(9);
//test1.value2 = new Value(9); error final 使引用恒定不变
test1.value2.i++; // 对象不是常量,对象其自身却是可以修改的
Test test2 = new Test("test2");
System.out.println(test1);
System.out.println(test2);
}
}
输出为
test1 : valueFour = 15, VALUE_FIVE = 18
test2 : valueFour = 13, VALUE_FIVE = 18
由于 valueOne 和 VALUE_TWO 都是带有编译数值的 final 基本类型,所有他们二者可以用做编译期的常量,无重大区别。
VALUE_THREE 是一种更加电信的对常量进行定义的方式:定义为 public,则可以用于包之外;定义为 static,则可以
则强调只有一份;定义为 final,则说明是一个常量不能因为认为某数据时 fianl 的就认为在编译时可以知道其值。在上述代码中的 valueFour 和 VALUE_FIVE 再运行时使用随机的值来初始化。
value1、value2、VALUE_3 展示了 final 引用的意义。在 main() 方法中可以看到,不能因为 value2 是 final 的,就认为无法改变其值。
main() 方法最后展示了将 final 数值定义为静态和和非静态的区别。注意 VALUE_FIVE 两次输出的值是相同的,这是因为被 static 修饰的,在类第一次装载时已经被初始化,而不是每次创建对象时都初始化。
空白final
Java 允许生成 空白final 即被声明为 final 却又未被给定初值的域(必须在构造器中进行初始化,不然编译不通过),这样我们在使用上更灵活,从而可以做到根据对象而有所不同。无论什么情况,编译器都要确保 final 在使用前必须被初始化。
class A {
private int i;
public A(int i) {
this.i = i ;
}
}
class BlankFinal{
private final int i = 0; //已初始化的final
private final int j ; //空白final基本数据类型
private final A a; //空白final引用
public BlankFinal() {
this.j =1; //初始化空白final基本数据类型
this.a = new A(1); //初始化空白final引用
}
public BlankFinal(int j){
this.j = j;
this.a = new A(j);
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(5);
}
}
final参数
Java 允许在参数列表中以声明的方式将参数指定为final,表示在方法中不能对参数引用进行改变。
class B{
public void say() {
System.out.println("this class is B");
}
}
class FinalArguments{
void withFinal(final B b){
//b = new B(); 不能对final修饰的引用参数进行改变,编译出错
}
void withOutFinal(B b){
b = new B();// 这里就编译就不会出错
b.say();
}
int f(final int i){
//return i++; 不能对final修饰的基本数据类型参数进行改变,编译出错
return i+1;
}
public static void main(String[] args) {
FinalArguments fa = new FinalArguments();
fa.withFinal(new B());
fa.withOutFinal(new B());
fa.f(5);
}
}
final 方法
final 修饰方法一般用在防止子类去修改该方法。
- 类中所有的 private 方法都隐式地指定为 final 的。由于子类无法取用 private 方法,所以也就无法对其进行修改。可以为 private 方法声明 final,但是毫无意义。
final 类
将某个类整体定义为 final 时,表明当前类不再被任何类继承。出于安全性考虑,不希望该类有子类。
- 无法继承 fianl 类,但是可以使用组合的方式使用 final 类。
- final 类禁止继承,所以 fianl 类中所有的方法都被隐式指定为 final。可以在 final 类的方法中添加 final,但是毫无意义。
final class C{
public void say(){
System.out.println("this is class c");
}
}
//class Cc extends C{} 编译出错,fianl类不能被继承
class Cc{
public static void main(String[] args) {
C c = new C();
c.say();
}
}