一文搞懂Java中final修饰符作用,编译时常量和运行时常量区别

前言

本文主要介绍Java中final修饰符的作用,同时也会探究final修饰符和常量的关系,通过字节码进行探究,以及final修饰会带来那些好处以及优化。

一、final修饰符

        在Java中final修饰符可以修饰变量方法,被final修饰的方法不能被继承,如String类,被final修饰的方法不能被子类重写。而对于final修饰变量,可以分为final修饰基本类型变量和引用类型变量。

1.1 final的基本使用

对于final修饰的变量,不管是基本类型还是引用类型,要么显示的对其进行初始化(即在声明时就进行赋值),要么必须在构造器或构造代码块中进行初始化,也就说final修饰的变量必须进行初始化,否则编译不通过。如下所示:

声明变量时进行初始化---该方法在编译阶段就能确定a的值

构造块初始化---该初始化方式需要运行阶段才能确定a的值

构造方法初始化---该初始化方式需要运行阶段才能确定a的值

以上三种初始化方式只能同时选择一种,因为final修饰的变量初始化后就不能被修改了。如下对final修饰的变量进行重新赋值,编译就会报错。

1.2 final修饰基本数据类型

对于final修饰的基本数据类型的作用

1、不变性:一旦初始化,值就不能再改变。

2、编译时常量:如果在声明时就赋值,这些变量通常会被视为编译时常量,编译器会进行优化,将它们的值直接内联到使用它们的地方。

3、内联优化:在编译时直接替换为其值,生成的字节码中不再引用这些变量,而是直接使用它们的字面值。

1.3 final修饰引用类型数据

  • 初始化后引用不可变,不能指向其他对象。
  • 引用指向的对象本身可以是可变的(除非对象是不可变的)。
  • 引用变量的不可变性不影响对象的可变性。

可以看到,当我们在声明变量时对引用类型赋值,在构造器中又去重新赋值时就会报错,因此final修饰的引入类型的变量就不能再指向其他对象

但是和基本类型不同的时,虽然不能改变引用,但是我们可以改变该对象的值

再TestFinal类中属性student被修饰为final,因此我们不能将student引用指向其他对象,但是我们可以修改stundet引用对象中的属性。如果student中age属性也被final修饰,则此时就不能修改了。

二、编译时常量和运行时常量

编译时常量是指在编译期间其值就已确定的常量。这些常量必须满足以下条件:

  1. 使用 final 关键字声明。
  2. 值在声明时直接给定,必须是编译时可确定的字面量。
  3. 可以是基本数据类型(如 int、float)或字符串(String)。

示例1:

声明变量时进行赋值,但是不使用final修饰

public class TestName {
    private byte field1 = 1;
    public int add() {
        return field1 + 1;
    }
}
//字节码如下
0 aload_0
1 getfield #2 <com/aismy/bytecode/TestName.field1 : B>
4 iconst_1
5 iadd
6 ireturn

查看add方法字节码,可以看到是通过getfield指令去使用变量的,因为没有使用final修饰,因此编译期间并不能确定该变量的最终值,因为运行阶段可能会修改该值。

示例2:

声明变量是进行赋值,使用final修饰

public class TestName {
    private final byte field1 = 10;
    public int add() {
        return field1 + 1;
    }
}

//字节码
0 bipush 11
2 ireturn

此时可以看到,add方法的字节码中,直接就出现了11,即直接10+1,而不是通过getfield去通过变量引用去使用。此时filed就是编译时常量

示例3:

final修饰基本类型

public class TestName {
    private final byte field1;

    public int add() {
        return field1 + 1;
    }

    public TestName() {
        field1 = 111;
    }
}

//字节码
0 aload_0
1 getfield #1 <com/aismy/bytecode/TestName.field1 : B>
4 iconst_1
5 iadd
6 ireturn

该情况下,因为是在构造方法中对属性进行初始化的,构造方法是使用创建该类的对象时才会执行,也就是说再运行阶段才能对属性进行初始化,因此编译阶段不能确定属性的值,即便用final修饰了,也不是编译时常量,它是运行时常量。

通过以上分析,我们看到如果在当我们对final修饰基本类型变量,并为其赋值,编译阶段如果能确定值,就会进行编译时常量优化。可以提高执行效率。

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值