Java中的默认值和初始化

94

根据我的参考资料,原始类型具有默认值,而对象为null。我测试了一段代码。

public class Main {
    public static void main(String[] args) {
        int a;
        System.out.println(a);
    }
}

当使用代码 System.out.println(a); 时,会出现关于变量 a 的错误提示,指出变量a可能尚未初始化。但是,在给定的参考中,整数将具有0作为默认值。然而,通过以下给定的代码,实际上将打印出0

public class Main {
    static int a;
    public static void main(String[] args) {
        System.out.println(a);
    }
}

第一段代码可能会出现什么问题?类变量与局部变量的行为有何不同?


第二个输出的是什么?0吗? - porfiriopartida
@porfiriopartida 是的,在第二段代码中打印了0。 - Michael 'Maik' Ardan
静态上下文在应用程序启动时初始化,它会开始一个默认值。例如,int类型的默认值为0,String类型的默认值为空字符串,布尔类型的默认值为false等等。但是对于局部或实例变量,您可以控制它们;您无法保证何时或谁将访问静态属性。 - porfiriopartida
1
这可能是 Variable might not have been initialized error 的重复。 - jww
在这里添加Java 8规范链接:https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.5 - mortalis
11个回答

94
在第一个代码示例中,a是一个main方法的局部变量。方法局部变量需要在使用之前进行初始化。
在第二个代码示例中,a是类成员变量,因此它将被初始化为默认值。

我注意到方法局部数组变量在使用时不会出现“可能未初始化变量a”的警告。是因为方法变量声明为数组时会被默认初始化,而独立变量则不会吗? - Androidcoder
在这种情况下,默认值是什么?是某种类型的空值吗? - Peter Mortensen
1
@PeterMortensen 原始数据类型不使用 null 作为默认值。它们使用一个合法的原始类型值,例如 int 的默认值是 0。完整的细节可以在这里找到:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html - Juned Ahsan

73

仔细阅读参考文献

默认值

声明变量时不总是需要赋值。 未初始化的变量将由编译器设置为合理的默认值。一般来说,这个默认值将是零或 null,具体取决于数据类型。然而,依赖这样的默认值通常被认为是不好的编程风格。

以下图表总结了上述数据类型的默认值。

...

局部变量略有不同;编译器从不向未初始化的局部变量分配默认值。如果您无法在声明时初始化本地变量,请确保在尝试使用它之前给它赋值。访问未初始化的局部变量将导致编译时错误。


3
我也读到了这个,但是关于数组呢?在局部作用域中使用的数组被赋予默认值,但文档中没有提到。 - Yahya Uddin
1
这里有一个很好的关于本地上下文中数组的解释,被接受为答案:https://dev59.com/SmYr5IYBdhLWcg3wpLv9 - gcbound

23

这些是涉及的主要因素:

  1. 成员变量(默认OK)
  2. 静态变量(默认OK)
  3. final成员变量(未初始化,必须在构造函数中设置)
  4. final静态变量(未初始化,必须在static块{}中设置)
  5. 局部变量(未初始化)

注1:您必须在每个实现的构造函数中初始化final成员变量!

注2:您必须在构造函数本身的块内初始化final成员变量,而不能调用另一个初始化它们的方法。例如,以下内容无效:

private final int memberVar;

public Foo() {
    // Invalid initialization of a final member
    init();
}

private void init() {
    memberVar = 10;
}

注意3: 在Java中,数组即使存储原始类型,也是对象。

注意4: 当您初始化一个数组时,无论它是成员数组还是本地数组,所有项目都将被设置为默认值。

我附上了一个代码示例,展示了上述情况:

public class Foo {
    // Static and member variables are initialized to default values

    // Primitives
    private int a; // Default 0
    private static int b; // Default 0

    // Objects
    private Object c; // Default NULL
    private static Object d; // Default NULL

    // Arrays (note: they are objects too, even if they store primitives)
    private int[] e; // Default NULL
    private static int[] f; // Default NULL

    // What if declared as final?

    // Primitives
    private final int g; // Not initialized. MUST set in the constructor
    private final static int h; // Not initialized. MUST set in a static {}

    // Objects
    private final Object i; // Not initialized. MUST set in constructor
    private final static Object j; // Not initialized. MUST set in a static {}

    // Arrays
    private final int[] k; // Not initialized. MUST set in constructor
    private final static int[] l; // Not initialized. MUST set in a static {}

    // Initialize final statics
    static {
        h = 5;
        j = new Object();
        l = new int[5]; // Elements of l are initialized to 0
    }

    // Initialize final member variables
    public Foo() {
        g = 10;
        i = new Object();
        k = new int[10]; // Elements of k are initialized to 0
    }

    // A second example constructor
    // You have to initialize final member variables to every constructor!
    public Foo(boolean aBoolean) {
        g = 15;
        i = new Object();
        k = new int[15]; // Elements of k are initialized to 0
    }

    public static void main(String[] args) {
        // Local variables are not initialized
        int m; // Not initialized
        Object n; // Not initialized
        int[] o; // Not initialized

        // We must initialize them before use
        m = 20;
        n = new Object();
        o = new int[20]; // Elements of o are initialized to 0
    }
}

