变量的作用域是一个程序的重要组成部分。因为它决定了某个时候每个地方某个变量是否可访问。与 C/C++ 一样。静态语言的作用域在编译期就是确定的。而且独立于函数调用堆栈。
类 ( class ) 是 Java 程序的基本组成单元。而类又通过 包 ( package ) 来组织。因此 Java 中的作用域可以分为以下几个部分。
成员变量 ( 类级作用域 )
成员变量 就是 Java 中的变量 中所提到的 实例变量 。也就是说,成员变量 是定义在类中的,而又在任何方法之外的变量。
成员变量 在类的任何位置都可以直接访问。
比如下面这个类
JavaTester.java
public class JavaTester { // 所有在类中定义的非静态变量都是成员变量 int a; private String b void method1() {....} int method2() {....} char c; }
成员变量 可以在类的任何位置声明和定义,但必须位于方法之外。
成员变量 前的 访问修饰符 规则并不适用于在类的内部访问。
而在类的外部,成员变量的可见性( 可访问性 )则由访问修饰符决定。并且遵循以下规则
访问修饰符 | 包 | 子类 | 全局 |
---|---|---|---|
public | 是 | 是 | 是 |
protected | 是 | 是 | 否 |
private | 否 | 否 | 否 |
默认(无修饰符) | 是 | 否 | 否 |
局部变量 ( 方法级别作用域 )
在方法内部声明的变量,嗯,就是局部变量,只限于方法级别的作用域。也就是说,局部变量在方法外部是无法访问的。
JavaTester.java
public class JavaTester { void run() { // 局部变量 ( 方法级别作用域 ) int x; } }
注意:当方法执行结束,局部变量也会随之销毁。也就是说在方法外部,局部变量就不存在了。
下面是方法作用域的另一个范例,在这个实例中,变量 x
是方法的一个参数。
public class JavaTester { private int x; public void setX(int x) { this.x = x; } }
上面这段代码中,我们使用 this
关键字来区分局部变量和实例变量。
作为一个练习,你能说出下面程序的执行结果吗?
JavaTester.java
public class JavaTester { static int x = 11; private int y = 33; public void run(int x) { JavaTester t = new JavaTester(); this.x = 22; y = 44; System.out.println("Test.x: " + JavaTester.x); System.out.println("t.x: " + t.x); System.out.println("t.y: " + t.y); System.out.println("y: " + y); } public static void main(String args[]) { JavaTester t = new JavaTester(); t.run(5); } }
我们运行下上面的代码,看看输出结果如何
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester Test.x: 22 t.x: 22 t.y: 33 y: 44
循环变量 ( 块级作用域 )
大括号 {
和 }
内声明的变量的作用域仅限于该大括号 {}
内部。
例如下面的代码,变量 x
的作用域仅限于离它最近的大括号 {}
JavaTester.java
public class JavaTester { public static void main(String args[]) { { // 变量 x 的作用域仅限于离它最近的大括号 int x = 10; System.out.println(x); } // 如果你把下一行代码的注释去掉,会抛出一个异常 // System.out.println(x); } }
编译运行以上代码,输出结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester 10
如果把最后一行的注释去掉,则会发生编译错误
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester JavaTester.java:13: 错误: 找不到符号 System.out.println(x); ^ 符号: 变量 x 位置: 类 JavaTester 1 个错误
循环中的作用域
循环中声明的变量的作用域范围仅限于该循环的大括号 {}
。
例如下面的代码,变量 x
的作用域仅限于 for
循环中的大括号。
JavaTester.java
public class JavaTester { public static void main(String args[]) { for (int x = 0; x < 4; x++) { System.out.println(x); } // 抛出错误 // System.out.println(x); } }
运行结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester 0 1 2 3
如果把注释去掉,编译时会产生一个错误
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester JavaTester.java:11: 错误: 找不到符号 System.out.println(x); ^ 符号: 变量 x 位置: 类 JavaTester 1 个错误
如何修复这个错误 ?
最佳的办法时把变量 x
的声明移到 for
循环之外,就像下面这样
public class JavaTester { public static void main(String args[]) { int x = 0; for (; x < 4; x++) { System.out.println(x); } // 抛出错误 System.out.println(x); } }
编译运行上面这段代码,输出结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester 0 1 2 3 4
但是,Java 中的 for
循环有一个诡异的地方,看看下面的代码,你知道输出结果是什么吗?
public class JavaTester { public static void main(String args[]) { int x = 0; for (int x = 0; x < 4; x++) { System.out.println(x); } // 抛出错误 System.out.println(x); } }
如果你熟悉 C/C++ ,你一定会感到非常诧异,因为编译的时候报错了
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester JavaTester.java:6: 错误: 已在方法 main(String[])中定义了变量 x for (int x = 0; x < 4; x++) ^ 1 个错误
同样的,你能猜出下面的代码输出结果是什么吗?
public class JavaTester { public static void main(String args[]) { { int x = 5; { int x = 10; System.out.println(x); } } } }
运行结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester JavaTester.java:8: 错误: 已在方法 main(String[])中定义了变量 x int x = 10; ^ 1 个错误
是不是很郁闷。我们总结下 Java 中的作用域的知识点:
- 通常来说,Java 中的作用域由花括号
{}
来界定。 - 在同一个花括号范围之内,只要定义了一个变量,就可以在该定义之后访问该变量。而且,一个变量可以在定义之后的任何子花括号作用域内访问。
- 在类中定义的且在方法之外定义的变量,俗称实例变量,可以在类中的任何方法中访问。
- 当一个访问内部定义的局部变量和类的实例变量同名时,局部变量会覆盖实例变量,如果要引用实例变量,可以使用
this
关键字。 - 如果要在循环之后再访问一个变量,则必须在循环之前就定义该变量。