📖 静态变量基本概念
什么是静态变量?
- 使用
static关键字修饰的成员变量 - 属于类本身,而不是类的某个实例
- 所有对象共享同一个静态变量
- 在类加载时初始化,生命周期与类相同
🎯 静态变量的特点
1. 类级别共享
java
public class StaticVariableDemo {
// 实例变量 - 每个对象独立拥有
private int instanceVar = 0;
// 静态变量 - 所有对象共享
private static int staticVar = 0;
public void increment() {
instanceVar++; // 修改实例变量
staticVar++; // 修改静态变量
}
public void show() {
System.out.println("instanceVar = " + instanceVar + ", staticVar = " + staticVar);
}
public static void main(String[] args) {
StaticVariableDemo obj1 = new StaticVariableDemo();
StaticVariableDemo obj2 = new StaticVariableDemo();
obj1.increment();
obj1.show(); // 输出: instanceVar = 1, staticVar = 1
obj2.increment();
obj2.show(); // 输出: instanceVar = 1, staticVar = 2
// 静态变量可以通过类名直接访问
System.out.println("通过类名访问: " + StaticVariableDemo.staticVar); // 输出: 2
// 也可以通过对象访问(不推荐)
System.out.println("通过对象访问: " + obj1.staticVar); // 输出: 2
}
}
2. 内存分配时机
java
public class MemoryAllocation {
// 静态变量在类加载时初始化
static String staticField = "静态变量(类加载时初始化)";
// 实例变量在对象创建时初始化
String instanceField = "实例变量(对象创建时初始化)";
// 静态代码块(类加载时执行)
static {
System.out.println("1. 静态代码块执行");
System.out.println(" 静态变量值: " + staticField);
}
// 实例代码块(对象创建时执行)
{
System.out.println("3. 实例代码块执行");
System.out.println(" 实例变量值: " + instanceField);
}
// 构造器(对象创建时执行)
public MemoryAllocation() {
System.out.println("4. 构造器执行");
}
public static void main(String[] args) {
System.out.println("2. main方法开始");
System.out.println("\n=== 创建第一个对象 ===");
MemoryAllocation obj1 = new MemoryAllocation();
System.out.println("\n=== 创建第二个对象 ===");
MemoryAllocation obj2 = new MemoryAllocation();
/*
输出顺序:
1. 静态代码块执行(只执行一次)
静态变量值: 静态变量(类加载时初始化)
2. main方法开始
=== 创建第一个对象 ===
3. 实例代码块执行
实例变量值: 实例变量(对象创建时初始化)
4. 构造器执行
=== 创建第二个对象 ===
3. 实例代码块执行
实例变量值: 实例变量(对象创建时初始化)
4. 构造器执行
*/
}
}
🏗️ 静态变量的声明和使用
1. 基本语法
java
public class StaticSyntax {
// 声明静态变量
static int count; // 默认值 0
static String name; // 默认值 null
static boolean flag; // 默认值 false
static double price; // 默认值 0.0
// 声明并初始化
static int total = 100;
static final double PI = 3.14159; // 静态常量
// 静态代码块初始化
static {
System.out.println("静态代码块执行");
count = 10;
name = "Java";
}
public static void main(String[] args) {
// 访问静态变量(推荐通过类名)
System.out.println("count = " + StaticSyntax.count);
System.out.println("name = " + StaticSyntax.name);
System.out.println("total = " + StaticSyntax.total);
System.out.println("PI = " + StaticSyntax.PI);
// 修改静态变量
StaticSyntax.total = 200;
System.out.println("修改后 total = " + StaticSyntax.total);
// 静态常量不能修改
// StaticSyntax.PI = 3.14; // ❌ 编译错误
}
}
2. 静态变量的访问方式
java
public class AccessWays {
static String message = "Hello, Static!";
String instanceMsg = "Hello, Instance!";
public static void main(String[] args) {
// ✅ 正确方式1:通过类名访问(推荐)
System.out.println("类名访问: " + AccessWays.message);
// ✅ 正确方式2:在静态方法中直接访问(同一类内)
System.out.println("直接访问: " + message);
// ✅ 正确方式3:通过对象访问(不推荐,会产生误导)
AccessWays obj = new AccessWays();
System.out.println("对象访问: " + obj.message); // 可以但不推荐
// ❌ 错误:静态方法中不能直接访问实例变量
// System.out.println(instanceMsg); // 编译错误
// ✅ 正确:需要先创建对象
System.out.println("实例变量: " + obj.instanceMsg);
// 访问其他类的静态变量
System.out.println("\n访问其他类的静态变量:");
System.out.println("Integer最大值: " + Integer.MAX_VALUE);
System.out.println("Math的PI: " + Math.PI);
}
}
// 另一个类
class AnotherClass {
static String info = "来自另一个类的静态变量";
}
🔧 静态变量的应用场景
1. 计数器/ID生成器
java
public class Counter {
// 静态变量作为计数器
private static int count = 0;
// 实例变量:每个对象的唯一ID
private final int id;
public Counter() {
count++; // 每次创建对象计数器加1
id = count; // 为对象分配唯一ID
System.out.println("创建第 " + id + " 个对象");
}
// 获取当前对象数量
public static int getCount() {
return count;
}
// 获取对象ID
public int getId() {
return id;
}
public static void main(String[] args) {
System.out.println("初始对象数: " + Counter.getCount());
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println("\n对象信息:");
System.out.println("c1 ID: " + c1.getId());
System.out.println("c2 ID: " + c2.getId());
System.out.println("c3 ID: " + c3.getId());
System.out.println("总对象数: " + Counter.getCount());
}
}
2. 配置信息/全局常量
java
public class AppConfig {
// 全局配置信息(静态常量)
public static final String APP_NAME = "MyApplication";
public static final String VERSION = "1.0.0";
public static final int MAX_USERS = 1000;
public static final double TAX_RATE = 0.13;
// 运行时配置(静态变量)
private static String language = "zh-CN";
private static boolean debugMode = false;
private static int connectionTimeout = 30000;
// 提供静态方法访问和修改配置
public static String getLanguage() {
return language;
}
public static void setLanguage(String lang) {
language = lang;
}
public static boolean isDebugMode() {
return debugMode;
}
public static void setDebugMode(boolean debug) {
debugMode = debug;
}
public static int getConnectionTimeout() {
return connectionTimeout;
}
public static void setConnectionTimeout(int timeout) {
if (timeout > 0) {
connectionTimeout = timeout;
}
}
public static void printConfig() {
System.out.println("=== 应用配置 ===");
System.out.println("应用名称: " + APP_NAME);
System.out.println("版本: " + VERSION);
System.out.println("语言: " + language);
System.out.println("调试模式: " + debugMode);
System.out.println("连接超时: " + connectionTimeout + "ms");
System.out.println("最大用户数: " + MAX_USERS);
System.out.println("税率: " + TAX_RATE);
}
public static void main(String[] args) {
// 使用全局配置
AppConfig.printConfig();
// 修改运行时配置
AppConfig.setDebugMode(true);
AppConfig.setLanguage("en-US");
System.out.println("\n=== 修改后配置 ===");
AppConfig.printConfig();
// 在其他类中使用配置
System.out.println("\n在业务代码中使用:");
double price = 100.0;
double tax = price * AppConfig.TAX_RATE;
System.out.println("商品价格: $" + price);
System.out.println("税费: $" + tax);
System.out.println("总价: $" + (price + tax));
}
}
3. 缓存数据
java
import java.util.HashMap;
import java.util.Map;
public class DataCache {
// 静态Map作为缓存
private static Map<String, Object> cache = new HashMap<>();
// 缓存过期时间(毫秒)
private static Map<String, Long> expireTime = new HashMap<>();
private static final long DEFAULT_EXPIRE = 60000; // 默认1分钟
/**
* 存入缓存
*/
public static void put(String key, Object value) {
put(key, value, DEFAULT_EXPIRE);
}
public static void put(String key, Object value, long expireMillis) {
cache.put(key, value);
expireTime.put(key, System.currentTimeMillis() + expireMillis);
System.out.println("缓存已存入: " + key + " = " + value);
}
/**
* 获取缓存
*/
public static Object get(String key) {
if (!cache.containsKey(key)) {
System.out.println("缓存未命中: " + key);
return null;
}
// 检查是否过期
long now = System.currentTimeMillis();
if (now > expireTime.get(key)) {
System.out.println("缓存已过期: " + key);
cache.remove(key);
expireTime.remove(key);
return null;
}
System.out.println("缓存命中: " + key);
return cache.get(key);
}
/**
* 清理过期缓存
*/
public static void cleanExpired() {
long now = System.currentTimeMillis();
int count = 0;
for (String key : new HashMap<>(expireTime).keySet()) {
if (now > expireTime.get(key)) {
cache.remove(key);
expireTime.remove(key);
count++;
}
}
System.out.println("清理了 " + count + " 个过期缓存");
}
/**
* 清空所有缓存
*/
public static void clear() {
cache.clear();
expireTime.clear();
System.out.println("缓存已清空");
}
/**
* 获取缓存统计信息
*/
public static void showStats() {
System.out.println("\n=== 缓存统计 ===");
System.out.println("缓存数量: " + cache.size());
System.out.println("缓存内容:");
for (Map.Entry<String, Object> entry : cache.entrySet()) {
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
}
}
public static void main(String[] args) throws InterruptedException {
// 测试缓存功能
DataCache.put("user:1", "张三");
DataCache.put("product:100", "iPhone", 5000); // 5秒过期
// 立即获取
System.out.println("\n获取缓存:");
System.out.println("user:1 = " + DataCache.get("user:1"));
System.out.println("product:100 = " + DataCache.get("product:100"));
// 等待6秒后再次获取
System.out.println("\n等待6秒后...");
Thread.sleep(6000);
System.out.println("user:1 = " + DataCache.get("user:1"));
System.out.println("product:100 = " + DataCache.get("product:100")); // 应已过期
// 清理过期缓存
DataCache.cleanExpired();
DataCache.showStats();
}
}
⚠️ 静态变量的注意事项
1. 线程安全问题
java
public class ThreadSafety {
// 非线程安全的静态变量
private static int counter = 0;
// 线程安全的递增方法(使用synchronized)
public static synchronized void safeIncrement() {
counter++;
}
// 非线程安全的递增方法
public static void unsafeIncrement() {
counter++;
}
public static void main(String[] args) throws InterruptedException {
// 创建10个线程,每个线程递增1000次
Thread[] threads = new Thread[10];
// 测试非线程安全的方法
counter = 0; // 重置计数器
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
unsafeIncrement(); // 非线程安全
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread t : threads) {
t.join();
}
System.out.println("非线程安全结果: " + counter + " (期望: 10000)");
// 测试线程安全的方法
counter = 0; // 重置计数器
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
safeIncrement(); // 线程安全
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("线程安全结果: " + counter + " (期望: 10000)");
// 更好的线程安全方案:使用AtomicInteger
java.util.concurrent.atomic.AtomicInteger atomicCounter =
new java.util.concurrent.atomic.AtomicInteger(0);
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
atomicCounter.incrementAndGet();
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("AtomicInteger结果: " + atomicCounter.get() + " (期望: 10000)");
}
}
2. 静态变量与继承
java
class Parent {
// 父类静态变量
static String staticField = "父类静态变量";
// 父类实例变量
String instanceField = "父类实例变量";
// 静态方法
static void staticMethod() {
System.out.println("父类静态方法");
}
// 实例方法
void instanceMethod() {
System.out.println("父类实例方法");
}
}
class Child extends Parent {
// 子类静态变量(隐藏父类同名静态变量)
static String staticField = "子类静态变量";
// 子类实例变量(隐藏父类同名实例变量)
String instanceField = "子类实例变量";
// 静态方法(隐藏父类同名静态方法)
static void staticMethod() {
System.out.println("子类静态方法");
}
// 实例方法(重写父类实例方法)
@Override
void instanceMethod() {
System.out.println("子类实例方法");
}
}
public class StaticInheritance {
public static void main(String[] args) {
System.out.println("=== 静态成员 ===");
// 静态成员属于类,通过类名访问
System.out.println("Parent.staticField: " + Parent.staticField);
System.out.println("Child.staticField: " + Child.staticField);
Parent.staticMethod();
Child.staticMethod();
System.out.println("\n=== 实例成员 ===");
Parent parent = new Parent();
Child child = new Child();
Parent polymorphic = new Child(); // 多态
System.out.println("parent.instanceField: " + parent.instanceField);
System.out.println("child.instanceField: " + child.instanceField);
System.out.println("polymorphic.instanceField: " + polymorphic.instanceField); // 访问父类的
parent.instanceMethod();
child.instanceMethod();
polymorphic.instanceMethod(); // 多态:调用子类重写的方法
System.out.println("\n=== 通过子类访问父类静态成员 ===");
// 可以通过子类类名访问父类静态成员,但不推荐
System.out.println("通过子类访问: " + Child.staticField); // 子类的
// 强制访问父类静态成员
System.out.println("强制访问父类: " + ((Parent)null).staticField); // 父类的
// 总结:
// 1. 静态成员不存在重写(Override),只有隐藏(Hide)
// 2. 静态成员通过类名访问,编译时确定
// 3. 实例成员通过对象访问,运行时确定(多态)
}
}
3. 静态变量的初始化顺序
java
public class InitializationOrder {
// 静态变量(按顺序初始化)
static int a = init("静态变量 a");
static int b = init("静态变量 b");
// 静态代码块(按顺序执行)
static {
System.out.println("静态代码块 1");
}
static int c = init("静态变量 c");
static {
System.out.println("静态代码块 2");
}
// 实例变量
int x = init("实例变量 x");
// 实例代码块
{
System.out.println("实例代码块");
}
// 构造器
public InitializationOrder() {
System.out.println("构造器执行");
}
static int d = init("静态变量 d");
// 初始化方法
static int init(String message) {
System.out.println("初始化: " + message);
return 0;
}
public static void main(String[] args) {
System.out.println("\n=== main方法开始 ===");
System.out.println("\n=== 第一次创建对象 ===");
new InitializationOrder();
System.out.println("\n=== 第二次创建对象 ===");
new InitializationOrder();
/*
输出顺序:
初始化: 静态变量 a
初始化: 静态变量 b
静态代码块 1
初始化: 静态变量 c
静态代码块 2
初始化: 静态变量 d
=== main方法开始 ===
=== 第一次创建对象 ===
初始化: 实例变量 x
实例代码块
构造器执行
=== 第二次创建对象 ===
初始化: 实例变量 x
实例代码块
构造器执行
*/
}
}
💡 静态变量最佳实践
1. 单例模式(Singleton)
java
public class Singleton {
// 1. 私有静态变量(唯一实例)
private static Singleton instance;
// 2. 私有构造器(防止外部创建实例)
private Singleton() {
System.out.println("Singleton实例被创建");
}
// 3. 公共静态方法(全局访问点)
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 懒加载
}
return instance;
}
// 4. 线程安全版本(双重检查锁定)
public static Singleton getInstanceThreadSafe() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
// 5. 静态内部类版本(推荐)
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstanceByHolder() {
return SingletonHolder.INSTANCE;
}
// 实例方法
public void showMessage() {
System.out.println("Hello Singleton!");
}
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println("s1 == s2? " + (s1 == s2)); // true
s1.showMessage();
}
}
2. 工具类(Utility Class)
java
// 工具类通常设计为final,包含静态方法,私有构造器防止实例化
public final class StringUtils {
// 私有构造器,防止实例化
private StringUtils() {
throw new AssertionError("不能实例化工具类");
}
// 静态工具方法
/**
* 检查字符串是否为空或空白
*/
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
/**
* 检查字符串是否不为空
*/
public static boolean isNotBlank(String str) {
return !isBlank(str);
}
/**
* 首字母大写
*/
public static String capitalize(String str) {
if (isBlank(str)) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
* 重复字符串
*/
public static String repeat(String str, int times) {
if (str == null || times <= 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < times; i++) {
sb.append(str);
}
return sb.toString();
}
/**
* 反转字符串
*/
public static String reverse(String str) {
if (str == null) {
return null;
}
return new StringBuilder(str).reverse().toString();
}
public static void main(String[] args) {
// 使用工具类的静态方法
System.out.println("isBlank测试:");
System.out.println(" null: " + StringUtils.isBlank(null));
System.out.println(" '': " + StringUtils.isBlank(""));
System.out.println(" ' ': " + StringUtils.isBlank(" "));
System.out.println(" 'abc': " + StringUtils.isBlank("abc"));
System.out.println("\ncapitalize测试:");
System.out.println(" 'hello': " + StringUtils.capitalize("hello"));
System.out.println(" 'WORLD': " + StringUtils.capitalize("WORLD"));
System.out.println("\nrepeat测试:");
System.out.println(" 'ab'*3: " + StringUtils.repeat("ab", 3));
System.out.println("\nreverse测试:");
System.out.println(" '12345': " + StringUtils.reverse("12345"));
// 尝试实例化工具类(会抛出异常)
try {
// StringUtils utils = new StringUtils(); // 运行时异常
} catch (Exception e) {
System.out.println("\n不能实例化工具类: " + e.getMessage());
}
}
}
3. 常量类
java
// 专门存放常量的类
public final class Constants {
private Constants() {
// 防止实例化
}
// 数据库相关常量
public static final String DB_URL = "jdbc:mysql://localhost:3306/mydb";
public static final String DB_USER = "root";
public static final String DB_PASSWORD = "password";
public static final int DB_MAX_CONNECTIONS = 10;
// 应用配置常量
public static final String APP_NAME = "MyApp";
public static final String VERSION = "2.1.0";
public static final String DEFAULT_LANGUAGE = "zh-CN";
public static final int SESSION_TIMEOUT = 1800; // 30分钟
// 文件路径常量
public static final String LOG_DIR = "/var/log/myapp";
public static final String CONFIG_FILE = "/etc/myapp/config.properties";
public static final String TEMP_DIR = "/tmp/myapp";
// 业务常量
public static final int MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
public static final int MAX_LOGIN_ATTEMPTS = 5;
public static final double TAX_RATE = 0.13;
// 状态码常量
public static final int STATUS_OK = 200;
public static final int STATUS_ERROR = 500;
public static final int STATUS_NOT_FOUND = 404;
// 颜色常量(RGB)
public static final int COLOR_RED = 0xFF0000;
public static final int COLOR_GREEN = 0x00FF00;
public static final int COLOR_BLUE = 0x0000FF;
public static final int COLOR_WHITE = 0xFFFFFF;
// 使用枚举也是更好的选择
public enum UserRole {
ADMIN, USER, GUEST
}
public enum OrderStatus {
PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}
public static void main(String[] args) {
// 使用常量
System.out.println("应用名称: " + Constants.APP_NAME);
System.out.println("版本: " + Constants.VERSION);
System.out.println("最大文件大小: " + Constants.MAX_FILE_SIZE + " bytes");
System.out.println("税率: " + Constants.TAX_RATE);
// 使用枚举
System.out.println("\n用户角色:");
for (UserRole role : UserRole.values()) {
System.out.println(" " + role);
}
}
}
📊 静态变量 vs 实例变量
| 特性 | 静态变量 | 实例变量 |
|---|---|---|
| 所属级别 | 类级别 | 对象级别 |
| 内存分配 | 类加载时 | 对象创建时 |
| 内存位置 | 方法区 | 堆内存 |
| 共享性 | 所有对象共享 | 每个对象独立 |
| 生命周期 | 与类相同 | 与对象相同 |
| 访问方式 | 类名.变量名 |
对象.变量名 |
| 默认值 | 有(同实例变量) | 有 |
| 初始化 | 静态代码块/声明时 | 实例代码块/声明时/构造器 |
| 线程安全 | 需要额外处理 | 每个对象独立 |
🎓 总结要点
- 静态变量属于类:所有对象共享同一份
- 初始化时机:类加载时初始化,只初始化一次
- 访问方式:推荐通过类名访问,不推荐通过对象访问
- 线程安全:静态变量需要特别注意线程安全问题
- 内存管理:静态变量生命周期长,要避免内存泄漏
- 使用场景 :
- 共享数据(计数器、缓存)
- 工具类、常量类
- 单例模式
- 配置信息
记住:静态变量是全局的,要谨慎使用!过多使用静态变量会导致代码耦合度高、难以测试、内存泄漏等问题。合理使用静态变量可以让代码更简洁高效。