成员变量在类内方法外声明,随对象存续;局部变量在方法或代码块内声明,作用域限于对应范围。前者有默认值、可加访问修饰符、存于堆;后者须显式初始化、不可用private等修饰、存于栈。

成员变量和局部变量的声明位置与生命周期不同
成员变量写在类里、方法外,随对象创建而存在,对象销毁时才释放;局部变量写在方法内部、代码块里或for循环中,只在对应作用域执行期间存在,方法结束就没了。
常见错误现象:Variable might not have been initialized——这是局部变量未赋值就使用导致的编译错误;而成员变量即使不显式初始化,也会有默认值(如int是0,Object是null)。
- 成员变量可加访问修饰符(
private/protected/public)和static/final等修饰 - 局部变量不能用
private、public、protected、static修饰(但可以用final) - 局部变量在栈上分配,成员变量在堆上(属于对象实例的一部分)
同名变量时的优先级与遮蔽(Shadowing)规则
当局部变量和成员变量重名,方法内直接写变量名会访问局部变量,成员变量被“遮蔽”。想访问被遮蔽的成员变量,得用this.变量名。
示例:
立即学习“Java免费学习笔记(深入)”;
class Example {
private int x = 10;
void method() {
int x = 20; // 局部变量x遮蔽了成员变量x
System.out.println(x); // 输出20
System.out.println(this.x); // 输出10
}
}
- 构造器参数常与成员变量同名,所以几乎总要用
this.x = x来赋值 - lambda表达式里引用的局部变量必须是
final或“事实上final”(Java 8+),但引用成员变量无此限制 - 静态方法里不能直接访问非
static成员变量,因为此时可能还没有对象实例
线程安全与并发场景下的实际影响
局部变量天然线程安全——每个线程调用方法都会获得自己的一份副本;成员变量则可能被多个线程共享,若没同步机制,容易出问题。
- 不要为了“省事”把临时计算结果存到成员变量里供多个方法共用,尤其在Servlet、Spring Bean这类可能被多线程复用的对象中
- 局部变量适合放中间状态(如循环计数器、临时字符串拼接结果);成员变量适合存对象的固有状态(如
User.name、Connection.url) - 如果某个“局部变量”在方法里反复创建大对象(如
new HashMap()),又频繁调用该方法,可能引发GC压力——这时可考虑提升为成员变量并复用(注意线程安全)
编译期检查差异:为什么局部变量必须显式初始化
Java编译器对局部变量做“确定性赋值检查(definite assignment)”,要求所有执行路径都必须给它赋过值才能读取;而成员变量由JVM保证初始化,默认值写死在字节码规范里。
典型报错:The local variable xxx may not have been initialized,哪怕你写了if (true) { x = 1; }也不行——编译器不分析逻辑恒真,只按控制流图判断可达性。
-
switch语句中漏写
default,且所有case都没赋值,就会触发该错误 - try-catch里在catch中赋值、try中读取,也会报错(因为try可能抛异常跳过赋值)
- 成员变量若声明为
final,则必须在声明时、构造器中或初始化块里完成赋值,否则编译失败










