Java static 关键字从浅入深

文章目录

    • 前言
    • [一、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 的本质是"属于类,而不是属于对象"。

用得好,它可以表达常量、工具方法、类级共享资源和单例结构;用得不好,它会变成全局变量,制造并发问题和测试污染。

记住一句话:能不共享就不共享,必须共享就尽量不可变;如果共享可变状态,就要认真考虑线程安全和生命周期。

相关推荐
猫猫的小茶馆4 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32
计算机安禾4 小时前
【c++面向对象编程】第38篇:设计原则(二):里氏替换、接口隔离与依赖倒置
开发语言·c++
_院长大人_4 小时前
Java Excel导出:如何实现自定义表头与字段顺序的完全控制
java·开发语言·后端·excel
磊 子4 小时前
1.4CPU缓存一致性
java·spring cloud·缓存·系统
周末也要写八哥4 小时前
Eclipse 2024全流程网盘下载与安装配置教程详解
java·ide·eclipse
code_whiter4 小时前
C++1进阶(继承)
开发语言·c++
来恩10034 小时前
JSTL的标签库种类
java·开发语言
Miss_min4 小时前
128K长序列数据生成
开发语言·python·深度学习
小宋0014 小时前
QT中控件qss样式修改
开发语言·qt