Java 中的 final 关键字

yufei       5 年, 6 月 前       1037

Java 中的 final 关键字可用于不同的上下文中。用于修饰一个类或一个变量或一个方法,而且仅仅只能修饰它们。final 关键字不是一个 访问修饰符,因此可以和访问修饰符一起使用。

下图,列出了 final 修饰符在修饰类、方法和变量时的不同作用。

最终变量

// 一个最终变量
final int THRESHOLD = 5;
// 声明一个最终变量
final int THRESHOLD;
// 一个静态的最终变量 PI
static final double PI = 3.141592653589793;
// 声明一个静态的最终变量 PI
static final double PI;

当一个变量添加了 final 修饰符时,就表示该变量的值是不可变更的。因为不能修改变量的值。所以,实际上就是常量。

因为变量一旦添加了 final 修饰符就不能修改值,所以,该变量的值必须在声明时同时初始化。

如果最终变量保存的值是一个引用 ( 比如对象 ),那么意味着我们不能重新给它赋值以引用另一个对象,但该引用变量指向的对象的内部状态可以更改。例如,我们可以添加或删除最终数组或最终集合中的元素。

最终变量的变量名一般以全部大写形式表示,如果包含多个单词,可以使用下划线 ( _ ) 分隔每个单词

最终变量的初始化

最终变量必须被正确的初始化,否则会在编译时抛出一个编译错误。

最终变量只能被初始化一次,要么在定义最终变量时使用赋值语句初始化,要么使用 实例化块 来初始化。

因为最终变量分为实例最终变量和静态最终变量,因此,最终变量的初始化方式也分为三种

  1. 定义时初始化 : 可以在声明最终变量时对其进行初始化。这种方式最常见。如果在声明时未初始化,则最终变量称为 「 空白最终变量 」。下面的两种方式可以对 空白最终变量 进行初始化操作。

  2. 初始化块 : 一个 空白最终变量 可以在 实例初始化块 中进行初始化,也可以在构造函数中进行初始化。

    如果一个类有多个构造函数,则必须在所有构造函数中初始化,否则将抛出编译时错误。但这种情况下,我们一般使用 实例初始化块

  3. 静态块 : 一个空白的静态的最终变量只能在 静态块 中进行初始化

范例

我们写一个范例来演示下最终变量的三种初始化方式。

JavaTester.java

public class JavaTester 
{ 
    // 最终变量在声明时同时初始化
    final int THRESHOLD = 5; 

    // 声明一个空白最终变量
    final int CAPACITY; 

    // 声明另一个空白最终变量 
    final int  MINIMUM; 

    // 静态最终变量在声明时同时初始化
    static final double PI = 3.141592653589793; 

    // 一个空白的静态的最终变量
    static final double EULERCONSTANT; 

    // 实例初始化块
    // 用于初始化空白最终变量 CAPACITY 
    { 
        CAPACITY = 25; 
    } 

    // 静态块
    // 用于初始化静态最终变量 EULERCONSTANT 
    static{ 
        EULERCONSTANT = 2.3; 
    } 

    // 构造函数
    // 在构造函数中初始化最终变量  MINIMUM 
    //
    // 注意: 如果有多个构造函数,则必须所有的构造函数中都必须对 MINIMUM 进行初始化
    public JavaTester()  
    { 
        MINIMUM = -1; 
    } 


    public static void main(String[] args) 
    { 
        JavaTester a = new JavaTester(); 
    } 
} 

什么时候使用最终变量 ?

普通变量和最终变量之间的唯一区别是: 普通变量可以多次的重复的赋值,但最终变量一旦赋值,就无法继续再变更。

因此,最终变量只能用于整个程序执行期间需要保持不变的值。

引用最终变量

当最终变量的值为一个对象或集合时。我们称这个最终变量为 「引用最终变量」。

例如,下面的代码定义了一个 StringBuffer 类型的最终变量

final StringBuffer sb

正如前面我们所阐述的,最终变量无法重新分配。但如果最终变量是一个引用最终变量,我们可以更改该引用变量指向的对象的内部状态。

