final可以修饰类,方法,变量和参数。

final类

被final修饰的类,就是final类,表示此类不能被继承。如果我们设计的类,想禁止被继承。则可以使用final关键字进行强制指出。这样,如果有人想继承final类,则编译器会给出编译错误。

final class TestFinal{
}

//compile-time error : SubClass cannot inherit from final class
class SubClass extends TestFinal{
}

在java中有很多类都是被设计成final类,表示禁止用户进行扩展。如字符串操作的String,StringBuilder类,装箱类Integer,Long等类。

在java中一个类是不允许同时被abstract和final这两个关键字修饰的。因为final表示类是最终的,是已经完全完成的,不能再被修改和继承的;而abstract表示类是残缺的,是不完整的,就是需要被子类继承和修改的。

final方法

被final修饰的方法,有两个优点。
1. 此方法不允许在子类被重写(Overriding)。
2. 效率。所有的final方法,在运行的时候,都会被内联(inline)优化,即用方法的代码,替换函数调用。

在inline的时候,java不会改变函数调用语义的,比如,对null对象调用方法,无论有没有inline,都会抛出NullPointerException异常。

class Parent{
    public final void f1(){
        System.out.println("This is parent class,f1 function.");
    }
}

class Child extends Parent{
    public void f1(){
    //compile-time error : Overridden method is final
    }
}

private方法和final类内的所有的方法,都是不可能被重写的,所以,默认都是final方法。

final变量

java中变量,大致分为有三种(个人理解,不一定正确)。局部变量(local variable),类成员变量(instance variable),类静态变量(class variable)。这三种变量都是可以被final关键字修饰的。
但这三者却又不太相同。

final修饰的类静态变量(class variable),可以在定义的地方赋值,也可以在静态初始化区进行赋值。

final修饰的类成员变量(instance variable),必须在定义的地方赋值,或者在构造函数结束前进行赋值。

final修饰的局部变量,可以在任何地方进行一次赋值,也可以不赋值。

所有被final修饰的变量,只能被赋值一次。如果final修饰的变量被赋值两次,则会产生编译期错误。

final变量与常量的区别

虽然,final变量,只能被赋值一次。但并不代表final变量的值,不会被改变。如果final变量是一个对象的引用。则final变量表示,则对象引用不能被修改。但仍然可以通过对象的操作,改变对象的内部值。典型的,则是Array对象。

public class FinalTest {
    static final String [] strArray = new String[3];

    public static void main( String[] args ) {
        System.out.print("Old array object:");
        for (String str:strArray){
            System.out.print(str + "  ");
        }
        System.out.println();
        strArray[0] = "String1";
        System.out.print("New array object:");
        for (String str:strArray){
            System.out.print(str+"  ");
        }
    }
}

上面代码的输出结果如下:

Old array object:null  null  null  
New array object:String1  null  null

从上面的结果,可以看到final变量字符串数组strArray内的字符串的值改变了。这是因为strArray对象没有改变,仍然是一个含三个字符串的数组对象。但数组的字符串引用是可变的。

如果final修饰的变量是内置数据类型(int,double)或者自动装箱类型,或者是String,则此变量是常量。因为此变量的引用和值都是不能改变的。

默认final变量

java中有三类变量是默认就是final变量,是只能被赋值一次的。虽然没有显式添加final关键字修饰,但
1. interface(接口)的静态变量;interface的所有变量都是public static final类型的
2. try-with-resources里定义的局部变量。try-with-resources是java7的新特性,可以自动回收资源。
3. multi-catch clause里定义的异常对象。
下面的代码,演示了,这三种

interface Parent{
    String str = "";
}

class Child implements Parent{
    public Child(){
        //interface的变量都是final
        //cannot assign a value to final variable
        str = "1";
        try {
            throw new SQLException();
        }catch (SQLException | NullPointerException e ){
            //multi-catch clause的变量是final的
            //cannot assign a value to final variable
            e = new NullPointerException();
        }

        try (BufferedReader br =
                     new BufferedReader(new FileReader(""))) {
            // try-with-resources里定义的局部变量是final的
            // cannot assign a value to final variable
            br = new BufferedReader(new FileReader(""));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}