【Java】final关键字

核心含义

final 译为 "最终的、不可改变的",在 Java 中它的核心作用是限制修改 ------ 被 final 修饰的元素(类、方法、变量)会失去 "可修改 / 可扩展" 的特性,变成 "不可变 / 不可扩展" 的状态。

简单来说,final 的核心就是 "锁死":

  • 修饰类:类不可被继承;
  • 修饰方法:方法不可被重写;
  • 修饰变量:变量的值 / 引用不可被修改。

具体用法

1. 修饰类:类不可被继承

定义与特点

  • final 修饰的类,称为 "最终类",不能被任何其他类继承(无法创建其子类)。
  • 设计目的:保证类的完整性和安全性,防止子类修改父类的核心逻辑。

代码示例

java 复制代码
// final 修饰类:String 是 Java 中典型的 final 类(源码中 public final class String)
final class FinalClass {
    public void sayHello() {
        System.out.println("我是 final 类的方法");
    }
}

// 错误:无法继承 final 类,编译报错
// class SubClass extends FinalClass {
//     // 子类试图扩展 final 类,不允许
// }

public class FinalDemo {
    public static void main(String[] args) {
        FinalClass fc = new FinalClass();
        fc.sayHello(); // 正常调用方法(final 类只是不能被继承,对象可以正常创建和使用)
    }
}

常见使用场景

  • 核心工具类:如 java.lang.Stringjava.lang.Integer(包装类)、java.lang.Math,这些类的逻辑是 Java 核心基础,不允许子类修改;
  • 安全类:涉及敏感逻辑(如加密、权限控制)的类,防止子类篡改核心逻辑。
2. 修饰方法:方法不可被重写

定义与特点

  • final 修饰的方法,称为 "最终方法",子类不能重写(Override) 这个方法(但可以重载)。
  • 设计目的:保护父类的核心方法逻辑不被子类修改,同时提升方法调用效率(JVM 可能对 final 方法做内联优化)。

代码示例

java 复制代码
class ParentClass {
    // final 修饰方法:不可被重写
    public final void finalMethod() {
        System.out.println("我是父类的 final 方法");
    }

    // 普通方法:可被重写
    public void normalMethod() {
        System.out.println("我是父类的普通方法");
    }
}

class ChildClass extends ParentClass {
    // 错误:无法重写 final 方法,编译报错
    // @Override
    // public void finalMethod() {
    //     System.out.println("子类试图重写 final 方法");
    // }

    // 正确:重写普通方法
    @Override
    public void normalMethod() {
        System.out.println("子类重写了普通方法");
    }

    // 正确:重载 final 方法(参数列表不同,不是重写)
    public final void finalMethod(int num) {
        System.out.println("子类重载了 final 方法,参数:" + num);
    }
}

public class FinalMethodDemo {
    public static void main(String[] args) {
        ChildClass child = new ChildClass();
        child.finalMethod(); // 调用父类的 final 方法,输出:我是父类的 final 方法
        child.finalMethod(100); // 调用子类重载的 final 方法,输出:子类重载了 final 方法,参数:100
        child.normalMethod(); // 调用子类重写的普通方法,输出:子类重写了普通方法
    }
}
关键区分:重写 vs 重载
  • 重写(Override) :子类方法与父类方法的「方法名、参数列表、返回值」完全一致 → final 方法禁止这种操作;
  • 重载(Overload) :子类 / 同一类中方法名相同,参数列表不同 → final 方法允许这种操作。
3. 修饰变量:变量不可被修改

这是 final 最常用的用法,也是最容易混淆的部分,需分「基本类型变量」和「引用类型变量」两种情况理解。

核心规则

代码示例:基本类型变量
  • final 修饰基本类型变量:值不可改
  • final 修饰的局部变量可以先声明后赋值(但只能赋值一次)
java 复制代码
public class FinalBasicVar {
    public static void main(String[] args) {
        // final 修饰基本类型变量:值不可改
        final int num = 10;
        System.out.println(num); // 输出:10

        // 错误:无法为最终变量 num 分配值,编译报错
        // num = 20;

        // final 修饰的局部变量可以先声明后赋值(但只能赋值一次)
        final double pi;
        pi = 3.1415926; // 第一次赋值,合法
        // pi = 3.14; // 错误:第二次赋值,编译报错
        System.out.println(pi); // 输出:3.1415926
    }
}
代码示例:引用类型变量
  • final 修饰引用类型变量:引用不可改,对象内容可改
java 复制代码
class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

public class FinalReferenceVar {
    public static void main(String[] args) {
        // final 修饰引用类型变量:引用不可改,对象内容可改
        final Person p = new Person("张三");
        System.out.println(p.getName()); // 输出:张三

        // 正确:修改对象的内容(引用地址没变)
        p.setName("李四");
        System.out.println(p.getName()); // 输出:李四

        // 错误:修改引用地址(指向新对象),编译报错
        // p = new Person("王五");

        // 同理:final 修饰数组(引用类型)
        final int[] arr = {1, 2, 3};
        arr[0] = 100; // 正确:修改数组内容
        System.out.println(arr[0]); // 输出:100
        // arr = new int[]{4,5,6}; // 错误:修改数组引用
    }
}
final 变量的赋值时机(必须赋值,且仅一次)

