目录
2.1.1 基本类型(byte short int long float double boolean char)
2.2.2 final变量构造函数中初始化(空白final)
一、前言
Java语言中final是一个关键字(key words),其表示的三个含义:final修饰的变量是常量,final修饰的类不可继承,final修饰的方法不可重写。
final关键字三个特性:final修饰的变量为常量、final修饰的类不可继承、final修饰的方法不可重写,详见第二、三、四部分。
二、final修饰的变量为常量
2.1 基本类型与引用类型
对于final修饰的变量,应该对基本类型和引用类型分开讨论:
2.1.1 基本类型(byte short int long float double boolean char)
基本类型为栈内存(-128~127情况下),一旦被final关键字修饰,其变量属性变为只读,不再进行写操作,否则编译报错。
2.1.2 引用类型 (类、接口、数组)
引用类型为堆内存,一旦被final关键字修饰,则引用不再改变,但是引用所指向的对象是可以被修改的,Java语言并未提供任何方式使用对象恒定不变。示意代码如下:
public class FinalData {
private final Value value=new Value(22);
public static void main(String[] args) {
FinalData finalData=new FinalData();
//finalData.value=new Value(33); //编译报错 value为final类型
finalData.value.i++; // 编译通过 value所指向的对象可以修改
}
}
class Value{
int i;
public Value(int i){
this.i=i;
}
}
2.2 初始化时机
2.2.1 final变量定义时初始化
final变量定义时初始化常见,定义时直接赋值即可,不再赘言。
2.2.2 final变量构造函数中初始化(空白final)
因为被final修饰的变量一定要在使用前被初始化,所以如果没在定义操作,一定在构造函数中初始化,这种情况称为空白final。示例代码如下:
public class FinalData {
private final int i;//基本类型示意
private final Value value;//引用类型示意
public FinalData(){ // 无参构造函数 空白final
i=1;
value=new Value(1);
}
public FinalData(int x){//有参构造函数 空白final
i=x;
value=new Value(x);
}
public static void main(String[] args) {
new FinalData();
new FinalData(22);
}
}
class Value{
int i;
public Value(int i){
this.i=i;
}
}
三、final修饰的类不可继承
类使用final关键字修饰之后,该类不可被继承,可以起到保护该类的作用。实例代码如下:
final class FinalFatherClass{
}
class SonClass extends FinalFatherClass{ //编译报错 final类不允许继承
}
四、final修饰的方法不可重写
class FinalFatherClass{
final void f(){
System.out.println("FinalFatherClass f()");
}
}
class SonClass extends FinalFatherClass{
final void f(){//编译报错
// 子类中某一方法的方法签名(方法名+形参列表)与父类相同,即可认为子类中该方法重写父类方法
//因final方法不可被重写,顾此处编译报错
System.out.println("SonClass f()");
}
}
值得注意的是,子类虽不能重写父类final函数,但是可以继承父类final函数,示意代码如下:
public class TestFinal {
public static void main(String[] args){
FinalFatherClass son=new SonClass();
son.f(); //打印 FinalFatherClass f()
//因为子类未重写父类final函数,也不能重新父类final函数
//但子类可以继承父类final函数,所以调用父类 f() 打印 FinalFatherClass f()
}
}
class FinalFatherClass{
final void f(){
System.out.println("FinalFatherClass f()");
}
}
class SonClass extends FinalFatherClass{
}
从上面可以看到,final修饰的方法在特定的访问权限下可以被继承。
这是因为,方法是否被final修饰与方法的访问权限是两回事,没有任何关系,被final修饰的方法和未被final修饰的方法都可以自由的指定访问权限。
我们有兴趣去谈论final关键字和访问权限关键字在修饰方法的过程的起到的作用, 如下表(声明:final和访问权限是两个独立的概念,没有任何比较意义,所有本表仅方便读者理解,不可作为正统知识) :
protect关键字 | final关键字 | private关键字 | |
是否可以继承 | 是 | 是 | 否 |
是否可以重写 | 是 | 否 | 否 |
小结 | private<final<protect (仅适用于方法) |
从表中可以看到,protect修饰的方法在子类中可见,所有可以被子类继承,亦可以被子类重写;
final修饰的方法可以被子类继承,但不可被子类重写(这里final方法的访问权限为默认的default、protected、public均可,只要保证子类可见即可);
private修饰的方法由于仅本类可见,故不能被子类继承,亦不能被子类重写;
五、private与final对比
实际上,Java语言中,所有被private关键字修饰的方法都隐式的被final关键字修饰,(正如上表所示,private实现了final方法不可重写的功能,并附加不可继承功能。)(再次声明:final和访问权限是两个独立的概念,没有任何比较意义,所有上表仅方便读者理解,不可作为正统知识)可以对private方法添加final关键字,但是这种做法没有任何意义。代码示例如下:
public class TestFinal {
public static void main(String[] args){
FinalFatherClass fatherClass=new FinalFatherClass();
SonClass sonClass=new SonClass();
//fatherClass.f(); //f() 为final方法 被private修饰后 仅本类可见 所有不能到TestFinal类中调用
//sonClass.f();
//fatherClass.g(); //g() 虽然不是final方法 但被private修饰后 也是仅本类可见 所有不能到TestFinal类中调用
//sonClass.g();
//小结:java中,private方法已经隐式的包含final关键字 所以final关键字有无并不重要
}
}
class FinalFatherClass{
private final void f(){
System.out.println("FinalFatherClass f()");
}
private void g(){
System.out.println("FinalFatherClass g()");
}
}
class SonClass extends FinalFatherClass{
private final void f(){
System.out.println("SonClass f()");
}
private void g(){
System.out.println("SonClass g()");
}
}
六、小结
本文介绍final关键字,分别介绍了:
final关键字修饰变量,将变量变成常量,一般用来设置全局变量;
final关键字修饰类,将类变为不可以被继承的类;
final关键字修饰方法,将方法变为不可被重写的方法(但是可以被继承,只要是在该方法的访问控制权限允许的情况下);
最后讨论final关键字和访问控制权限的关系,对比了private和final,从某种意义上来说,final是介于protected和private之间的一种访问权限(虽然不准确,但是至少可以这样理解)。
天天打码,天天进步!