Bootstrap

java之final关键字详解

final关键字表示最终的、不可改变的

可以修饰以下维度:

1、修饰类

目的:不能被其他类继承

  • 该类为最终类,不能被其他类继承

  • 该类中的所有方法都是最终方法(隐式的被final修饰)

如图:即使手动添加final修饰方法,IDEA也会提醒该final可以移除

在开发中,哪些类需要定义为final呢?

  • final修饰类的初衷是不能让其他类继承该类,实际开发中,我们一般遵循"低耦合,高内聚合"的原则

  • 如果一个类被其他类继承,那么当父类被修改时,可能会造成子类报错,从而子类也需要同步修改,该条继承链为"高耦合"

综上,对于经常需要修改的类(迭代更新会经常改动的类),可以使用final修饰

2、修饰方法

目的:子类不能重写(覆盖)该方法

如图所示:子类重写父类中的最终方法,会出现错误

哪些方法需要定义为最终方法?

对继承没有太大意义的方法,或者父类自身特有的方法,这些方法不需要给子类使用,从而定义为最终方法,在类继承时,可以降低耦合度

3、修饰局部变量

作用:final修饰的变量是不可改变的

优点:不需要重复的创建对象

这里的不可改变又分为2种:

  • 对于基本数据类型,指的是数值不可改变

  • 对于引用数据类型,指的是变量对应的地址不可改变

这里区别来源于java内存分配方式:

  1. 在java中,基本数据类型定义的值是存储在栈中的

  1. 引用数据类型定义的名称是存储在栈中的,而对应的实例(值),是存储在堆中

3.1、基本数据类型

如图:内存中的值不可改变

3.2、引用数据类型

注意:这里不能使用String来举例,String是一个特殊的存储机制

以User类为例

@Data
@AllArgsConstructor
public class User{
    String name;
    Integer age;
}

fina修饰User对象,地址更改的示例代码:

public static void main(String[] args) {
    //1、声明User最终的局部变量,并name=张三/age=18存储在堆中
    final User user = new User("张三", 18);
    System.out.println("堆中的值name发生改变前,user对象存储的结果为:" + user);
    //2、修改最终变量user中,地址指向的堆中的值name=李四
    user.setName("李四");
    System.out.println("堆中的值name发生改变后,user对象存储的结果为:" + user);
}

示意图如下:user对象内存地址是不可改变的,而实际的值可以一致修改,耗用内存比重复创建对象小

4、修饰全局变量(属性)

作用与上述局部变量一致

注意以下4点

4.1、final修饰全局变量,必须手动赋初始值

全局变量在没有final修饰的情况下,默认是存在初始值的,如下:

public class FinalStudy {
    /** 为了能在主方法中直接获取到全局变量,这里使用static修饰 */
    //定义一个基本数据类型的全局变量a
    static int a;
    //定义一个包装数据类型的全局变量b
    static Integer b;
    //定义一个引用类型的全局变量c
    static String c;

    public static void main(String[] args) {
        System.out.println("int的默认值为:" + a);
        System.out.println("Integer的默认值为:" + b);
        System.out.println("String的默认值为:" + c);
    }
}

结果为:

其实:引用数据类型的默认值都为null,而每一种基本数据类型都有其对应的默认值。

改造下上面的代码,当我们使用final修饰时,会报错,提示需要给初始值:

4.2、对于final修饰的全局变量,要么直接赋值,要么通过构造方法赋值

还是以上面的代码为例:

注意:由于构造方法为:与类同名,无返回类型且不允许static修饰的方法,从而需要去除上述的static修饰符

示例图如下:

4.3、对于未直接赋值的final类型的全局变量,所有的构造方法都必须对该变量进行赋值

案列1:

即:

我们也可以给无参构造放方法添加参数

;