Java 中 static 与 final 详解(简单易懂版)
这是 Java 初学者和面试最常混淆的两个关键字。我们用最直白的话 + 生活比喻 + 表格 + 代码来一次讲透。
一句话总结(背下来就行)
- static:“属于全班,不属于某个人” —— 共享、一份拷贝、类级别
- final:“一旦定下来就不能改了” —— 不可变、最终的、锁死
两者可以叠加使用:static final → “全班共用,且永远不变的那个东西”(最常见的常量写法)
1. 对比表(最清晰)
| 维度 | static | final | static final(组合最常见) |
|---|---|---|---|
| 含义 | 静态、类级别、共享 | 最终的、不可修改 | 静态常量(全局唯一、不可改) |
| 修饰对象 | 变量、方法、代码块、内部类 | 变量、方法、类 | 通常修饰变量(常量) |
| 内存中份数 | 只有一份(方法区/元空间) | 看情况(实例变量每对象一份) | 只有一份,且值不可改 |
| 什么时候赋值 | 类加载时(静态变量/代码块) | 声明时 / 构造器 / 初始化块(只能一次) | 声明时或静态初始化块(推荐声明时赋值) |
| 能否修改值 | 可以(除非再加 final) | 绝对不能(基本类型值、引用类型地址) | 绝对不能 |
| 调用方式 | 类名.XXX(推荐) / 对象.XXX(不推荐) | 看情况(实例变量用对象访问) | 类名.常量名(全大写+下划线) |
| 典型场景 | 工具方法、计数器、全局配置、单例 | 常量、不可变对象、防止子类重写 | public static final int MAX = 100; |
| 常见误区 | 静态方法里不能用 this / 非静态成员 | final 引用类型只是地址不可变,内容可变 | 常量名必须全大写 + 下划线分隔 |
2. static 的核心用法(4种)
- static 变量(类变量)
class Counter {
static int count = 0; // 全班共用一个计数器
public Counter() {
count++; // 每new一个对象,count+1
}
}
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.count); // 2
- static 方法(工具方法、工厂方法)
public class MathUtil {
public static int max(int a, int b) {
return a > b ? a : b;
}
}
// 调用方式(推荐)
MathUtil.max(3, 8); // 不需要new对象
铁律:静态方法里不能出现 this、super、非静态成员(因为没有对象)
- static 代码块(类加载时执行一次,常用于初始化静态资源)
static {
System.out.println("类第一次被加载时执行,只执行一次");
// 加载配置文件、注册驱动等
}
- static 内部类(最不常用,但单例模式常用)
3. final 的核心用法(3种)
- final 变量(最常用)
- 基本类型:值不可改
- 引用类型:地址不可改(但对象内容可以改)
final int MAX = 100;
// MAX = 200; // 编译错误
final List<String> list = new ArrayList<>();
list.add("hello"); // OK,内容可改
// list = new ArrayList<>(); // 错误,地址不能改
- final 方法(防止子类重写)
class Parent {
final void sayHello() {
System.out.println("父类说hello");
}
}
class Child extends Parent {
// void sayHello() {} // 编译错误,不能重写
}
- final 类(不能被继承)
public final class String { ... } // String 就是 final 类
4. static final 组合(常量定义标准写法)
public class Constants {
public static final int MAX_USERS = 1000;
public static final String DEFAULT_THEME = "dark";
public static final double PI = 3.141592653589793;
}
命名规范(必须遵守):
- 全大写
- 单词之间用下划线
_分隔 - 属于类级别 →
public static final
5. 经典面试/易错点(一问就露馅)
| 问题 | 答案 |
|---|---|
| static 方法能被重写吗? | 可以“隐藏”(hide),不是真正重写(override) |
| final 方法能被 static 修饰吗? | 可以,常见于工具类(public static final void xxx()) |
| static final 变量必须在声明时赋值吗? | 不一定,可以在静态代码块中赋值,但推荐声明时赋值 |
| final 数组内容能改吗? | 能!final 只锁地址,数组元素可以改 |
| private static final 有什么用? | 类内部常量(不对外暴露) |
| 接口中变量默认是什么修饰符? | public static final(可以省略写) |
6. 记忆口诀(背下来就过关)
- static → “全班共用一份”
- final → “锁死,不能改”
- static final → “全班共用的锁死的东西”(常量)
- static 方法里 → “没有this,就没有对象成员”
- final 引用 → “人可以换手机壳,但不能换人”(地址不变,内容可变)
有哪部分还想再看例子?比如:
- static + final 在单例模式里的用法
- final 在多线程/不可变对象中的作用
- static 代码块 vs 实例初始化块 vs 构造器执行顺序
- 常量池与 static final 的关系
直接说,我继续给你展开~