final关键字
final是最终的、最后的意思,在Java中,final作为一个修饰符,可以用来修饰:
- 类class
- 方法method
- 变量variable(包括局部变量、成员变量、静态成员变量)
当final修饰不同的结构时涵义是不同的。
final修饰类
final修饰类时,表示最终的类,即表示这个类不能被继承。
语法如下
[访问权限修饰符] final class 类名{
// 类体
}
final修饰类后,这个类就无法被继承了,但是该类的其他使用(比如创建对象、调用方法等)不会受到影响,还是一个普通的类。
什么情况下需要使用final修饰类
- 当认为一个类的设计已经足够完善,功能足够强大,不需要再让子类去扩展它了,这时出于安全考虑,可以将一个类设置为final,这样类中成员不会被继承,更不会被修改。
- 当一个类很重要、很关键,绝不希望被继承、被重写,那么也可以将其设置为final。
- 实际情况下,比较少见主动将一个类设为final,在考虑将一个类设置为final时要慎重。
- 常见的final修饰的类,都在JDK源码中。
final修饰方法
final修饰方法,表示最终的方法,即表示该方法无法被重写,但是仍然可以被继承。
语法如下
[访问权限修饰符] final 返回值类型 方法名(形参列表){
// 方法体
}
什么情况下需要使用final修饰方法
- 方法的设计已经很完善了,不需要子类进行修改,子类只需要继承使用父类的实现即可,就可以用final修饰这个方法。
- 一个方法一旦修改就容易出现问题,就可以使用final修饰 以保护这个方法不会被修改。
- 即使父类方法不是很完善,但是出于某些原因希望子类不要改写方法,就可以用final修饰这个方法。
- final修饰方法多见于JDK源码中。
不能被重写的方法
在《方法重写》中,已经确定不能被重写的方法有三种:
- 私有方法
- 构造方法
- 静态方法
现在添上第四种:
- final修饰的方法
final可否修饰私有方法、构造方法、静态方法
-
私有方法
可以但没必要,私有方法本身就不能被子类重写,加上final没意义,但是语法上可以加。
-
构造方法
构造方法不能用final修饰。
-
静态方法
可以但没必要,静态方法本身就不能被子类重写,加上final没意义,但是语法上可以加。
final修饰变量
final修饰变量,表示最终的变量,即表示常量。
首先明确:
-
常量的分类
-
字面值常量,指的是直接写在代码中的值
-
自定义常量,指的就是final修饰的变量
注:在final修饰静态成员变量表示静态成员常量(一个真正意义上的常量)时,要采取常量命名规范(全部大写,单词间用下划线分隔)。
-
-
final修饰变量后,仅表示这个变量的取值不再发生变化从而成为一个常量。其他的比如使用方式、生命周期等都不发生变化。
-
final修饰变量时的具体表现
-
基本数据类型
基本数据类型在栈上存储值,所以final修饰基本数据类型表示该基本数据类型变量的取值不再发生变化。
-
引用数据类型
引用数据类型在栈上存储引用,堆上存储对象,final修饰引用数据类型只能修饰引用,不能修饰对象。所以被final修饰的引用,表示引用中存储的地址成为一个常量,这个引用不能再指向新的对象了,但是引用所指向的对象的状态可以改变。
-
final修饰变量时可修饰如下三种
- 局部变量
- 成员变量
- 静态成员变量
接下来就具体说明final修饰这三种变量时的具体情况。
修饰局部变量
final修饰局部变量,表示局部常量,即表示它的取值不能再改变了。
方法中的局部位置:
- 方法体中
- 形参列表中
语法
final 数据类型 变量名 = 值;
解释
- 在方法体中的局部常量,只能在初始化时赋值,之后就不能再修改了。
- 在形参列表中的局部常量,不是意味着方法必须传入常量做实参,而是表示该参数一旦传入方法,就成为一个常量,在方法体中禁止修改它。
修饰成员变量
final修饰成员变量,表示成员常量。仍属于对象,必须创建对象才能访问。
语法
[访问权限修饰符] final 数据类型 成员常量名;
成员常量的取值
常量的取值一旦确定就不可更改,所以语法上要求final修饰的成员变量的取值不能依赖于默认初始化的默认值,除开默认初始化,成员变量的赋值方式还有显式赋值、构造代码块赋值。构造器赋值三种。
成员常量的取值必须在创建对象的过程中明确,以上的三种赋值方式必须选择一个也只能选择一个。需要注意的是,当选择构造器赋值时,要保证不论使用哪一个构造器,都能够给成员常量赋值。
注意
成员常量是由final修饰的成员变量,依然属于对象,创建不同的对象,成员常量的取值都可能不同,所以成员常量并不是一个真正意义上的常量。
如果需要真正意义上的常量,就需要使用final修饰静态成员变量表示静态成员常量,这是一个真正意义上的常量。
修饰静态成员变量
final修饰静态成员变量,表示静态成员常量。由于静态成员变量属于类,在类的全局唯一,所以静态成员常量在类的全局唯一且取值一旦确定就不变,是一个真正意义上的常量(全局常量)。
语法
[访问权限修饰符] final static 数据类型 全局常量名;
注:静态成员常量是一个“全局常量”,建议采用常量的命名规范。
静态成员常量的取值
全局常量的赋值同样不能依赖于默认初始化的默认值,那么赋值方式还有显式赋值和静态代码块赋值两种。
全局常量的取值必须在类加载过程中明确,以上两种赋值方式必须选一个且只能选一个。
注意
- 全局常量是真正意义上的常量,所以实际中全局常量的访问权限修饰符通常是public(语法上这不是必须的)。
final static
和static final
都可以使用。
访问全局常量触发类加载的一种特殊情况:
访问类的静态成员是一种触发类加载的时机,会触发类加载(类初始化),在类加载过程中静态代码块会被执行。
但是,访问类的全局常量(final修饰的静态成员常量)时,虽然全局常量也是static修饰的,然而在访问类的使用字面值常量赋值的全局常量时,不会触发类初始化,静态代码块不执行。
这是因为:这些字面值常量赋值的全局常量,相当于给字面值起名字,编译器会做特殊处理,将它们直接放入常量池。访问时,不需要进行类加载,因为它们本身和类也没有关系。这是类加载为了提升效率的一种机制。
但全局常量一旦不是用字面值赋值,比如用new对象的方式赋值的引用数据类型全局常量,依然会触发类初始化。