10

在声明基本类型的值时需要注意以下几点:

  1. 在方法内声明的变量不会被赋予默认值。
  2. 在实例变量或静态变量中声明的变量将被赋予默认值,即0。

因此,在您的代码中:

public class Main {
    int instanceVariable;
    static int staticVariable;
    public static void main(String[] args) {
        Main mainInstance = new Main()
        int localVariable;
        int localVariableTwo = 2;
        System.out.println(mainInstance.instanceVariable);
        System.out.println(staticVariable);
       // System.out.println(localVariable); // Will throw a compilation error
        System.out.println(localVariableTwo);
    }
}

3

是的,实例变量将被初始化为默认值。对于局部变量,您需要在使用之前进行初始化:

public class Main {

    int instaceVariable; // An instance variable will be initialized to the default value

    public static void main(String[] args) {
        int localVariable = 0; // A local variable needs to be initialized before use
    }
}

2
本地变量不会获得默认值。未经任何方式赋值,它们的初始值将未定义。在使用本地变量之前,必须对其进行初始化。
在类级别(作为成员,即字段)和方法级别声明变量时存在很大的区别。
如果您在类级别声明字段,则它们根据类型获取默认值。如果您在方法级别或作为块(表示任何代码内部 {})中声明变量,则不会获取任何值,并且在某种程度上保持未定义状态,直到它们获得一些起始值,即分配给它们一些值。

1

所有成员变量必须加载到堆中,因此在创建类的实例时必须使用默认值进行初始化。

对于局部变量,它们不会加载到堆中。它们存储在栈上,直到被使用。这是在Java 7之前的情况,因此我们需要明确地对它们进行初始化。


1
在Java中,仅适用于类成员的实例变量具有默认初始化。对于局部变量不适用。

0
我编写了以下函数来返回原始或数字的默认表示0或false:
/**
 * Retrieves the default value 0 / false for any primitive representative or
 * {@link Number} type.
 *
 * @param type
 *
 * @return
 */
@SuppressWarnings("unchecked")
public static <T> T getDefault(final Class<T> type)
{
    if (type.equals(Long.class) || type.equals(Long.TYPE))
        return (T) new Long(0);
    else if (type.equals(Integer.class) || type.equals(Integer.TYPE))
        return (T) new Integer(0);
    else if (type.equals(Double.class) || type.equals(Double.TYPE))
        return (T) new Double(0);
    else if (type.equals(Float.class) || type.equals(Float.TYPE))
        return (T) new Float(0);
    else if (type.equals(Short.class) || type.equals(Short.TYPE))
        return (T) new Short((short) 0);
    else if (type.equals(Byte.class) || type.equals(Byte.TYPE))
        return (T) new Byte((byte) 0);
    else if (type.equals(Character.class) || type.equals(Character.TYPE))
        return (T) new Character((char) 0);
    else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE))
        return (T) new Boolean(false);
    else if (type.equals(BigDecimal.class))
        return (T) BigDecimal.ZERO;
    else if (type.equals(BigInteger.class))
        return (T) BigInteger.ZERO;
    else if (type.equals(AtomicInteger.class))
        return (T) new AtomicInteger();
    else if (type.equals(AtomicLong.class))
        return (T) new AtomicLong();
    else if (type.equals(DoubleAdder.class))
        return (T) new DoubleAdder();
    else
        return null;
}

在 Hibernate ORM 投影查询中,当底层 SQL 查询返回 null 而不是 0 时,我会使用它。

/**
 * Retrieves the unique result or zero, <code>false</code> if it is
 * <code>null</code> and represents a number
 *
 * @param criteria
 *
 * @return zero if result is <code>null</code>
 */
public static <T> T getUniqueResultDefault(final Class<T> type, final Criteria criteria)
{
    final T result = (T) criteria.uniqueResult();

    if (result != null)
        return result;
    else
        return Utils.getDefault(type);
}

Java 中许多不必要的复杂特性使其难以使用。为什么实例变量被初始化为默认值 0 而本地变量却没有这样做是不合逻辑的。同样,枚举为什么没有内置的标志支持和许多其他选项也是如此。与 C# 相比,Java Lambda 是一场噩梦,并且不允许类扩展方法也是一个大问题。
Java 生态系统找各种借口解释为什么不可能改进,但我作为用户/开发人员并不关心他们的借口。我需要简便的方法,如果他们不修复这些问题,他们将在未来失去市场份额,因为 C# 和其他语言正在等待着让开发人员的生活更加简单。过去 10 年中,由于我每天都在使用 Java 工作,看到它的下滑令人沮丧。

0

我已经在代码之间添加了注释:

public class Main {
    public static void main(String[] args) {
         // This is local variable. 
         // Look! you have declared it within the "body of a method".
         // Local variables must be initialized.
        int a;
        System.out.println(a);
    }
}

现在是下一个

public class Main {
    //This is NOT a local variable. It is NOT in a method body!
    //Look! you have defined it as class member ( or a property). 
    //Java is more generous with local variables and initiates them.
    //(ex: int with 0, boolean with false, String(or any object) with null, ...)
    static int a;
    public static void main(String[] args) {
        System.out.println(a);
   }

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接