Java中的final关键字该怎么用?

  • A+
所属分类:JAVA


共计 4478 个字符,预计需要花费 12 分钟才能阅读完成。

首先final从字面解释,表示最终的,最后的,java编程中通常指“这是无法改变的”;下面谈一谈final使用的3种场景:数据方法

数据被final关键字修饰的时候,就是告知编译器这一块数据是恒定不变的,我们称之为常量

tips:常量又有以下区分
1.编译时常量;
2.运行时常量(在运行时才会被初始化,而且你也不希望它在后续使用过程中被改变);

编译时常量,在编译时执计算式,相对地减轻了运行时的负担,这类常量必须是基本数据类型,必须在定义的时候进行赋值

import java.util.Random;

class Value {
    String id;

    public Value(String id) {
        System.out.println("Value " + id + " Constructor.");
        this.id = id;
    }

    @Override
    public String toString() {
        return "Value{" +
                "id='" + id + '\'' +
                '}';
    }
}

/**
 * @author plm
 * @create 2021/3/6 18:28
 */
public class FinalTest {
    private static Random random = new Random(47);
    // 编译时常量
    private final int valueOne = 1;
    private static final int VALUE_TWO = 2;
    public static final int VALUE_THREE = 3;
    // 运行时常量
    private final int valueFour = random.nextInt(20);
    private static final int VALUE_FIVE = random.nextInt(20);

    // 引用类型
    private Value v1 = new Value("v1");
    private final Value v2 = new Value("v2");
    private static final Value V_3 = new Value("v3");

    // 数组
    private final int[] arr1 = {1, 2, 3};

    @Override
    public String toString() {
        return "FinalTest{" +
                "valueFour=" + valueFour +
                "VALUE_FIVE=" + VALUE_FIVE +
                '}';
    }

    public static void main(String[] args) {
        FinalTest f1 = new FinalTest();
        //! f1.valueOne++; // Cannot assign a value to final variable 'valueOne
        f1.v2.id = "v2.0";
        f1.v1 = new Value("v1.0");

        //! f1.arr1 = new int[2]; // Cannot assign a value to final variable 'arr1'
        f1.arr1[1]++;

        //! f1.v2 = new Value("v2.1"); // Cannot assign a value to final variable 'v2'
        //! f1.V_3 = new Value("v3.0"); // Cannot assign a value to final variable 'V_3'

        System.out.println(f1);

        System.out.println("===============================");

        FinalTest f2 = new FinalTest();
        System.out.println(f1);
        System.out.println(f2);

    }
}

/* 输出
Value v3 Constructor.
Value v1 Constructor.
Value v2 Constructor.
Value v1.0 Constructor.
FinalTest{valueFour=15VALUE_FIVE=18}
===============================
Value v1 Constructor.
Value v2 Constructor.
FinalTest{valueFour=15VALUE_FIVE=18}
FinalTest{valueFour=13VALUE_FIVE=18}
 */
总结

编译期常量有valueOneVALUE_TWOVALUE_THREE,平常开发中常见的是后两者,尤其是变量名以全大写字母命名;
运行时常量有valueFourVALUE_FIVE;区别在于是否static修饰,静态只在类加载时初始化一次就不会再变,非静态每次创建对象都会初始化一次,对象不同就可能不同,同一个对象不会再变化!
引用类型常量有v1v2V_3和数组arr1,仅仅是引用不变(不可以在指向其他引用类型数据),但是该数据内部属性可做修改;

空白final

指的是某一属性被final修饰而没有给定初始值;

/**
 * @author plm
 * @create 2021/3/6 19:07
 */

class Pro {
    private int i;

    public Pro(int i) {
        this.i = i;
    }
}

public class FinalBlank {
    private final int i = 0;
    private final int j; // 如果没有初始化,编译器会提示:Variable 'j' might not have been initialized
    private final Pro pro;

    public FinalBlank() {
        j = 0;
        pro = new Pro(2);
    }

    public FinalBlank(int j, Pro pro) {
        this.j = j;
        this.pro = pro;
    }

    public static void main(String[] args) {
        new FinalBlank();
        new FinalBlank(2, new Pro(2));
    }
}
总结

编译器会确保final修饰的数据在使用前被初始化,否则编译工具就会提示Variable ‘j’ might not have been initialized
空白final提供了很大的灵活性,可以做到因对象而异;

final方法入参

方法入参亦可使用final关键字修饰的,同样意味着你将无法在方法体中对其进行修改(基本数据类型无法修改值,引用数据类型无法修改引用);

final修饰方法,目前主要有两个原因:
1.把方法锁定,防止任何继承类的重写导致其含义被修改;
2.提高执行效率,在早期的Java版本(新版本虚拟机可以自行优化,不再需要单独指定final方法)中,遇到调用final方法,编译器就会将该调用转成内嵌调用

内嵌调用机制:将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中数据,处理返回值,并且以方法体中的实际代码的副本来代替方法调用,这将消除方法调用的开销!
/**
 * @author plm
 * @create 2021/3/6 21:31
 */
class WithFinals {
    private final void f() {
        System.out.println("WithFinals.f()");
    }

    private void g() {
        System.out.println("WithFinals.g()");
    }
}

class OverridePrivate extends WithFinals {
    //! @Override // Method does not override method from its superclass
    private final void f() {
        System.out.println("OverridePrivate.f()");
    }

    //! @Override // Method does not override method from its superclass
    private void g() {
        System.out.println("OverridePrivate.g()");
    }
}

class OverridePrivate2 extends OverridePrivate {
    //! @Override // Method does not override method from its superclass
    public final void f() {
        System.out.println("OverridePrivate2.f()");
    }

    //! @Override // Method does not override method from its superclass
    public void g() {
        System.out.println("OverridePrivate2.g()");
    }
}

public class FinalOveride {
    public static void main(String[] args) {
        OverridePrivate2 overridePrivate2 = new OverridePrivate2();
        overridePrivate2.f();
        overridePrivate2.g();

        OverridePrivate overridePrivate = new OverridePrivate();
        /*
         * Ambiguous method call. Both
         * f() in OverridePrivate
         * and
         * f() in WithFinals match
         */
        //! overridePrivate.f();
        /*
         * Ambiguous method call. Both
         * g() in OverridePrivate
         * and
         * g() in WithFinals match
         */
        //! overridePrivate.g();
    }
}

/* 输出
OverridePrivate2.f()
OverridePrivate2.g()
 */

可以给private方法添加final修饰词,虽然这无意义(类中所有private方法都隐式的指定为final的);
如果你试图重写一个private方法(隐含final的),当你加了校验注解@Override就会发现,编译器报错Method does not override method from its superclass

如果将某个类指定为final,即表明你不希望该类被继承,该类也不需要做任何改动;如String

/**
 * @author plm
 * @create 2021/3/6 21:50
 */
final class FinalPapa {
    int i = 1;
    int j = 2;
    String s = "1";

    void f() {
        System.out.println("FinalPapa.f()");
    }
}

//! class FutureFinal extends FinalPapa {} // Cannot inherit from final 'FinalPapa'

final类中所有方法都隐式地被final修饰,无法重写它们;
也同样可以添加显示final修饰词,虽然这么做毫无意义;

final修饰词的使用,日常主要集中在变量修饰

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: