夯实Java基础(九)——final关键字

时间:2022-10-03 16:19:58

1、前言

Java语言中的final关键字,想必大家都不是很陌生,我们自己用的最多的应该是用来定义常量吧,那么今天我们就来了解final这个关键字的用法,这个关键字还是非常简单的。

final从字面意思是不可更改的,最终的意思,它可以用来修饰类、方法、变量(包括成员变量和局部变量)、参数。

2、final修饰类

final修饰类,表示该类不能再被继承了,如果一个类它的功能比较完整,不需要扩展功能,我们就可以使用final来修饰。我们最常用String、Integer、System等类就是用final修饰的,其不能再被其他类所继承。

夯实Java基础(九)——final关键字

用final修饰类如果被继承了那么在编译的时候就会报错。

3、修饰方法

final修饰方法,表示这个方法不能被子类方法重写。如果你认为一个方法的功能已经足够完整了,不需要再去子类中扩展的话,我们可以声明此方法为final。比如Object类中getClass()方法就是final修饰的。

夯实Java基础(九)——final关键字

注意:类中所有的private方法都自动成为final。由于不能访问一个private方法,所以它绝对不会被覆盖。

4、修饰变量

final修饰变量表示是常量,一般都和static一起使用,用来定义全局常量。final无论修饰的是成员变量还是局部变量,都必须进行显式初始化,如下。

 public class FinalTest {
     //显式初始化
     final int AA=11;
     final int BB;
     final int CC;
     final int DD;
     //构造代码块初始化
     {
         BB=22;
     }
     //构造方法初始化
     public FinalTest(){
         CC=33;
         DD=44;
     }

     public FinalTest(int CC, int DD) {
         this.CC = CC;
         this.DD = DD;
     }
 }

我们可以通过显式初始化、构造代码块初始化、构造方法初始化这三种方式对final修饰的变量进行初始化。

前面都是讲的final修饰基本数据类型的变量,如果final修饰引用变量会是怎么样,我们来看一下举例:

 public class FinalTest {

     public static void main(String[] args) {
         final AA aa = new AA();
         //这里在引用其他的对象就会报错Cannot assign a value to final variable 'aa'
         //aa=new AA();
         aa.i++;
         System.out.println(aa.i);
     }

 }

 class AA{
     int i;
 }

 //运行结果:1

可以看到,虽然我们将引用变量用final修饰了,但是该对象的变量 i 我们还是可以进行修改,而这个变量的引用不能再指向另一个对象了。表明final修饰的是引用变量,就不能再指向其他对象,但是如果该对象内的变量不是final修饰的,其变量的值是可以进行修改的。

5、修饰参数

final修饰参数准确的来说应该是修饰形参,表明该形参的值不能被修改。

 public class FinalTest {

     public static void main(String[] args) {
         FinalTest f=new FinalTest();
         f.show(5);
     }

     public void show(final int param){
         //编译报错:Cannot assign a value to final variable 'param'
         //param=10;
         System.out.println(param);
     }
 }

我在使用final修饰形参时,表明此形参是一个常量。当我们调用方法的时候,给形参进行赋值,一旦赋值以后,就只能在该方法使用,而且不能进行重新赋值操作,否则编译报错。如果形参是引用类型,则引用变量不能再指向其他对象,但是该对象的内容是可以改变的。

6、类的final变量和普通变量有什么区别?

参考链接:https://www.cnblogs.com/xiaoxi/p/6392154.html

当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。那么final变量和普通变量到底有何区别呢?下面请看一个例子:

 public class Test {
     public static void main(String[] args)  {
         String a = "hello2";
         final String b = "hello";
         String d = "hello";
         String c = b + 2;
         String e = d + 2;
         System.out.println((a == c));
         System.out.println((a == e));
     }
 }

输出结果:true、false
     大家可以先想一下这道题的输出结果。为什么第一个比较结果为true,而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的值。而对于变量d的访问却需要在运行时通过链接来进行。想必其中的区别大家应该明白了,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:

 public class Test {
     public static void main(String[] args)  {
         String a = "hello2";
         final String b = getHello();
         String c = b + 2;
         System.out.println((a == c)); 

     } 

     public static String getHello() {
         return "hello";
     }
 }

这段代码的输出结果为false。这里要注意一点就是:不要以为某些数据是final就可以在编译期知道其值,通过变量b我们就知道了,在这里是使用getHello()方法对其进行初始化,他要在运行期才能知道其值。