一、final关键字的作用
- 修饰类:不能被继承,所有成员方法被隐式地被指定为final,成员属性可根据需要设置为final(一般不这样处理)
- 修饰变量:如果修饰的是基本数据类型,初始化后不能被重新赋值;如果修饰的是引用类型,初始化后不能指向其他对象。
- 修饰方法:不能被覆盖,防止继承类修改它的含义(通过内嵌调用提升效率在最新的java版本中不存在)
注:private方法会被隐式地被指定为final方法。
二、final关键字面试题
1.final变量和普通变量的区别
public class TestFinal {
public static void main(String[] args) {
String a = "hello2";
final String b = "hello";
String d = "hello";
String c = b + 2;
String e = d + 2;
//都是常量池的常量
System.out.println((b == d));
//通过final让编译器知道,都是编译时常量->常量折叠
System.out.println((a == c));
//运行时会进行拼接操作,创建新的对象
System.out.println((a == e));
}
}
三、为什么局部内部类和匿名内部类只能访问局部final变量?
例:
public class TestFinal3 {
public static void main(String[] args) {
String i ="hello";
Function function1 = new Function(){
@Override
public Object apply(Object o) {
//Error:(13, 24) java: 从内部类引用的本地变量必须是最终变量或实际上的最终变量
return i;
}
};
Function function2 = (a)->{
//Error:(13, 24) java: 从内部类引用的本地变量必须是最终变量或实际上的最终变量
return i;
};
//java8中不再修改不会报错,因此这里重新赋值
i = "b";
}
}
首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:
public class OuterClass {
public void display(final String name,String age){
class InnerClass{
void display(){
System.out.println(name);
}
}
}
}
从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:
public class OuterClass$InnerClass {
public InnerClass(String name,String age){
this.InnerClass$name = name;
this.InnerClass$age = age;
}
public void display(){
System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
}
}
所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。
在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
四、final关键字常见疑问
- 编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率?
答:不正确。如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。 - 当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量?
答:不正确。java为值传递。如果为基本数据类型,则为变量的拷贝,修改参数值不影响于原值;如果为引用类型,则为引用的拷贝,修改参数引用,不影响原变量的引用,但修改参数的内容,会影响原变量的内容。= - 局部变量用final修饰提高性能?
答:不正确。局部变量的final在编译后的字节码中不存在,只是用于编译时禁止重新赋值。(可能如果存在常量折叠可能有影响,但好的JVM(JIT)进行优化) - 成员变量用final修饰提高性能?
暂不明确,但是个人认为是有提升,线程拷贝到本地副本。
参考:
https://www.cnblogs.com/xiaorenwu702/p/5167997.html
https://www.cnblogs.com/dolphin0520/p/3736238.html