首页 / Java 入门教程 / 成员变量与局部变量

Java 入门教程

成员变量与局部变量

本教程共 100 篇 · 第 31 篇 · 更新于 2026-08-05 · 约 5 分钟阅读

JavaJava 入门教程成员变量局部变量作用域变量默认值

31. 成员变量与局部变量

本节目标:分清写在类里的成员变量和写在方法里的局部变量,说清它们在默认值、作用域、生命周期上的差别,并能看懂相关的编译报错。

变量放的位置不同,脾气差很多。Java 主要区分两类:成员变量(写在类里、方法外)和局部变量(写在方法或代码块里)。

搞混这两个,会在最基础的地方卡住。下面一项项拆。

成员变量:属于对象

写在类里面、方法外面的变量叫成员变量,也叫字段。它跟着对象走,每 new 一个对象就多一份。

public class Person {
    String name;     // 成员变量
    int age;         // 成员变量

    void intro() {
        System.out.println(name + "," + age + " 岁");
    }
}
```bash

类里所有方法都能直接用它,不用当参数传来传去。这是成员变量最实用的地方——它是这个对象内部的"公共数据区"

上一章说过,成员变量有默认值:数字是 0,布尔是 false,引用是 null。不赋值也能直接读,不会报错。

> [!NOTE]
> 成员变量还细分为实例变量和静态变量。上面这种不带 static 的是**实例变量**,每个对象一份;加了 static 的叫**静态变量**,全类共用一份。static 留到第 35 章讲,本章说的成员变量都指实例变量。

## 局部变量:属于方法

写在方法内部、方法参数位置、或者某个代码块里的变量,都叫局部变量。它只在那一小块范围里活着。

```java
public class Demo {
    public static void main(String[] args) {   // args 也是局部变量(参数)
        int x = 10;                // 局部变量,整个 main 可见
        if (x > 5) {
            int y = 20;            // 局部变量,只在 if 的花括号里活
            System.out.println(y);
        }
        // System.out.println(y);  // 这里 y 已经不存在了
    }
}

把注释那行放开,编译直接失败:

error: cannot find symbol
        System.out.println(y);
                           ^
  symbol:   variable y
  location: class Demo
```bash

"找不到符号"不是说你打错字,而是**这个名字在当前位置根本不存在**。花括号一闭合,里面声明的变量就注销了。

## 局部变量为什么必须先赋值

这是两类变量最扎眼的差别:局部变量**没有默认值**。

```java
public class Demo {
    public static void main(String[] args) {
        int x;
        System.out.println(x);   // 编译不通过
    }
}

报错长这样:

error: variable x might not have been initialized
        System.out.println(x);
                           ^
```java

注意措辞是 **might not**(可能没有),不是"一定没有"。编译器做的是保守的流程分析,只要存在一条路径可能读到未赋值的变量,它就拦。

下面这种写法就能通过,因为两条分支都赋了值:

```java
int x;
if (args.length > 0) {
    x = 1;
} else {
    x = 2;
}
System.out.println(x);   // 编译器确认所有路径都赋过值,放行

为什么成员变量能享受默认值、局部变量不行?因为成员变量属于对象,对象在堆上创建时有统一的清零动作;局部变量在栈帧上分配,进方法就有、出方法就没,为了性能不做清零。编译器强制你赋值,等于用编译期检查换掉了运行期的一整类”读到脏数据”的 bug。

Tip

这个报错对初学者是好事。C 语言里读未初始化的局部变量不报错,拿到的是内存里的随机值,问题能潜伏很久。Java 直接在编译期堵死。

默认值速查

类型成员变量默认值局部变量
byte / short / int / long0无默认值,必须显式赋值
float / double0.0无默认值,必须显式赋值
char\u0000无默认值,必须显式赋值
booleanfalse无默认值,必须显式赋值
引用类型null无默认值,必须显式赋值

五点区别总表

对比项成员变量局部变量
定义位置类里、方法外方法内 / 参数 / 代码块内
默认值有,按类型自动初始化无,用之前必须赋值
作用范围整个类的所有实例方法声明它的那对花括号内
生命周期随对象创建而生,对象被回收才亡进块而生,出块即亡
内存位置堆(跟着对象)栈(方法栈帧里)
能否加访问修饰符能,如 private / public不能,只能加 final

最后一行容易被忽略:给局部变量写 private int x; 会直接编译报错,因为访问控制只对类的成员有意义。

同名时:就近原则

局部变量和成员变量重名是允许的,方法里默认用离得近的那个

public class Demo {
    int num = 100;                     // 成员变量

    void test() {
        int num = 200;                 // 局部变量,同名,遮蔽了成员变量
        System.out.println(num);       // 200,局部优先
        System.out.println(this.num);  // 100,加 this 才是成员变量
    }
}
```java

这种现象叫**遮蔽**。想在方法里明确指成员变量,前面加 `this.`,下一章会专门讲。

同一个方法里的两个局部变量则不能同名:

```java
void bad() {
    int x = 1;
    int x = 2;   // 编译报错
}
error: variable x is already defined in method bad()
        int x = 2;
            ^
```bash

外层块的变量在内层块里也不能被重新声明,规则是一样的。

## 循环变量的作用域

for 语句头里声明的变量,只在循环体内有效:

```java
for (int i = 0; i < 3; i++) {
    System.out.println(i);
}
// System.out.println(i);   // 报错:cannot find symbol

想在循环结束后还用这个值,就把变量提到循环外面声明。

var 只能用在局部变量上

Java 10 引入了 var,让编译器根据右边表达式推断类型:

var name = "小明";      // 推断为 String
var count = 10;         // 推断为 int
var list = new int[3];  // 推断为 int[]
```java

**只服务于局部变量**。写到成员变量上会被当场拒绝:

```java
public class Demo {
    var name = "小明";   // 编译报错
}
error: 'var' is not allowed here
    var name = "小明";
    ^
```bash

原因是成员变量要对整个类甚至外部可见,类型必须写死才能保证接口稳定;局部变量只在几行内活动,推断出来不影响别人。

> [!WARNING]
> var 不是"动态类型"。`var count = 10;` 之后 count 就是铁打的 int,再赋一个字符串照样报错。它只是省了你敲类型名的力气,类型仍然在编译期定死。

## 常见疑问

**Q:成员变量能不能也在声明时就赋值?**

A:能,`int age = 18;` 完全合法,这叫显式初始化,会在对象创建时执行,覆盖掉默认值 0。

**Q:既然成员变量哪里都能用,为什么不把变量全写成成员变量?**

A:作用域越大越难维护。成员变量一多,任何方法都能改它,出了问题得把整个类翻一遍才知道是谁动的。能用局部变量解决的,就别提升成成员变量。

**Q:方法的参数到底算哪一类?**

A:算局部变量,作用域是整个方法体。所以它同样没有"默认值"一说——调用方必须传值进来,编译器会替你把关。

> [!TIP]
> 命名上尽量别让局部变量和成员变量撞名,撞了就得靠 this 区分,读代码的人也费劲。构造方法和 setter 是唯一的例外:那里参数名和字段名保持一致反而更清楚,代价是必须记得写 this。