☰
别再用错 final 和 static 了!一文讲透 Java 两大高频关键字(附避坑指南)
2026/10/5 10:02:15 网站建设 项目流程

Java 关键字详解:final 与 static(作用 + 使用场景)

final和static是 Java 中使用频率最高、也最容易混淆的两个关键字。本文从「能修饰什么 → 起到什么作用 → 用在什么场景」三个层次,把这两个关键字彻底讲清楚。

📌 一句话区分:final管「不可改变」,static管「类级别共享」。


目录

  • 一、final 关键字
    • 1.1 final 修饰类
    • 1.2 final 修饰方法
    • 1.3 final 修饰变量
    • 1.4 final 修饰引用类型的易错点
    • 1.5 final 的三大使用场景
  • 二、static 关键字
    • 2.1 static 修饰成员变量
    • 2.2 static 修饰成员方法
    • 2.3 static 修饰代码块
    • 2.4 static 修饰内部类
    • 2.5 static 的典型使用场景
    • 2.6 static 常见误区
  • 三、final 与 static 对比总结
  • 四、总结

一、final 关键字

一句话概括:final的语义是「最终的、不可改变的」,它可以修饰类、方法、变量三种目标,但「不可改变」的具体含义随修饰目标而不同。

修饰目标作用一句话记忆
类该类不能被继承断子绝孙
方法该方法不能被重写不容篡改
变量该变量只能赋值一次(常量)一锤定音

1.1 final 修饰类

当final修饰一个类时,该类不能被继承,所有成员方法都不能被重写。

