跳至主要內容

final关键字详解

fangzhipeng约 2147 字大约 7 分钟

final关键字的基本用法

final关键字,用于表示不可变,表示变量或者方法定义后不能被修改或者重写。final关键字可以用于修饰类、方法和变量。

  1. 修饰类:当类被声明为final时,表明该类不能被继承。它是一个最终类,不能有子类继承它。
public final class FinalTest {
}
  1. 修饰方法: 当方法被声明为final时,该方法不能被子类重写。子类只能继承父类中的final方法,但不能修改其实现。
public class FinalMethodDemo {

    public final void test(){
        System.out.println("test");
    }

    static class FinalMethodeDemo2 extends FinalMethodDemo{
    //编译不通过
        @Override
        public  void test(){
            System.out.println("test");
        }
    }
}

FinalMethodeDemo2是不能重新它的父类FinalMethodDemo的final方法的,编译的时候会报错。idea会提示错误,如下图所示:

image-20231125201006272
  1. 修饰变量: 当变量被声明为final时,该变量的值不能被修改,即它成为一个常量。一旦被赋值,就不能再更改。
  • 对于实例变量(成员变量),可以在代码块中赋值,也可以在构造函数中赋值,一旦赋值就不能被改变:
    private final String s1="s1";

//    {
//        s1="s1";  //在代码快中赋值
//    }

    private static final String s2;

    static {
        s2="s2";  //在静态代码快中赋值
    }

    private final String s3;

    //在构造函数中赋值
    public FinalTest(String s3) {
        this.s3 = s3;
    }

  • 对于被final修饰的局部变量,在使用前必须赋值。
    public void test2(){
        final String s;
        s="ss";
        System.out.println(s);//在使用final变量前必须赋值
    }
  • 如果被final修饰的变量时引用类型,则在变量初始化后,就不能再指向另一个对象了,但是变量的值是可以被修改的。示例代码如下:
public class FinalObject {


    public static void main(String[] args) {
        final int[] arrays=new int[]{1,2,3};
       // arrays=null; 非法

        final Person person=new Person("sam",1);
        person.setAge(2);
       // person=null; 非法

    }


    static class Person {

        private String Name;
        private int age;

        public Person(String name, int age) {
            Name = name;
            this.age = age;
        }

        public String getName() {
            return Name;
        }

