问题:
(1)这里我们来探讨一下成员变量和局部变量的默认初始值问题,当成员变量和局部变量声明之后不进行初始化赋值操作,就在接下去的代码中使用它们,比如说输出它们,会出现一个什么问题?
(2)如果成员变量被final关键字所修饰,只声明而不显式初始化赋值,JVM依然会帮成员变量隐式初始化吗?
前提:
在解决问题之前,我们先来理清楚一些问题,什么是成员变量?什么是局部变量?
了解了这些概念的前提下,我们就可以看以下的代码了。
分别对成员变量和局部变量进行只声明而不初始化的测试
代码(一):
成员变量测试
public class MemberVar {
//静态变量只声明而没有赋值,JVM会对静态变量进行隐式的默认值初始化
static int sI;
static Integer sInteger;
static char sC;
static String sString;
static boolean sB;
static Boolean sBoolean;
//实例变量只声明而没有初始化,JVM也会对实例变量进行隐式的默认值初始化
int oInt;
char oChar;
public static void main(String[] args) {
System.out.println("静态变量 - int的基本类型和包装类型:");
System.out.println("sI="+sI);
System.out.println("sInteger="+sInteger);
System.out.println("静态变量 - char型和String型:");
System.out.println("sC="+sC);
System.out.println("sString="+sString);
System.out.println("静态变量 - boolean型和Boolean型:");
System.out.println("sB="+sB);
System.out.println("sBoolean="+sBoolean);
System.out.println("实例变量 - int,char的基本类型");
MemberVar memberVar = new MemberVar();
System.out.println(memberVar.oInt);
System.out.println(memberVar.oChar);
}
}
输出结果:
静态变量 - int的基本类型和包装类型:
sI=0
sInteger=null
静态变量 - char型和String型:
sC='\u0000' (结果无法复制,实际效果是一个小方块空格)
sString=null
静态变量 - boolean型和Boolean型:
sB=false
sBoolean=null
实例变量 - int,char的基本类型
0
'\u0000'
代码(二):
局部变量测试
package variable;
public class LocalVar {
public static void main(String[] args) {
//基本类型
int a = 0;
int b = 0;
int c; //局部变量只声明没初始化
//引用类型
Integer i = null;
Integer j = null;
Integer k ; //局部变量只声明没初始化
System.out.println(a);
System.out.println(b);
//System.out.println(c); //编译器报错,The local variable c may not have been initialized
System.out.println(i);
System.out.println(j);
//System.out.println(k); //编译器报错,The local variable c may not have been initialized
}
}
结论:
注意:
局部变量没有初始化情况下,是不允许被调用的,因为该变量没有指向任何值。但是也分为两种情况
假如成员变量和局部变量被final所修饰,默认值赋值的情况会是怎么样?
public class FinalVar {
final static int a = 0;
final static Integer b = 0;
//final static int c; //只声明而不赋值,就报错,The blank final field c may not have been initialized
//final static Integer d;
final int i = 0;
final Integer j = 0;
//final int k; //只声明而不赋值,就报错,The blank final field k may not have been initialized
//final Integer l;
public static void main(String[] args) {
final int localA; //局部变量只声明没有初始化,在没有使用的情况下是不会报错的,与final无关。
final Integer localB; //与final成员变量不同,编译器允许局部变量延缓进行初始化,但是final局部变量也只能被赋值一次
localA = 0;
//localA = 1; //The final local variable localA may already have been assigned,final变量不允许第二次赋值
}
}
结论
通过jd-gui工具对编译后的class文件进行反编译,查看区别
源代码:
public class FinalVar {
final static int a = 0;
final static Integer b = 0;
static int c;
static Integer d;
final int i = 0;
final Integer j = 0;
int k;
Integer l;
public static void main(String[] args) {
final int localA;
localA = 0;
final Integer localB;
int localC;
localC = 0;
Integer localD;
}
}
编译后的代码:
public class FinalVar
{
static final int a = 0;
static final Integer b = Integer.valueOf(0);
static int c;
static Integer d;
final int i = 0;
final Integer j = Integer.valueOf(0);
int k;
Integer l;
public static void main(String[] args)
{
int localA = 0;
int localC = 0
}
}
结论:
final
修饰,在编译期间,是会被编译器当做垃圾代码删除掉的,如localB
,localD
两个局部变量的声明。int localA; localA = 0
会别优化成一句话int localA = 0
(所以我们可以知道为什么局部变量可以延迟初始化而成员变量不允许了。因为成员变量未初始化,虚拟机也会为其隐式初始化,而编译器也是不会认为未初始化且未使用的成员变量是垃圾代码。这一点则不同于局部变量。举个例子,一个final的成员变量没有使用构造器初始化,也没有在声明时用赋值语句初始化,在之后的代码中,我们也不为它进行初始化。该成员变量因为是final变量,所以虚拟机也不会为其初始化,同时编译器也不会删除成员变量的声明。那么这么一个未存储任何值的全局成员变量在类加载的时候会被分配到内存中。这就有点奇怪了)
final
修饰的变量,无论是成员变量还是局部变量,都是不可变的变量,都必须显式地进行初始化操作,且只能被赋值一次。补充:
DataType | Default Vaule |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | ‘\u0000’ |
boolean | false |
引用型变量 | null |