final关键字示意的不可变的。上面探讨final关键字应用的三种场合:数据、办法以及类。

final数据
1、final属性

程序中常常须要用到一些“常数”。常数次要利用于两个方面:

编译期常数,永远不会扭转
在运行期初始化一个值,不心愿它产生扭转。

对于编译期的常数,计算能够在编译期间提前执行,能够将常数值间接用于程序中。Java中,这种常数必须是根本数据类型。前置关键字final申明。定义时必须提供一个值。

class Person {

final String name;  // name未初始化,编译出错

}

如果对对象句柄应用final,final会将句柄变成一个常数。进行申明时,必须将句柄初始化到一个具体的对象,而且不能将句柄指向另一个对象。

复制代码
class Person {

String name = "张三";

}

public class FinalDemo {

public static void main(String[] args) {    final Person p = new Person();    p = new Person();   // Error:无奈为最终变量p调配值}

}
复制代码
然而,对象自身是能够批改的。

复制代码
class Person {

String name = "张三";

}

public class FinalDemo {

public static void main(String[] args) {    final Person p = new Person();    p.name = "萧萧弈寒";}

}
复制代码
一个可能的后果:

name = fd1,i1 = 0, i2 = 6

name = fd2,i1 = 8, i2 = 6

i1,i2是在运行期间随机产生的数据。

2、空白final

Java1.1容许创立“空白final”,它们属于非凡字段。只管被申明为final,然而却未失去一个初始值。即便如此,空白final还是必须在应用之前失去初始化。 示例:

复制代码
class Person {}

public class FinalDemo {

final int i;final Person p;FinalDemo() {    i = 1;    p = new Person();}FinalDemo(int x) {    i = x;    p = new Person();}public static void main(String[] args) {    FinalDemo fd = new FinalDemo();}

}
复制代码
当初强行要求对final进行赋值解决,要么在定义字段时应用一个表达式,要么在每个构建器中。

3、用final润饰参数

查了一些材料,很多人都说用final润饰办法参数是避免参数在调用时被批改。集体认为这种说法其实有两种了解:一种是变量的理论值不会被批改,另一种是在办法外部不能被批改。无论是基本参数类型还是援用类型,前一种说法都是谬误的。因为Java是值传递。

复制代码
public class FinalDemo {

static void f(final int i) {    i++;    // 无奈为final变量赋值,编译谬误}public static void main(String[] args) {    int x = 10;    f(x);   // ①}

}
复制代码
①处调用的f办法只是将x的值赋给了i,实际上i和x是两个变量。

再看上面的例子:

复制代码
class Person {

String name = "张三";

}

public class FinalDemo {

public static void main(String[] args) {    final Person p = new Person();    changeName(p);    System.out.println(p.name);}static void changeName(final Person p) {    p.name = "萧萧弈寒";}

}
复制代码
【运行后果】:

萧萧弈寒

由此阐明,final并不能阻止changeName()办法扭转p的内容。接下来,咱们删除润饰参数的final,而后在changeName办法体内扭转p指向的实例:

复制代码
class Person {

String name = "张三";

}

public class FinalDemo {

public static void main(String[] args) {    final Person p = new Person();    p.name = "萧萧弈寒";    changeName(p);    System.out.println(p.name);}static void changeName(Person p) {    p = new Person();}

}
复制代码
【运行后果】:
changeName中的name:张三
萧萧弈寒

咱们能够看出,尽管办法体内的p指向了其余对象,然而对于main办法中的p并没有影响。起因还是Java是值传递的。具体的请参考Java值传递还是援用传递?

final办法

final办法次要有两个方面的作用:一种是避免任何继承类笼罩办法。若心愿一个办法的行为在继承期间放弃不变,不可被笼罩和改写,就能够采取这种做法。另一种是进步程序执行的效率。将一个办法设成final后,编译器就会疏忽为执行办法调用机制而采取的惯例代码插入方法(将自变量压入堆栈;跳至办法代码并执行它;跳回来;革除堆栈自变量;最初对返回值进行解决)。它会用办法主体内理论代码的一个副原本替换办法调用。这样能够防止办法调用时的零碎开销。若办法体太大,可能效率也得不到晋升。

复制代码
class Human {

public final void show() {    //...}

}
public class Man extends Human{

public void show() {}   //Cannot override the final method from Human

}
复制代码

类内所有的private办法都主动成为final。因为不能拜访一个private办法,所以它相对不会被笼罩。

final类

如果整个类都是final,就表明这个类不容许被继承。或者出于平安方面的理由,不心愿进行子类化。除此之外,或者还思考执行效率的问题,确保波及这个类各对象的所有口头都要尽可能地无效。

final class Human {
}

public class Man extends Human{ // The type Man cannot subclass the final class Human
}
留神:数据成员既能够是final,也能够不是。无论类是否被定义成final,利用于final的规定同样实用于数据成员。

将类定义成final后,后果只是禁止被继承。因为禁止了继承,所以一个final类中的所有办法都默认为final。