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。