为什么实例变量在java中有默认值?

为什么在类中声明的变量具有默认值,但在方法中声明的变量(称为“局部变量”)在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给出了可能尚未初始化的错误。

所有成员变量都必须加载到堆中,因此在创建类的实例时必须使用默认值初始化它们。 在局部变量的情况下,它们不会被加载到堆中,它们被存储在堆栈中,直到它们在java 7之前被使用,因此我们需要显式初始化它们。 现在,“Java Hotspot Server Compiler”执行“转义分析”,并决定在堆栈而不是堆上分配一些变量。

局部变量初始化

在方法和块中声明的变量称为局部变量。 在方法调用中创建局部变量时,不会初始化它们。 因此,必须在使用之前显式初始化局部变量。 否则,编译器会在执行包含方法或块时将其标记为错误。

例:

 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) } } 

字段初始化

如果没有为实例或静态变量提供初始化,则无论是在声明时还是在初始化块中,都会使用其类型的默认值隐式初始化每次实例化类时,都会使用其类型的默认值初始化实例变量 ,即从类创建的每个对象。 首次加载类时,静态变量使用其类型的默认值进行初始化。

当局部变量在堆栈上分配时,局部变量的内存块在分配值时会被分配。

举个简单的例子

 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 + da; 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."":()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 "":()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; 赋值发生在隐式init函数Abc()

  int a = -111; 5: bipush -111 7: putfield #2 // Field a:I 

由于字段变量e未赋值任何值,如果是原始值则为0,如果是Object引用则为null

如果你看看doSomething()

  int a = 10; 0: bipush 10 

对于要使用的本地,在这种情况下需要将初始值推入堆栈10。 没有这个’push'[初始化]后续语句无法访问a的值(因为该值不在堆栈中)。 一旦将值推送到堆栈,其他操作(如iadd istore等)就会在堆栈上执行

下面的语句实际上在堆空间上创建了一个对象并调用了init方法。 这是未初始化的变量(如’e’)获取默认值的地方

  15: new #3 // class Abc 18: dup 

我将进一步的字节码比较留给你;)但我希望它很清楚

tl;博士:这或多或少是一种随意的选择

如果你问我,那么Java有实例变量的默认值是一个错误。 编译器应该强制程序员初始化它,就像局部变量的情况一样。

默认值背后的基本原理是安全性。 当一个对象被实例化时,将为该对象分配一块内存,其中包含实例变量指向的位置等.Java设计者认为用零和空值擦除这部分内存是个好主意。 这样,在分配对象之前,您将永远不会读取碰巧在那里的垃圾。 他们本可以强迫初始化; 选择没有任何根本性。 它可能使实现变得容易,并使Java的设计者有足够的意识。

在局部变量的情况下,设计者选择强制初始化(或者更准确地说,当仅声明局部变量时,他们选择不进行任何类型的初始化,因此编译器的最符合逻辑的行为是强制初始化使用前的变量)。