Java final关键字

99 阅读3分钟

final修饰符

类似于C#中的sealed修饰符,用于表示类、方法和变量的不可改变

1.可修饰范围

  • 变量
  • 方法

2.修饰变量

可以修饰

  • 成员变量(类变量和实例变量)
  • 局部变量
  • 形参

2.1 修饰成员变量

成员变量包括:

  • 类变量
  • 实例变量

成员变量随着类的初始化或对象的初始化而初始化的。

当类执行静态初始化块时可以对类变量赋初始值;当执行普通初始化块、构造器时对实例变量赋予初始值。

对于final修饰的成员变量而言,一旦有了初始值,就不能被重新赋值

也就是说,如果final修饰的成员变量没有在最开始定义成员变量时指定初始值,也没有再初始化块、构造器中指定初始值,那么该变量就会一直是系统默认分配的值,例如0\false\null等,那么该值将会没有意义。

所以Java语法规定:final修饰的成员变量,必须由程序员显式指定初始值。

final修饰的成员变量可以指定初始值的地方有:

  1. 类变量:
    • 静态初始块(static { } )
    • 声明该类变量时指定
  2. 实例变量:
    • 普通初始块
    • 声明该变量时指定
    • 构造函数中指定

注意:

  • final修饰的成员变量只能在一个地方指定,指定之后不能再次赋值

2.2 修饰局部变量

局部变量必须由程序员显式初始化,因此final修饰的局部变量可以在定义时指定初始值,也可以在之后指定(但是只能一次)。

2.3 修饰形参

final修饰形参后,在方法内就不能再对该参数进行赋值操作,具体的初始化是由系统根据传入的参数来完成的。

publi void test(final int a ){
    
    a= 5; // 报错,不能对final修饰的形参赋值
    
}

如果执行方法 test(5),那么a= 5;

2.4 修饰引用变类型与基本类型变量的区别

final修饰引用变量类型时,依然不能重新赋值,但是可以改变已经赋值的引用变量的成员变量。

例如:

final Person p = new Person(age:45);
p.setAge(12); // 可以改变person变量的age成员变量

// 此时报错,final修饰的引用变量不可重新赋值
p = null;

2.5 final与直接量

直接量:指在程序中通过源代码直接给出的值,也就是该变量在编译时就可确定下来,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。

满足以下三个条件,这个final变量就是一个直接量:

  • 使用final修饰
  • 定义该final变量时就指定了初始值
  • 该初始值可以在编译时就可以确定下来

举例:

  String s1 = "HelloWorld";
  String s2 = "Hello" + "World";
  s1==s2; // true
  
  String s3 = "Hello";
  String s4 = "World";
  String s5 = s3 +s4;
  
  s1==s5; // false
  

Java中的字符串有一个常量池的概念,Java会有常量池来管理曾经使用过的字符串直接量,编译器在编译期间就能知道 s2="HelloWorlad",所以会让s2直接指向常量池中的"HelloWorld"字符串,所以s1 和s2的指向相同,所以s1 == s2。

第二种情况,s5的值是由s3和s4进行连接运算后得到,因为s3和s4是两个普通变量,而不是一个直接量,因此编译器无法在编译期间确定s5的值,也就是说无法让s5指向常量池中的"HelloWorld",所以s1 == s5结果不成立。

所以,如果想要让s1==s5结果成立,只要将s3和s4变为直接量,即使用final修饰这两个变量即可。

3.修饰方法

final修饰方法主要是不希望子类重写此方法。

4.修饰类

final修饰的类不可以有子类(不可以被继承)。

  • final类中的所有方法都会被隐式指定为final方法