publicfinalclassString{// String 就是典型的 final 类}// ❌ 编译报错:无法从最终类 String 中进行继承classMyStringextendsString{}

核心作用:防止通过继承来改变类的行为。JDK 中的典型案例:String、Integer等包装类都被声明为final——因为如果允许继承并重写方法,就可能破坏它们「不可变」的语义。

1.2 final 修饰方法

当final修饰一个方法时,该方法不能被重写,但可以正常被子类继承使用。

classFather{publicfinalvoidshow(){System.out.println("Father 的方法,不允许被改写");}}classSonextendsFather{// ❌ 编译报错:Son 中的 show() 无法覆盖 Father 中的 show()// public void show() { }}

核心作用:① 锁定方法行为,防止子类篡改;② 在早期 JVM 中还有利于内联优化(现代 JVM 已能自动判断,这条更多是历史原因)。

1.3 final 修饰变量

final修饰变量时,该变量成为常量,只能被赋值一次。按变量种类又分三种情况:

① 修饰局部变量:赋值后不可再改。

publicstaticvoidmain(String[]args){finalinta=10;// a = 20; // ❌ 编译报错:无法为最终变量 a 分配值}

② 修饰成员变量:必须显式初始化,且只能通过以下两种方式之一赋值:

  • 声明时直接赋值;
  • 在构造方法中赋值(每个构造方法都要保证赋值一次)。
classStudent{finalStringname="Hanmeimei";// 方式一:声明时赋值finalintage;Student(intage){// 方式二:构造方法中赋值this.age=age;}}

③ 修饰静态变量(static final):即全局常量,通常要求声明时立即赋值,命名习惯是全大写。

publicclassConstants{publicstaticfinaldoublePI=3.1415926;publicstaticfinalintMAX_COUNT=100;}

1.4 final 修饰引用类型的易错点

这是面试与日常编码的高频坑点:当final修饰引用类型时,它约束的是引用本身(指向不能变),而不是引用指向的对象内容。

publicstaticvoidmain(String[]args){finalint[]array={1,2,3,4,5};array[0]=100;// ✅ 编译通过:内容可以修改System.out.println(Arrays.toString(array));// [100, 2, 3, 4, 5]// array = new int[]{4, 5, 6}; // ❌ 编译报错:无法为最终变量 array 分配值}

结论:final修饰引用类型 →指向不能变,内容可以变。这也是「String 不可变不是因为 value 被 final 修饰」的原因——真正原因是 String 类中没有任何修改 value 内容的方法。

1.5 final 的三大使用场景

场景写法说明
定义全局常量public static final int MAX = 100;最常用,配合static形成类级别常量
保护不可变类public final class Xxx禁止被继承,防止行为被篡改
锁定关键方法逻辑public final void login()安全校验、模板方法等不允许被子类改写

二、static 关键字

一句话概括:static的语义是「静态的、类级别的」——被static修饰的成员属于类,而不属于某个具体对象,在类加载时就完成初始化,全类共享同一份。

修饰目标作用一句话记忆
成员变量变成类变量,全类共享一份大家共用一个
成员方法变成类方法,可通过类名直接调用不用 new 也能用
代码块成为静态代码块,类加载时执行一次一次性初始化
内部类成为静态内部类,不依赖外部类实例独立于外部对象

2.1 static 修饰成员变量

被static修饰的变量称为静态变量 / 类变量,它在方法区中只存一份,被该类的所有对象共享。

classStudent{publicStringname;// 实例变量:每个对象各有一份publicstaticStringschool="第一中学";// 静态变量:所有对象共享一份publicStudent(Stringname){this.name=name;}}publicstaticvoidmain(String[]args){Students1=newStudent("张三");Students2=newStudent("李四");System.out.println(s1.school);// 第一中学(不推荐这样写)System.out.println(Student.school);// ✅ 推荐:通过类名访问Student.school="第二中学";// 修改静态变量System.out.println(s2.school);// 第二中学 —— s2 也受影响!}

关键区别:实例变量随对象的创建而存在;静态变量在类加载时就已存在,即使没有创建任何对象也能使用。

2.2 static 修饰成员方法

被static修饰的方法称为静态方法 / 类方法,可以直接通过类名调用,不需要创建对象。

publicclassUtils{publicstaticintadd(inta,intb){returna+b;}publicintsub(inta,intb){// 实例方法returna-b;}}publicstaticvoidmain(String[]args){intr1=Utils.add(1,2);// ✅ 静态方法:类名直接调用// Utils.sub(3, 1); // ❌ 编译报错:实例方法必须先 newUtilsu=newUtils();intr2=u.sub(3,1);// 实例方法:通过对象调用}

静态方法的两条硬性限制:

  1. 静态方法中不能直接访问非静态(实例)成员——因为静态方法在对象创建前就可能被调用,此时实例成员还不存在;
  2. 静态方法中不能使用this和super—— 它们都依赖当前对象。
publicclassDemo{intnum=10;// 实例变量publicstaticvoidtest(){// System.out.println(num); // ❌ 编译报错:无法从静态上下文中引用非静态变量// System.out.println(this.num); // ❌ 编译报错:this 不可用于静态上下文}}

反之,实例方法可以自由访问静态成员。

2.3 static 修饰代码块

静态代码块在类加载时自动执行一次,且只执行一次,常用于初始化静态成员(如加载配置文件、初始化连接池)。

publicclassConfig{publicstaticMap<String,String>map;static{// 静态代码块:类加载时执行一次map=newHashMap<>();map.put("url","jdbc:mysql://localhost:3306/db");map.put("driver","com.mysql.cj.jdbc.Driver");System.out.println("静态代码块执行了");}publicConfig(){System.out.println("构造方法执行了");}publicstaticvoidmain(String[]args){newConfig();// 输出顺序:静态代码块执行了 → 构造方法执行了newConfig();// 输出:构造方法执行了(静态代码块不再执行)}}

执行顺序(重要):

父类静态代码块 → 子类静态代码块 → 父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法

2.4 static 修饰内部类

静态内部类不依赖外部类对象即可创建,不能直接访问外部类的非静态成员。

publicclassOuter{privatestaticinta=1;privateintb=2;staticclassInner{// 静态内部类publicvoidshow(){System.out.println(a);// ✅ 可以访问外部类的静态成员// System.out.println(b); // ❌ 不能访问外部类的实例成员}}classInner2{// 普通内部类publicvoidshow(){System.out.println(b);// ✅ 可以访问外部类实例成员}}publicstaticvoidmain(String[]args){Outer.Innerinner=newOuter.Inner();// 静态内部类:无需外部类对象inner.show();Outerouter=newOuter();Outer.Inner2inner2=outer.newInner2();// 普通内部类:必须先有外部类对象inner2.show();}}

2.5 static 的典型使用场景

场景示例说明
工具类方法Math.abs()、Arrays.sort()无需创建对象,直接调用
全局常量public static final int MAX = 100;类级别共享常量
全局计数器static int count;统计该类创建了多少对象
单例模式private static final Xxx instance = new Xxx();饿汉式单例
静态工厂方法Integer.valueOf(1)隐藏构造细节
资源初始化静态代码块加载配置文件类加载时执行一次
main方法public static void main(String[] args)JVM 无需创建对象即可调用入口
// 场景示例:用静态变量统计创建了多少个对象classCounter{publicstaticintcount=0;// 所有对象共享publicCounter(){count++;// 每创建一个对象,count 加 1}}publicstaticvoidmain(String[]args){newCounter();newCounter();newCounter();System.out.println(Counter.count);// 3}

2.6 static 常见误区

  1. 静态方法里不能用this/super,也不能直接访问实例变量和实例方法;
  2. 静态方法不能被重写:父类静态方法在子类中「同名同参」时是隐藏(Hide),不是重写,且调用取决于编译时类型;
  3. 静态变量是共享的,一个对象改了会影响所有对象(多线程场景下要格外小心,可能需要synchronized或volatile);
  4. static不能修饰局部变量(局部变量属于方法栈帧,无"类级别"概念)。

三、final 与 static 对比总结

3.1 final vs static

维度finalstatic
核心语义最终的、不可改变静态的、类级别共享
修饰类类不能被继承只用于静态内部类
修饰方法方法不能被重写方法属于类,类名可直接调用
修饰变量变量只能赋值一次变量属于类,全类共享一份
生效时机编译期约束(引用不可变)类加载时初始化
常见组合public static final→ 全局常量同左

3.2 容易混淆的组合

组合含义示例
static final静态常量,类加载时赋值,全类共享public static final int MAX = 100;
final static与上等价(修饰符顺序不影响语义)final static int MIN = 0;
abstractvsfinal互斥:抽象要求被继承重写,final 禁止二者不能同时修饰同一个类/方法
staticvsabstract互斥:静态方法不能被重写,抽象方法必须被重写不能同时修饰同一个方法

四、总结

  1. final是「不可变」的保证,三种修饰含义不同:类不可继承、方法不可重写、变量只能赋值一次;注意final修饰引用类型时只锁指向、不锁内容;
  2. static是「类级别」的标记,修饰的成员属于类、全类共享、类加载时初始化;静态方法不能访问实例成员、不能用 this/super、不能被重写;
  3. static final组合是定义全局常量的标准姿势,命名习惯全大写;

如果这篇文章对你有帮助,欢迎点赞 👍 + 收藏 ⭐ + 关注,后续会继续更新 Java 系列文章!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询