1. 被final修饰的变量不可以被改变
final修饰变量时,表示该变量一旦获得了初始值就不可被改变,即该final变量的值就不能被重新赋值。
1. final 修饰成员变量
final修饰的类成员变量
必须在声明时或静态代码块中初始化;
final修饰的实例成员变量
必须在声明时、非静态初始化块或构造器中初始化
final修饰的本地方法变量,必须使用前初始化,系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。
public class Test {
// 定义变量时赋初值
final int a = 6;
final String str;
final int c;
final static double d;
final char ch;
// 初始代码块,可以对实例Field赋予初值
{
str = "hello";
}
// 静态代码块,可以为类Field赋予初值
static {
d = 5.6;
}
// 构造方法,可以为实例Field赋予初值
public Test(){
c = 5;
}
// 普通方法中,不能为final成员变量赋初值
public void changeFinal(){
// ch = 'a';
}
// 普通方法中,不能为final局部变量赋初值
public void testFinal(){
final String s = "abc";
}
}
2. final修饰基本类型变量和引用类型变量的区别
当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
public class Main {
public static void main(String[] args) {
final int[] arr = {1,2,3,4};
// 对数组元素进行排序,合法
Arrays.sort(arr);
// 对数组元素进行赋值,合法
arr[2] = 5;
// 对arr重新赋值,非法
arr = null;
}
}
使用final修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。例如上面arr变量所引用的数组对象,final修饰后的arr变量不能被重新赋值,但arr所引用数组的数组元素可以被改变。
2. 被final修饰的方法不可以被重写
如果出于某些原因,不希望子类重写父类的某个方法,则可以使用final修饰该方法。
Java提供的Object类里就有一个final方法:getClass(),因为Java不希望任何类重写这个方法,所以使用final把这个方法密封起来。但对于该类提供的toString()和equals()方法,都允许子类重写,因此没有使用final修饰它们。
3. 被final修饰的类不可以被继承
当子类继承父类时,将可以访问到父类内部数据,并可通过重写父类方法来改变父类方法的实现细节,这可能导致一些不安全的因素。为了保证某个类不可被继承,则可以使用final修饰这个类。
4. 可执行“宏替换”的final变量
对一个final变量来说,不管它是类Field、实例Field,还是局部变量,只要该变量满足3个条件,这个final变量就不再是一个变量,而是相当于一个直接量:
(1) 使用final修饰符修饰;
(2) 在定义该final变量时指定了初始值;
(3) 该初始值可以在编译时就被确定下来。
public class Main {
public static void main(String[] args) {
// 定义了一个final局部变量,并在定义该final变量时指定初始值为5
final int a=5;
// 变量a其实根本不存在,当程序执行System.out.println(a);时,实际转换为执行System.out.println(5)。
System.out.println(a);
}
}
final修饰符的一个重要用途就是定义“宏变量”。当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
除了上面那种为final变量赋值时赋直接量的情况外,如果被赋的表达式只是基本的算术表达式或字符串连接运算,没有访问普通变量,调用方法,Java编译器同样会将这种final变量当成“宏变量”处理。
public class Main {
public static void main(String[] args) {
// 算术表达式
final int a = 1+2;
// 字符串连接运算
String b = "hello" + "99";
// 字符串连接运算中包含隐式类型(将数值转换为字符串)转换
String c = "hello" + 99;
// 编译器可以在编译时就确定a、b、c这3个变量的值,因此它们都是“宏变量”
System.out.println(b==c); // true
// 由于该变量的值需要调用String类的方法,因此编译器无法在编译时确定d的值,d不会被当成“宏变量”处理
String d = "hello" + String.valueOf(99);
System.out.println(b==d); // false
}
}
其实final关键字在jvm,并发编程,异常中都有一定的作用,这个我们后面再深入补充,这里就先不发散了。