        public void setName(String name) {
            Name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

final关键字的使用可以增加代码的可读性,提高代码的安全性,以及优化性能。它可以在合适的场景中使用,例如常量、线程安全等需要不可变性的情况。

为什么内部类引用的外部变量必须用final修饰

JDK1.8之后,如果将局部变量(或表达式)传递给匿名内部类或Lambda表达式作为捕捉的变量,它们会被隐式地视为final,是不可以被更改的。

为什么会这样呢?

public abstract class Task {
    public abstract void run();
}

public class FinalDemo {

    public void run1(final String taskname) {
        Task task = new Task() {
            @Override
            public void run() {
                System.out.println("taskname=" + taskname +" run");
            }
        };
        task.run();
    }

    public void run2(final String taskname) {
        new Task() {
            @Override
            public void run() {
                System.out.println("taskname=" + taskname+" run");
            }
        }.run();
    }

    public static void main(String[] args) {
        FinalDemo demo = new FinalDemo();
        demo.run1("task1");
        demo.run2("task2");
    }
}

首先,内部类和外部类其实是处于同一个级别,反编译中可以看到都是对象级别的类文件,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随外部类被销毁。

如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。

如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中(反编译class文件中可以看到),这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。

谈谈final、finally、finalize的区别

  1. final是Java中的修饰符,可以用来修饰类、方法和变量,表示不可改变的
  • 修饰类:当类被声明为final时,该类不能被继承,成为最终类。

  • 修饰方法:当方法被声明为final时,该方法不能被子类重写,成为最终方法。

  • 修饰变量:当变量被声明为final时,该变量的值一旦被赋值,不能被修改,成为常量。

  1. finallyfinally是Java中的关键字,用于异常处理的最后一个代码块,在try-catch语句中使用。无论是否发生异常,finally块中的代码都会被执行。
try {
    // 可能会发生异常的代码
} catch (Exception e) {
    // 异常处理逻辑
} finally {
    // 最终会执行的代码块
}

finally块通常用于释放资源、关闭连接等必须执行的操作,以确保代码的可靠性。即使在try块或catch块中出现return语句,finally块中的代码也会在方法返回之前执行。

  1. finalizefinalize是一个方法,它是在对象被垃圾回收器回收之前调用的。每个类都可以重写finalize方法来执行对象的清理操作。

    需要注意的是,finalize方法已经过时,不推荐使用了。因为垃圾回收器的工作是由Java虚拟机自动处理的,无法保证finalize方法的及时执行。更好的做法是使用try-finally或其他资源管理机制来确保资源的释放。

@Override
protected void finalize() throws Throwable {
    // 进行资源释放或清理操作
    super.finalize();
}

finally语句到底是在return之前还是之后执行?

在回答这个问题前,先来说一下finally语句是不会被执行的情况:

  • try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要条件是:相应的try语句一定被执行到。
  • 在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,finally语句也不会被执行到。

finally语句到底是在return之前还是之后执行?

  1. 当没有发生异常时:如果在try块中没有发生异常,finally语句将在try块执行完毕后立即执行。然后,程序将继续执行后续的代码。finally块中的代码将在return语句执行之后、方法返回之前执行。

请看下面的示例代码:

public class FinalReturn1 {

    public static void main(String[] args) {
        System.out.println(myMethod());
    }

    public static int myMethod() {
        int a = 1;
        try {
            return a;
        } finally {
            a = 2;  //idea提示:The value 2 assigned to 'a' is never used 
       
            System.out.println("Finally block executed.");
        }
    }
}

在这个示例中,无论try块中是否发生异常,finally块中的代码都会被执行。控制台会输出以下内容:

Finally block executed.
1
  1. 当发生异常时:如果在try块中发生了异常,会根据异常类型在catch块中进行匹配,然后执行相应的catch块。之后,无论是否有匹配的catch块,finally语句块都将执行。如果在catch中再次使用了return操作,则会覆盖try里面的return操作。
package io.github.forezp.javabasiclab.finaltest;

public class FinalReturn2 {

    public static void main(String[] args) {
        System.out.println(myMethod());
    }

    public static int myMethod() {
        int a=1;
        try {
            int result = 10 / 0; // 抛出异常
            return a;
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException caught.");
            a=2;
            return a;
        } finally {
            System.out.println("Finally block executed.");
        }
    }
}

控制台会输出以下内容:

ArithmeticException caught.
Finally block executed.
2
  1. 当发生异常时:如果在try块中发生了异常,会根据异常类型在catch块中进行匹配,然后执行相应的catch块。之后,无论是否有匹配的catch块,finally语句块都将执行。如果在finally中再次使用了return操作,则会覆盖之前try或者catch里面的return操作。
public class FinalReturn3 {
    public static void main(String[] args) {
        System.out.println(myMethod());
    }

    public static int myMethod() {
        int a=1;
        try {
            int result = 10 / 0; // 抛出异常
            return a;
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException caught.");
            a=2;
            return a;
        } finally {
            System.out.println("Finally block executed.");
            a=3;
            return 4;
        }
    }

}

控制台会输出以下内容:

ArithmeticException caught.
Finally block executed.
4

总结来说,finally语句在Java中用于定义一段无论是否发生异常都会执行的代码块。它在try块或catch块中的代码执行完毕后立即执行,并且在方法返回之前被执行。

  • trycatch块中使用return语句时,finally块中的代码也会在return语句执行后执行。(finally语句中没有使用return操作)
  • 如果在finally中再次使用了return操作,则会覆盖之前try或者catch里面的return操作。