为什么在类中声明的变量具有默认值,但在方法中声明的变量(称为“局部变量”)在Java中没有默认值?
例如
class abc
{
int a;
public static void main(String ss[])
{
int b;
abc aa=new abc();
System.out.println(aa.a);
System.out.println(b);
}
}
在上面的示例中,变量a
的默认值为0,但变量b
给出了可能尚未初始化的错误。
答案 0 :(得分:14)
所有成员变量都必须加载到堆中,因此在创建类的实例时必须使用默认值初始化它们。在局部变量的情况下,它们不会被加载到堆中,它们存储在堆栈中,直到它们在java 7之前被使用,因此我们需要显式初始化它们。 现在,“Java Hotspot Server Compiler”执行“转义分析”并决定在堆栈而不是堆上分配一些变量。
答案 1 :(得分:2)
局部变量初始化
在方法和块中声明的变量称为局部变量。在方法调用中创建局部变量时,不会初始化它们。因此,必须在使用之前显式初始化局部变量。否则,编译器会在执行包含方法或块时将其标记为错误。
示例:强>
public class SomeClassName{
public static void main(String args[]){
int total;
System.out.println("The incremented total is " + total + 3); //(1)
}
}
编译器抱怨在(1)的println语句中使用的局部变量total可能未初始化。 在使用之前初始化局部变量total解决了问题:
public class SomeClassName{
public static void main(String args[]){
int total = 45; //Local variable initialized with value 45 System.out.println("The incremented total is " + total+ 3); //(1)
}
}
字段初始化
如果没有为实例或静态变量提供初始化,无论是在声明时还是在初始化程序块中,它都是使用其类型的默认值隐式初始化 。 每次实例化类时,实例变量都会使用其类型的默认值进行初始化,即对于从类创建的每个对象。 首次加载类时,静态变量将使用其类型的默认值进行初始化。
答案 2 :(得分:1)
当局部变量在堆栈上分配时,局部变量的内存块在分配值时会被分配。
举个简单的例子
class Abc {
int i = -111;
int e;
int doSomething() {
int a = 10;
int b = a + i;
int c = b + 100;
Abc d = new Abc();
e = b + c + d.a;
return e + 1000;
}
}
和来自javap -c Abc
Compiled from "Abc.java"
class Abc {
int i;
int e;
Abc();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush -111
7: putfield #2 // Field i:I
10: return
int doSomething();
Code:
0: bipush 10
2: istore_1
3: iload_1
4: aload_0
5: getfield #2 // Field i:I
8: iadd
9: istore_2
10: iload_2
11: bipush 100
13: iadd
14: istore_3
15: new #3 // class Abc
18: dup
19: invokespecial #4 // Method "<init>":()V
22: astore 4
24: aload_0
25: iload_2
26: iload_3
27: iadd
28: aload 4
30: getfield #2 // Field i:I
33: iadd
34: putfield #5 // Field e:I
37: aload_0
38: getfield #5 // Field e:I
41: sipush 1000
44: iadd
45: ireturn
}
当一个方法被驱动时,分配了一个名为当前帧的堆栈中的内存空间
如果仔细观察甚至int a=-111;
赋值发生在隐式初始化函数Abc()
中!
int a = -111;
5: bipush -111
7: putfield #2 // Field a:I
由于字段变量e
未分配任何值,如果是原始值则为0,如果是对象引用则为null
如果你看doSomething()
int a = 10;
0: bipush 10
对于要使用的本地,在这种情况下需要将初始值推入堆栈10。没有这个'push'[初始化]后续语句无法访问a的值(因为该值不在堆栈中)。一旦将值推到堆栈,其他操作如iadd istore等就会在堆栈上执行
下面的语句实际上在堆空间上创建了一个对象并调用了init方法。这是未初始化的变量,如'e'获取默认值
15: new #3 // class Abc
18: dup
我将进一步的字节码比较留给你;)但我希望它很清楚
答案 3 :(得分:0)
tl; dr:这或多或少是一种随意的选择
如果你问我,Java有实例变量的默认值是一个错误。编译器应该强制程序员初始化它,就像局部变量的情况一样。
默认值背后的基本原理是安全性。当一个对象被实例化时,将为该对象分配一块内存,其中包含实例变量指向的位置等.Java设计者决定用零和空值擦除这部分内存是个好主意。这样,在分配对象之前,您永远不会读取碰巧在那里的垃圾。他们本可以强制初始化;选择没有任何根本性。它可能使事情易于实现,并使Java的设计者有足够的意识。
在局部变量的情况下,设计者选择强制初始化(或者更准确地说,当仅声明局部变量时,他们选择不进行任何类型的初始化,因此编译器的最符合逻辑的行为是在使用前强制初始化变量。)