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

时间:2013-08-14 08:12:33

标签: java class

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

4 个答案:

答案 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的设计者有足够的意识。

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