final 变量必须在「声明时 / 构造方法 / 初始化块」中完成赋值,否则编译报错,具体分为:

  1. 成员变量(类中声明)
    • 方式 1:声明时直接赋值(推荐):final int num = 10;
    • 方式 2:在构造代码块中赋值;
    • 方式 3:在构造方法中赋值(每个构造方法都要赋值,避免遗漏)。
  2. 局部变量(方法中声明):声明时可暂不赋值,但使用前必须赋值,且仅能赋值一次。

代码示例:成员变量的赋值时机

java 复制代码
public class FinalVarInit {
    // 方式1:声明时赋值(推荐)
    final int a = 10;

    // 方式2:构造代码块中赋值
    final int b;
    {
        b = 20;
    }

    // 方式3:构造方法中赋值(每个构造方法都要赋值)
    final int c;
    public FinalVarInit() {
        c = 30;
    }
    public FinalVarInit(int c) {
        this.c = c;
    }

    public static void main(String[] args) {
        FinalVarInit demo1 = new FinalVarInit();
        System.out.println(demo1.a + "," + demo1.b + "," + demo1.c); // 输出:10,20,30

        FinalVarInit demo2 = new FinalVarInit(40);
        System.out.println(demo2.a + "," + demo2.b + "," + demo2.c); // 输出:10,20,40
    }
}
静态 final 变量(常量)

**static + final**组合修饰的变量,称为 "静态常量"(也叫类常量),是 Java 中定义常量的标准方式:

  • 属于类,而非对象,所有对象共享;
  • 必须在声明时或静态代码块中赋值(不能在构造方法 / 构造代码块中赋值);
  • 命名规范:全大写,单词间用下划线分隔(如 public static final double PI = 3.1415926;)。

代码示例:静态常量

java 复制代码
public class FinalStaticVar {
    // 静态常量:声明时赋值(推荐)
    public static final String SCHOOL_NAME = "北京大学";

    // 静态常量:静态代码块中赋值(适合复杂初始化,如读取配置)
    public static final int MAX_NUM;
    static {
        MAX_NUM = 1000;
    }

    public static void main(String[] args) {
        // 直接通过类名访问静态常量
        System.out.println(FinalStaticVar.SCHOOL_NAME); // 输出:北京大学
        System.out.println(FinalStaticVar.MAX_NUM); // 输出:1000
    }
}

常见误区

  1. 误区 1 :final 修饰的引用变量,指向的对象内容也不可改。→ 错误:final 仅限制「引用地址」不可改,对象 / 数组的内容可以正常修改(如 p.setName("李四") 合法)。
  2. 误区 2:final 方法不能被重载。→ 错误:final 禁止的是「重写(Override)」,允许「重载(Overload)」(参数列表不同即可)。
  3. 误区 3:final 变量必须声明时赋值。→ 错误:成员变量可在构造代码块 / 构造方法中赋值,局部变量可在使用前赋值(但都仅能赋值一次)。
  4. 误区 4 :final 类中的方法自动是 final 的。→ 正确 ,但无需显式加 final:final 类不能被继承,其方法自然无法被重写,因此默认是 final 的(显式加 final 也可以,但没必要)。

实际应用场景

  1. 定义常量static final 组合,如 Math.PIInteger.MAX_VALUE
  2. 保护核心逻辑:修饰类 / 方法,防止子类篡改(如 String 类、父类的核心业务方法);
  3. 线程安全:final 变量赋值后不可改,多线程环境下无需担心被修改,提升线程安全性;
  4. 方法参数 :修饰方法参数,防止方法内修改参数值(如 public void method(final int num));
  5. 优化性能:JVM 对 final 方法 / 变量可能做优化(如内联 final 方法),提升执行效率。

总结

  1. final 的核心是「限制修改」:修饰类→不可继承,修饰方法→不可重写,修饰变量→值 / 引用不可改;
  2. 关键区分:final 基本类型变量「值不可改」,final 引用类型变量「引用不可改,内容可改」;
  3. 常用组合:static + final 定义静态常量(Java 常量的标准写法);
  4. 赋值规则:final 变量必须赋值且仅赋值一次,成员变量可在声明 / 构造代码块 / 构造方法中赋值,静态 final 变量仅能在声明 / 静态代码块中赋值。

掌握 final 的关键是理解 "不可改" 的具体范围 ------ 不同元素(类 / 方法 / 变量)的 "不可改" 含义不同,避免一概而论。

相关推荐
C雨后彩虹2 小时前
ConcurrentHashMap 核心锁机制:CAS+Synchronized 的协同工作原理
java·数据结构·哈希算法·集合·hashmap
柒许宁安2 小时前
在 Cursor 中运行 Android 项目指南
android·java·个人开发
任子菲阳2 小时前
学Javaweb第四天——springboot入门
java·spring·mybatis
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 基于Springboot的球场管理平台的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
前端不太难2 小时前
RN 列表里的「局部状态」和「全局状态」边界
开发语言·javascript·ecmascript
C雨后彩虹2 小时前
HashMap的线程安全问题:原因分析与解决方案
java·数据结构·哈希算法·集合·hashmap
3824278272 小时前
python3网络爬虫开发实战 第二版:绑定回调
开发语言·数据库·python
有趣灵魂2 小时前
Java-Spingboot根据HTML模板和动态数据生成PDF文件
java·pdf·html
BIBI20492 小时前
Windows 上配置 Nacos Server 3.x.x 使用 MySQL 5.7
java·windows·spring boot·后端·mysql·nacos·配置