在Java中,并非所有的变量都具有相同的生命周期和存储方式。只有被 static 关键字修饰的变量才被称为类变量,它们归属于类本身,而不是某个具体的实例。
类变量(静态变量)
通过使用 static 修饰符声明的变量称为类变量,这类变量独立于任何对象存在,属于类级别,在整个程序运行期间仅有一份副本。
static
实例变量(成员变量)
未使用 static 修饰的变量是实例变量,也叫成员变量。每个对象实例都有自己独立的一套实例变量,存储在堆内存中的对应对象结构里。
代码示例解析
示例1:类变量与实例变量的区别
public class VariableStorageExample {
// 类变量(静态变量) - 存储在方法区
private static String classVariable = "我是类变量";
private static int staticCount = 0;
// 实例变量(成员变量) - 存储在堆中的对象实例内
private String instanceVariable;
private int instanceId;
public VariableStorageExample(String name) {
this.instanceVariable = name;
this.instanceId = ++staticCount; // 所有实例共享该计数器
}
public void printInfo() {
System.out.println("实例ID: " + instanceId +
", 实例变量: " + instanceVariable +
", 类变量: " + classVariable);
}
public static void main(String[] args) {
// 不需要创建实例即可访问类变量
System.out.println("直接访问类变量: " + VariableStorageExample.classVariable);
// 创建两个对象实例
VariableStorageExample obj1 = new VariableStorageExample("对象1");
VariableStorageExample obj2 = new VariableStorageExample("对象2");
obj1.printInfo();
obj2.printInfo();
// 修改类变量后,所有实例都会受到影响
VariableStorageExample.classVariable = "修改后的类变量";
System.out.println("修改类变量后:");
obj1.printInfo();
obj2.printInfo();
}
}
内存区域分布说明
方法区 (元空间):
└─ VariableStorageExample类信息
└─ 类变量: classVariable = "修改后的类变量"
└─ 类变量: staticCount = 2
堆内存:
└─ obj1对象实例:
├─ 对象头
├─ 实例变量: instanceVariable = "对象1"
├─ 实例变量: instanceId = 1
└─ 方法区引用
└─ obj2对象实例:
├─ 对象头
├─ 实例变量: instanceVariable = "对象2"
├─ 实例变量: instanceId = 2
└─ 方法区引用
进阶示例
示例2:包含数组和对象引用的成员变量
下面的例子展示了更复杂的成员变量结构,包括基本类型、数组、字符串以及集合类型的处理方式。
public class ComplexStorageExample {
// 类变量 - 存储在方法区
private static final String CLASS_NAME = "ComplexStorageExample";
private static int totalObjects = 0;
// 实例变量 - 分配在堆中每个对象内部
private int[] scores; // 数组引用位于对象内,实际数组对象也在堆中
private String name; // 字符串引用在对象内,字符串内容在堆中
private boolean active; // 基本数据类型直接存储在对象内部
private List<String> hobbies; // 集合引用在对象中,集合实例在堆中
public ComplexStorageExample(String name, int[] scores) {
this.name = name;
this.scores = scores;
this.active = true;
this.hobbies = new ArrayList<>();
totalObjects++;
}
public void addHobby(String hobby) {
hobbies.add(hobby);
}
public void printMemoryInfo() {
System.out.println("对象: " + name);
System.out.println(" - 类变量totalObjects: " + totalObjects);
System.out.println(" - 实例变量scores: " + Arrays.toString(scores));
System.out.println(" - 实例变量hobbies: " + hobbies);
System.out.println(" - 实例变量active: " + active);
}
public static void main(String[] args) {
// 演示不同对象间的内存分配情况
}
}
int[] scores1 = {90, 85, 95};
int[] scores2 = {80, 75, 88};
ComplexStorageExample obj1 = new ComplexStorageExample("Alice", scores1);
ComplexStorageExample obj2 = new ComplexStorageExample("Bob", scores2);
obj1.addHobby("Reading");
obj1.addHobby("Swimming");
obj2.addHobby("Gaming");
obj1.printMemoryInfo();
obj2.printMemoryInfo();
// 输出类变量的共享状态
System.out.println("\n总对象数(类变量): " + ComplexStorageExample.totalObjects);
}
}
内存布局详解
方法区 (元空间):
└─ ComplexStorageExample类信息
└─ 类变量: CLASS_NAME = "ComplexStorageExample" (常量池)
└─ 类变量: totalObjects = 2
堆内存:
└─ obj1对象实例:
├─ 对象头
├─ scores → [90, 85, 95] (int数组对象)
├─ name → "Alice" (String对象)
├─ active = true (直接存储在对象内)
├─ hobbies → ArrayList对象
│ └─ 内部数组 → ["Reading", "Swimming"] (String对象数组)
└─ 方法区引用
└─ obj2对象实例:
├─ 对象头
├─ scores → [80, 75, 88] (int数组对象)
├─ name → "Bob" (String对象)
├─ active = true
├─ hobbies → ArrayList对象
│ └─ 内部数组 → ["Gaming"] (String对象)
└─ 方法区引用
运行时常量池:
└─ "ComplexStorageExample", "Reading", "Swimming", "Gaming"等字面量
关键总结
1. 存储位置对比
| 变量类型 | 修饰符 | 存储区域 | 生命周期 | 访问方式 |
|---|---|---|---|---|
| 类变量 | static | 方法区 | 类加载到类卸载 | 类名.变量名 |
| 实例变量 | 无 | 堆内存(对象实例内) | 对象创建到回收 | 对象.变量名 |
static
2. 重要特点
类变量(静态变量):
- 在类加载阶段完成初始化
- 被该类所有实例共享,仅存在一份拷贝
- 可通过类名直接调用,无需创建对象
- 存储于方法区(或元空间)中
实例变量(成员变量):
- 每个对象都拥有自己独立的一份副本
- 随对象的创建而初始化
- 必须通过具体的对象实例进行访问
- 其数据保存在堆内存中对应对象的结构内部
3. 验证示例
public class ValidationExample {
static int classVar = 0; // 类变量 - 存储在方法区
int instanceVar = 0; // 实例变量 - 存在于堆中对象内部
public static void main(String[] args) {
// 类变量可直接通过类访问
System.out.println("类变量: " + ValidationExample.classVar);
// 实例变量需通过实例对象访问
ValidationExample obj = new ValidationExample();
System.out.println("实例变量: " + obj.instanceVar);
// 验证两者存储差异的行为表现
ValidationExample obj1 = new ValidationExample();
ValidationExample obj2 = new ValidationExample();
obj1.instanceVar = 100;
ValidationExample.classVar = 200;
System.out.println("obj1.instanceVar: " + obj1.instanceVar); // 输出:100
System.out.println("obj2.instanceVar: " + obj2.instanceVar); // 输出:0
System.out.println("obj1.classVar: " + obj1.classVar); // 输出:200
System.out.println("obj2.classVar: " + obj2.classVar); // 输出:200
}
}
输出结果
类变量: 0
实例变量: 0
obj1.instanceVar: 100
obj2.instanceVar: 0
obj1.classVar: 200
obj2.classVar: 200
上述运行结果明确表明:
- 修改
obj1的实例变量不会影响obj2的值,说明实例变量是各对象私有的。 - 当一个对象间接修改类变量(通过类名访问)后,其他所有对象都能看到这一变化,证明类变量为全局共享。


雷达卡


京公网安备 11010802022788号







