前言
在学习使用
Java
时,常常会把它和较为熟悉的C语言
进行对比。在对比过程中,发现关于变量部分有些概念非常容易混淆;特作此篇以总结。
基本用法
声明:这一点与 C语言
一样,所有的变量都需 先声明再使用 。1
//tepe - 类型
//identifier - 变量名
//可以使用逗号隔开来声明多个同类型变量
type identifier [ = value][, identifier [= value] ...] ;
具体例子(部分还包含初始化):
int a, b, c; // 声明三个 int 型整数:a、 b、c
int d = 3, e = 4, f = 5; // 声明三个整数并赋予初值
byte z = 22; // 声明并初始化 z
String s = "runoob"; // 声明并初始化字符串 s
double pi = 3.14159; // 声明了双精度浮点型变量 pi
char x = 'x'; // 声明变量 x 的值是字符 'x'。
分类
在 Java
中变量可分为以下几种:
局部变量(方法变量)
声明位置
局部变量可声明在 方法、构造方法或者 语句块 中。
public class Variable {
//构造方法
public void Variable() {
int a;
}
//方法
public void method(){
int b;
}
public static void main(String[] args) {
//语句块
for(int i=0;i<10;i++) {
int sum =3;
}
}
}
默认值
局部变量 没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。否则将会报错!
public class Variable {
public static void main(String[] args) {
int a;
//Error:The local variable a may not have been initialized
int test = a;
}
}
储存位置
还记得在 Java 关键字 - static中用到的这张图吗?
局部变量是在 虚拟机栈 上分配的。
生命周期
局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁。
即 局部变量的生命周期只在当前函数中。
作用域
前文 Variable 类
中的局部变量作用域如下:
a
:Variable()函数内b
:method()函数内c
:main()函数内
修饰符
由于其生命周期只在当前函数,因而再声明访问修饰符没有意义。
即 访问修饰符不能用于局部变量。仅有一个例外 — final
public class Variable {
public void Variable() {
final int a = 1;
}
}
实例变量(类中非静态变量)
声明位置
实例变量声明在一个 类中,但 在方法、构造方法和语句块之外。
这里我举个 Student类
的例子。
public class Student {
//实例变量
int ID;
int name;
int age;
public void Student(int ID,int name,int age){
this.ID = ID;
this.name = name;
this.age = age;
}
public static void main(String[] args) {
}
}
有趣的一点:书写的时候对类的成员变量和成员方法的相对顺序没有要求的。
class Demo {
int a = 0;
int sum(){
return a+b;
}
int b = 1;
}
初始化
实例变量初始化的方法有四种:
- 在声明时初始化
- 最常见的就是上文这种通过 构造函数 初始化。
- 当然我们也可以对外暴露一个 方法 ,这个方法用于设置某些实例变量。
- 还有一种做法,即通过 实例变量初始化器(又叫实例块) 来初始化较复杂的实例变量。
需要 注意 的是:对象代码块的执行是在对象执行构造函数前执行。
public class Variable {
//实例变量
int a;
Student m_student;
boolean tf;
//实例块,在创建的对象时都执行
{
a = 2;
tf = true;
}
public void output() {
System.out.println(a);
System.out.println(m_student);
System.out.println(tf);
}
}
结果
默认值
当一个对象被实例化之后,每个实例变量的值就跟着确定。
如果该变量没有被赋予初值呢?则会被 赋予默认值。
public class Variable {
//实例变量
int a;
Student m_student;
boolean tf;
public void output() {
System.out.println(a);
System.out.println(m_student);
System.out.println(tf);
}
}
我们在外部创建该对象,并调用其 output( )
方法打印默认值。
可以发现:
- 数值型变量的默认值是
0
- 布尔型变量的默认值是
false
- 引用类型变量的默认值是
null
存储位置
实例变量存储在 堆内存的对象中 ,所以也叫对象的特有数据。2这里的图片来自悟空悟不空。3
需要 注意 的是:
实例变量是多线程共享资源,要注意同步访问时可能出现的问题。4
生命周期
实例变量在对象创建的时候创建,在对象被销毁的时候销毁。
作用域
关于实例变量的作用域,首先应该明确的是仅有在生命周期内,才有可能存在作用域。而其他限制条件则取决于访问控制修饰符。
访问控制修饰符在 C语言
是没有的,因此若你也是从 C
转 Java
,须注意该部分内容。
修饰符
图片选自 菜鸟教程 5,这里仅展示访问控制修饰符
非访问控制修饰符
final
,最终修饰符,指定此变量的值不能变。static
(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类。transient
(过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量。volatile
(易失修饰符)指定该变量可以同时被几个线程控制和修改。6
访问方式
实例变量可以直接通过 变量名 访问。但在静态方法以及其他类中,就应该使用完全限定名:ClassName.VariableName
。
类变量(类中静态变量)
声明位置
类变量也称为静态变量,在类中以 static
关键字声明,但必须在方法之外。
public class Variable {
static int a;
static Student student;
static boolean tf;
public static void main(String[] args) {
}
}
初始化
由于是静态变量,因而我们一般不会在构造函数中对其进行初始化,常见的有:
- 在声明时初始化
- 对外暴露设置该静态变量的方法,完成初始化
- 通过类变量初始化器(静态块)完成初始化
public class Variable {
//声明时初始化
static int a = 1;
static Student student;
static boolean tf;
//通过对外暴露接口初始化
public void settf(boolean Sign){
tf = Sign;
}
//静态块完成初始化
static{
student = new Student();
}
public static void main(String[] args) {
}
}
默认值
默认值和实例变量相似:
- 数值型变量默认值是
0
- 布尔型默认值是
false
- 引用类型默认值是
null
存储位置
静态变量储存在 方法区 。更准确的做法可以参考下面这张图3。
注意方法区是 线程共享数据区。
所有由这类生成的对象都共用这个类变量,类装载时就分配存储空间。一个对象修改了变量,则所以对象中这个变量的值都会发生改变。4
生命周期
静态变量随类加载而存在,在 类卸载 时消失。
在类使用完之后,如果满足下面的情况,类就会被 卸载:
- 该类所有的实例都已经被回收,也就是
java
堆中不存在该类的任何实例。 - 加载该类的
ClassLoader
已经被回收。 - 该类对应的
java.lang.Class
对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,JVM
就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java
类的整个生命周期就结束了。7
作用域
与实例变量具有相似的可见性。
但为了对类的使用者可见,大多数静态变量声明为 public
类型。
修饰符
与实例变量的修饰符类似,这里不再枚举。
访问方式
静态变量可以通过 ClassName.VariableName
的方式访问。
总结
下文图片引用至8