面试题:JAVA中final关键字的作用
final关键字的功能概述
在Java中,关键字 final 的意思是终态,可以用于声明变量、方法和类,分别表示基本类型变量值不可变,引用类型变量引用地址不可变但值可变,方法不可被覆盖,类不可被继承。下面进行详细阐述。
1、用来修饰变量
当使用final修饰一个变量(属性)时,这个属性就成为一个常量。此时可以考虑赋值的位置有直接初始化、代码块中初始化和构造器中初始化。
final修饰局部变量
使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参以后,就只能在方法体内使用此形参,而不能重新赋值。形参是局部变量,其作用域是整个方法体。
public void localArg(final int arg){
arg = 20;//编译不通过,异常信息【Cannot assign a value to final variable 'arg'】
System.out.println(arg);
}
public void localArg(){
final int ARG = 10;//常量,基本类型变量
ARG += 20;//编译不通过
}
final关键字修饰成员变量
对于成员变量来说,一旦使用final关键字,也是不能再次改变。
和局部变量的不同点在于,成员变量需要有默认值,因此必须手动赋值。final修饰的成员变量赋值途径有两个,而且只能二者选择其一:①在定义的时候直接赋值,②在构造方法体里面赋值,此时必须保证所有重载的构造方法都对final修饰的成员变量赋值。
public User(Long id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
var = 91;
}
//定义时直接赋值
private final int a = 32;
//使用构造方法赋值
private final int var;
public User() {
var = 90;
}
final修饰基本类型变量
当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。上文中的int ARG就属于基本类型变量。
final修饰引用类型变量
当使用final修饰引用类型变量时,比如对象和数组等,不可变的含义就是地址值不能改变,但是该地址指向的对象的内容是可以改变的。它保存的仅仅是一个引用,final只能保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以改变。
这里引用一个网上常见的例子,以说明final修饰基本类型变量和引用类型变量的区别。
//final修饰基本类型变量和引用类型变量的区别
import java.util.Arrays;
class Person
{
private int age;
public Person(){}
// 有参数的构造器
public Person(int age) {
this.age = age;
}
public void setAge(int age){this.age = age;}
public int getAge(){return age;}
}
public class FinalReferenceTest {
public static void main(String[] args) {
//final修饰数组,iArr是一个引用变量
final int[] iArr = {5,6,12,19};
System.out.println(Arrays.toString(iArr));
//对数组元素排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//对数组元素赋值,合法
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//下面语句对iArr重新赋值,非法
//iArr = null;
//final修饰Person变量,p是一个引用变量
final Person p = new Person(45);
//改变Person对象的age实例变量,合法
p.setAge(23);
System.out.println(p.getAge());
//下面语句对p重新赋值,非法
//p = null;
}
}
综上所述,对于一个被final修饰的变量,如果是基本数据类型的变量,则其数值一旦被初始化便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是,可以改变其数值。
2.用来修饰方法
当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。例如Object类中的getClass()方法。使用IDEA,在User类中重写 Object类中的getClass()方法,则可以看到如下异常信息,提示方法无法被子类重写。
'getClass()' cannot override 'getClass()' in 'java.lang.Object'; overridden method is final
在java中,把一个类中的方法修饰为final,意味着两个功能,第一点是该方法不能被重载,第二点是允许编译器将所有对此方法的调用转化为inline(行内)调用的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高。第二点也同时提醒我们,要谨慎的将方法定义为final,因为如果方法很庞大,你的代码量就会因为inline而迅速膨胀,可能反而会影响效率。
3.用来修饰类
当用final修改类时,该类成为最终类,无法被继承。简称为“断子绝孙类”。
/***
* final用法3:修饰类,则该类成为最终类,无法被继承
*下面这行代码放开注释,就会抛出异常提示
*/
//class MyString extends String{}
比如常用的String类、System类、StringBuffer类,它们都是最终类。定义断子绝孙类的目的是提高安全性,增强程序的可读性。
老铁们, 因个人能力有限,难免有瑕疵,如果发现bug或者有更好的建议,那么请在文章下方留言!