Skip to main content

java final 关键字

说到final关键字,搞过java的都不陌生。基本上每个码农对final的基本用法都清楚,但是经常看到一些奇怪的用法和理解,那今天就好好梳理下。

1.final关键字的基本用法
在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)

1.1修饰类
当final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。默认final类中的方法都是final,成员变量可以设为final也可以不是。

[java]
package com.learn.core.ch02;

public final class Country {

private String name;

public void display() {

}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Country(String name) {
this.name = name;
}
}

public class CountrySub extends Country{
//The type CountrySub cannot subclass the final class Country
}

[/java]

1.2 修饰方法
下面这段话摘自《Java编程思想》第四版第143页:

“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。

1.3 修饰变量(成员变量或者局部变量)
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。注意如果是引用类型的变量,只是说引用不能再变,但是引用所代表的对象还是可以改变该对象内在的行为。
成员变量:必须初始化或者在构造器中初始化

[java]
package com.learn.core.ch02;

public final class Country {

private String name;

public Country(String name) {
this.name = name;
}

public static void main(String[] args) {
final Country country = new Country("china");
country.display();

// update to us
country.setName("US");
country.display();

// output
// china
// US

// country = new Country("GE");
// error The final local variable country cannot be assigned. It must be blank and not using a compound
// assignment
}

public void display() {
System.out.println(name);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

[/java]

2. 深究final关键字
2.1 类的final变量和普通变量有什么区别?
当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了
2.2 final的引用变量指向的对象内容可变吗?
只要不改变引用,引用所指向的对象是可以改变内在行为或者外在行为的,就是可以调各种方法。可以参考上边的例子。
2.3 匿名内部类中使用的外部局部变量为什么只能是final变量?
关于这个,可以参考另外一篇文章。
2.4 static 和final一起使用,可以定义不可变的常量
2.5 所有的局部变量定义成final,除了匿名内部类可以使用的有点,还有其他优势吗?

[java]
public void display() {
System.out.println(name);
String title = "Hello";
Date date = new Date();
System.out.println(title + "," + date);
}
[/java]
001.png
局部变量

[java]
public void display() {
System.out.println(name);
final String title = "Hello";
final Date date = new Date();
System.out.println(title + "," + date);
}
[/java]

final局部变量

通过查看字节码,发现final修饰的局部变量在编译阶段就会被替换掉,有点类似C语言的宏定义,如果没有final修饰的局部变量,会在方法运行栈中多用2个指令(aload_)去存储和加载局部变量所指向的值

12: astore_1
13: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_1

所以final局部变量确实可以减少指令,提升程序的性能,但是我认为如果所有的局部变量都用final修饰,会影响代码的整洁。至于选择性能还是代码的可阅读性,要看自己的习惯了!

欢迎大家拍砖纠正!

参考资料
深入理解Java中的final关键字
浅析Java中的final关键字