文章目录
-
- 前言
- [一、static 的基本概念](#一、static 的基本概念)
-
- [1.1 static 修饰什么](#1.1 static 修饰什么)
- [1.2 static 的一句话理解](#1.2 static 的一句话理解)
- [二、static 变量](#二、static 变量)
-
- [2.1 类变量与实例变量](#2.1 类变量与实例变量)
- [2.2 使用场景](#2.2 使用场景)
- [三、static 方法](#三、static 方法)
-
- [3.1 静态方法的特点](#3.1 静态方法的特点)
- [3.2 使用场景](#3.2 使用场景)
- [四、static 代码块](#四、static 代码块)
-
- [4.1 static 代码块什么时候执行](#4.1 static 代码块什么时候执行)
- [4.2 初始化顺序](#4.2 初始化顺序)
- [五、static 内部类](#五、static 内部类)
-
- [5.1 静态内部类](#5.1 静态内部类)
- [5.2 使用场景](#5.2 使用场景)
- [六、static 与内存模型](#六、static 与内存模型)
-
- [6.1 static 存在哪里](#6.1 static 存在哪里)
- [6.2 类加载与初始化](#6.2 类加载与初始化)
- [6.3 线程安全思考](#6.3 线程安全思考)
- [七、项目中如何使用 static](#七、项目中如何使用 static)
-
- [7.1 推荐使用](#7.1 推荐使用)
- [7.2 谨慎使用](#7.2 谨慎使用)
- [八、面试如何考察 static](#八、面试如何考察 static)
-
- [8.1 高频问题](#8.1 高频问题)
- [8.2 回答思路](#8.2 回答思路)
- 总结
前言
static 是 Java 里非常常见的关键字。它的核心意思是:这个成员属于类,而不是属于某一个对象。
如果把类比作一张"设计图",对象就是按设计图造出来的一台台机器。普通字段是每台机器自己的零件,static 字段则像挂在工厂墙上的公共看板,所有机器看到的是同一份。
一、static 的基本概念
1.1 static 修饰什么
static 可以修饰变量、方法、代码块、内部类。
常见形式:
java
public class Demo {
static int count;
static void hello() {
System.out.println("hello");
}
static {
System.out.println("class init");
}
static class Helper {
}
}
不能直接修饰普通外部类,也不能修饰局部变量。
1.2 static 的一句话理解
普通成员依赖对象,static 成员依赖类。
所以普通成员通常用 对象.成员 访问,静态成员通常用 类名.成员 访问。
二、static 变量
2.1 类变量与实例变量
被 static 修饰的变量叫类变量。它在类加载后只有一份,被这个类的所有对象共享。
实例变量属于对象,每 new 一次就有一份。
java
class User {
static int total;
String name;
User(String name) {
this.name = name;
total++;
}
}
这里 name 是每个用户自己的名字,total 是所有用户共同维护的总人数。
2.2 使用场景
static 变量适合存放"全班共享"的数据。
常见场景:
java
public class AppConfig {
public static final String APP_NAME = "demo";
}
public class Counter {
private static int total;
public Counter() {
total++;
}
}
适合放常量、计数器、共享配置、缓存引用。
不适合放用户登录状态、请求参数、订单数据等会因对象或线程变化而变化的数据。
三、static 方法
3.1 静态方法的特点
静态方法属于类,可以不创建对象直接调用。
java
Math.max(1, 2);
Integer.parseInt("123");
静态方法中不能直接访问非静态字段,因为它执行时不一定有对象。
java
class Demo {
String name;
static void test() {
// System.out.println(name); // 编译错误
}
}
原因很简单:你站在工厂办公室里,能看公共看板,但不能知道某一台机器里私有零件的状态,除非你拿到那台机器的引用。
3.2 使用场景
静态方法适合无状态工具方法。
java
public class StringUtils {
public static boolean isBlank(String value) {
return value == null || value.trim().isEmpty();
}
}
如果方法强依赖对象状态,就不要写成 static。
四、static 代码块
4.1 static 代码块什么时候执行
静态代码块在类初始化时执行,并且一个类只执行一次。
java
class Demo {
static {
System.out.println("load Demo");
}
}
常见触发时机包括:创建对象、访问静态字段、调用静态方法、反射加载类。
4.2 初始化顺序
大致顺序是:
text
类加载 -> 静态字段赋值和静态代码块 -> 实例字段和实例代码块 -> 构造方法
静态内容先于对象存在。它像工厂开门前先装好的公共设备,对象只是后面生产出来的产品。
五、static 内部类
5.1 静态内部类
静态内部类不依赖外部类对象。
java
class Outer {
static class Helper {
}
}
Outer.Helper helper = new Outer.Helper();
普通内部类依赖外部类对象。
java
class Outer {
class Inner {
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
5.2 使用场景
静态内部类常用于把辅助类收在外部类内部,表达"它只服务于这个外部类"的关系。
经典用法是 Holder 单例:
java
public class Singleton {
private Singleton() {
}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
它利用类初始化的线程安全特性,实现懒加载和安全发布。
六、static 与内存模型
6.1 static 存在哪里
很多旧资料会说 static 变量在方法区。更准确地说,类的元数据在方法区的实现中,HotSpot 从 JDK 8 开始使用元空间保存类元数据;而静态字段的实际存储与类对象相关,底层实现细节和 JVM 版本有关。
学习时可以先记住:static 成员和类生命周期绑定,不和某个实例对象绑定。
6.2 类加载与初始化
Java 类从被 JVM 认识到可使用,会经历加载、验证、准备、解析、初始化等阶段。
对 static 来说,重点是:
准备阶段会给静态变量分配默认值。
初始化阶段会执行显式赋值和 static 代码块。
java
static int count = 10;
准备阶段:count = 0。
初始化阶段:count = 10。
6.3 线程安全思考
static 变量是共享的,共享就可能有并发问题。
java
private static int count;
public static void add() {
count++;
}
count++ 不是原子操作,多线程下可能丢失更新。项目中可用 AtomicInteger、锁、不可变对象,或者避免共享可变静态状态。
七、项目中如何使用 static
7.1 推荐使用
推荐用于常量:
java
public static final int MAX_RETRY = 3;
推荐用于无状态工具方法:
java
public static String normalize(String value) {
return value == null ? "" : value.trim().toLowerCase();
}
推荐用于 Holder 单例:
java
private static class Holder {
private static final Service INSTANCE = new Service();
}
7.2 谨慎使用
谨慎使用可变静态变量。
java
public static List<String> users = new ArrayList<>();
它容易带来全局状态污染、测试互相影响、线程安全问题和难以追踪的数据变化。
项目经验里,static 最大的问题不是不会用,而是用得太方便。
八、面试如何考察 static
8.1 高频问题
常见问题:
static 变量和实例变量区别是什么?
static 方法能不能访问普通成员?
static 代码块什么时候执行?
类加载初始化顺序是什么?
static final 和普通 static 有什么区别?
静态内部类为什么能实现单例?
静态变量线程安全吗?
8.2 回答思路
先讲归属:static 属于类,普通成员属于对象。
再讲生命周期:static 随类加载初始化,普通成员随对象创建初始化。
再讲访问限制:静态上下文没有 this,不能直接访问实例成员。
最后讲项目风险:共享可变状态要注意并发和测试污染。
总结
static 的本质是"属于类,而不是属于对象"。
用得好,它可以表达常量、工具方法、类级共享资源和单例结构;用得不好,它会变成全局变量,制造并发问题和测试污染。
记住一句话:能不共享就不共享,必须共享就尽量不可变;如果共享可变状态,就要认真考虑线程安全和生命周期。