注意: 这不是重新分配。 final 这个修饰符是 非传递性

我们写一段代码来演示下什么是对象内部状态。

public class JavaTester 
{ 
    public static void main(String[] args)  
    { 
        final StringBuilder sb = new StringBuilder("简单教程"); 

        System.out.println(sb); 
        sb.append("简单编程"); 

        System.out.println(sb); 
    }  
} 

编译运行上面的实例,输出结果如下

[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
简单教程
简单教程简单编程

这种 final 关键字的非传递性同样适用于数组,因为在 Java 语言中,数组也是一个对象

带有 final 关键字的数组也称为最终数组。

最终变量的几点注意事项

  1. 如果上面所阐述的,最终变量不能重新在分配值,否则会报编译错误。

    public class JavaTester 
    { 
        static final int CAPACITY = 4;
    
        public static void main(String args[]) 
        {
            CAPACITY = 5; 
        }  
    }
    

    编译运行这段代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    JavaTester.java:7: 错误: 无法为最终变量CAPACITY分配值
        CAPACITY = 5; 
        ^
    1 个错误
    
  2. 在类的方法/构造函数/块 中创建一个最终变量时,这个最终变量称之为 局部最终变量,它必须在创建它的地方初始化一次。

    例如下面的代码,可以正常运行

    public class JavaTester 
    { 
        static final int CAPACITY = 4;
    
        public static void main(String args[]) 
        {
            final int i; 
            i = 20;  
            System.out.println(i);  
        }  
    }
    

    编译运行以上代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    20
    
  3. 如果你同时会 C++,那么一定要注意 C++ 中的 const 变量和 Java final 变量之间的区别。

    C++ 中的 const 变量要求在声明时必须同时初始化。

    但 Java 中则不是必须的,因为可以先声明一个空白最终变量,然后稍后再初始化。

  4. Java for 循环中的最终变量。在 for 循环中,迭代的变量可以添加 final 修饰符。这是合法的。

    public class JavaTester 
    { 
        static final int CAPACITY = 4;
    
        public static void main(String args[]) 
        {
            int arr[] = {1, 2, 3};
    
            for (final int i : arr) 
                System.out.print(i + " ");
    
            System.out.println();
        }  
    }
    

    编译运行上面这段代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    1 2 3
    

    在上面这个 for 循环中,由于 i 变量在循环的每次迭代时超出范围,因此实际上每次迭代都重新声明,允许使用相同的标记(即 i )来表示多个变量。

最终类 ( final class )

当一个类添加了 final 修饰符后,该类就变成了 「 最终类 」 ( final class )。对于一个最终类,它的最大特性就是不能够被扩展,也不能被继承。

最终类一般有两个使用场景

  1. 阻止被继承。因为最终类不能够被继承。例如,所有包装类如 IntegerFloat 等都是最终类。我们无法扩展它们。

    final class A
    {
         // methods and fields
    }
    
    // 这个 B 类是不合.法的。
    class B extends A 
    { 
        // 产生一个编译错误 COMPILE-ERROR! Can't subclass A
    }
    
  2. 最终类的另一个用途是创建一个 不可变的类 ( immutable class )。例如预定义的类 String 。在 Java 语言中,如果不让一个类成为最终的,我们就无法使一个类成为不可变的。

最终方法 ( final method )

当给一个方法添加上 final 关键字时,那么这个方法就变成了 「 最终方法 」 。 一个最终方法最大的特征就是它不能被 重写 ( override )。就像 Object 类中的大多数方法一样。

如果我们要一个方法在所有的派生类中都有着同样的实现 ( 同一份拷贝 ),则必须使用 final 关键字来修饰该方法。

例如下面的代码,如果在子类中重写最终方法,则会抛出一个编译错误

class A 
{
    final void m1() 
    {
        System.out.println("This is a final method.");
    }
}

class B extends A 
{
    void m1()
    { 
        // COMPILE-ERROR! Can't override.
        System.out.println("Illegal!");
    }
}
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.