当我在类的构造函数中声明并初始化它们时,为什么我的字段初始化为null或默认值为零? [英] Why are my fields initialized to null or to the default value of zero when I've declared and initialized them in my class' constructor?

查看:115
本文介绍了当我在类的构造函数中声明并初始化它们时,为什么我的字段初始化为null或默认值为零?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是针对类似问题的规范性问题和答案,其中问题是阴影的结果。

This is meant to be a canonical question and answer for similar questions where the issue is a result of shadowing.

我在我的类中定义了两个字段,一个是引用类型,另一个是基本类型。在类的构造函数中,我尝试将它们初始化为一些自定义值。

I've defined two fields in my class, one of a reference type and one of a primitive type. In the class' constructor, I try to initialize them to some custom values.

当我稍后查询这些字段的值时,它们会返回Java的默认值, null 表示引用类型,0表示基本类型。为什么会这样?

When I later query for those fields' values, they come back with Java's default values for them, null for the reference type and 0 for the primitive type. Why is this happening?

这是一个可重现的例子:

Here's a reproducible example:

public class Sample {
    public static void main(String[] args) throws Exception {
        StringArray array = new StringArray();
        System.out.println(array.getCapacity()); // prints 0
        System.out.println(array.getElements()); // prints null
    }
}

class StringArray {
    private String[] elements;
    private int capacity;
    public StringArray() {
        int capacity = 10;
        String[] elements;
        elements = new String[capacity];
    }
    public int getCapacity() {
        return capacity;
    }
    public String[] getElements() {
        return elements;
    }
}

我预计 getCapacity() 返回值10和 getElements()以返回正确初始化的数组实例。

I expected getCapacity() to return the value 10 and getElements() to return a properly initialized array instance.

推荐答案

Java程序中定义的实体(包,类型,方法,变量等)名称的。这些用于引用程序其他部分中的那些实体。

Entities (packages, types, methods, variables, etc.) defined in a Java program have names. These are used to refer to those entities in other parts of a program.

Java语言定义 范围

The Java language defines a scope for each name


声明的范围是程序的一个区域,其中
声明声明的实体可以使用
简单名称引用,只要它是可见的(§6.4.1)。

The scope of a declaration is the region of the program within which the entity declared by the declaration can be referred to using a simple name, provided it is visible (§6.4.1).

换句话说,范围是一个编译确定名称可用于引用某个程序实体的时间概念。

In other words, scope is a compile time concept that determines where a name can be used to refer to some program entity.

您发布的程序有多个声明。我们先来看看

The program you've posted has multiple declarations. Let's start with

private String[] elements;
private int capacity;

这些是字段声明,也称为实例变量 ,即。在类主体。 Java语言规范声明

These are field declarations, also called instance variables, ie. a type of member declared in a class body. The Java Language Specification states


声明成员声明的范围 m
类类型中继承或继承的 C (§8.1.6)是 C 的整个主体,包括任何嵌套的
类型声明。

The scope of a declaration of a member m declared in or inherited by a class type C (§8.1.6) is the entire body of C, including any nested type declarations.

这意味着你可以使用名称元素容量 StringArray 正文中引用这些字段。

This means you can use the names elements and capacity within the body of StringArray to refer to those fields.

构造函数体中的两个第一个语句

The two first statements in your constructor body

public StringArray() {
    int capacity = 10;
    String[] elements;
    elements = new String[capacity];
}

实际上是本地变量声明声明


局部变量声明语句声明一个或多个局部变量名称。

A local variable declaration statement declares one or more local variable names.

这两个语句在程序中引入了两个新名称。碰巧这些名字与你的字段相同。在您的示例中, capacity 的局部变量声明还包含一个初始化程序,初始化该局部变量,而不是同名字段。名为 capacity 的字段初始化为默认值的类型,即。值 0

Those two statements introduce two new names in your program. It just so happens that those names are the same as your fields'. In your example, the local variable declaration for capacity also contains an initializer which initializes that local variable, not the field of the same name. Your field named capacity is initialized to the default value for its type, ie. the value 0.

元素的情况是有点不同。本地变量声明语句引入了一个新名称,但是赋值表达式

The case for elements is a little different. The local variable declaration statement introduces a new name, but what about the assignment expression?

elements = new String[capacity];

什么实体元素是指?

范围州的规则


范围块中的局部变量声明(第14.4节)是声明出现的块的
其余部分,从其
自己的初始值设定项开始,包括任何进一步的声明符
中的局部变量声明语句。

The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.

在这种情况下,块是构造函数体。但是构造函数体是 StringArray 主体的一部分,这意味着字段名也在范围内。那么Java如何确定你所指的是什么?

The block, in this case, is the constructor body. But the constructor body is part of the body of StringArray, which means field names are also in scope. So how does Java determine what you're referring to?

Java引入了 阴影 消除歧义。

Java introduces the concept of Shadowing to disambiguate.


某些声明的部分范围可能会被另一个同名的
声明所掩盖,在这种情况下,一个简单的名称不能用
来引用声明的实体。

Some declarations may be shadowed in part of their scope by another declaration of the same name, in which case a simple name cannot be used to refer to the declared entity.

(一个简单名称是一个单个标识符,例如元素。)

(a simple name being a single identifier, eg. elements.)

文档还说明


本地变量的声明 d 或名为 n的异常参数
阴影,在 d 的范围内,(a)任何其他声明名为 n
字段位于 d 发生
的范围内,并且( b)
名为 n 的任何其他变量的声明,其范围为
d 发生但未在最内层的类
中声明,其中声明了 d

A declaration d of a local variable or exception parameter named n shadows, throughout the scope of d, (a) the declarations of any other fields named n that are in scope at the point where d occurs, and (b) the declarations of any other variables named n that are in scope at the point where d occurs but are not declared in the innermost class in which d is declared.

这意味着名为 elements 的局部变量优先于名为元素的字段。表达式

This means that the local variable named elements takes priority over the field named elements. The expression

elements = new String[capacity];

因此正在初始化局部变量,而不是字段。该字段初始化为默认值对于它的类型,即。值 null

is therefore initializing the local variable, not the field. The field is initialized to the default value for its type, ie. the value null.

在方法内 getCapacity getElements ,您在各自的 return 语句中使用的名称引用字段,因为它们的声明是唯一的在该计划的特定点范围内。由于字段初始化为 0 null ,因此这些是返回的值。

Inside your methods getCapacity and getElements, the names you use in the in their respective return statements refer to the fields since their declarations are the only ones in scope at that particular point in the program. Since the fields were initialized to 0 and null, those are the values returned.

解决方案是完全删除局部变量声明,因此名称引用实际变量,就像你最初想要的那样。例如

The solution is to get rid of the local variable declarations altogether and therefore have the names refer to the instance variables, as you originally wanted. For example

public StringArray() {
    capacity = 10;
    elements = new String[capacity];
}






构造函数参数阴影



与上述情况类似,您可能形式(构造函数或方法)参数遮蔽具有相同名称的字段。例如


Shadowing with constructor parameters

Similar to the situation described above, you may have formal (constructor or method) parameters shadowing fields with the same name. For example

public StringArray(int capacity) {
    capacity = 10; 
}

阴影规则状态


名为 n d $ c>阴影,整个 d 范围内的
,任何其他变量的声明
命名为 n d 发生的范围内。

A declaration d of a field or formal parameter named n shadows, throughout the scope of d, the declarations of any other variables named n that are in scope at the point where d occurs.

在上面的示例中,构造函数参数 capacity 的声明会影响名为 capacity 的实例变量的声明。因此,不可能用简单的名称来引用实例变量。在这种情况下,我们需要使用来引用它。限定名称

In the example above, the declaration of the constructor parameter capacity shadows the declaration of the instance variable also named capacity. It's therefore impossible to refer to the instance variable with its simple name. In such cases, we need to refer to it with its qualified name.


限定名称由名称a组成。令牌和标识符。

A qualified name consists of a name, a "." token, and an identifier.

在这种情况下,我们可以使用主要表达式 作为字段访问表达式以引用实例变量。例如

In this case, we can use the primary expression this as part of a field access expression to refer to the instance variable. For example

public StringArray(int capacity) {
    this.capacity = 10; // to initialize the field with the value 10
    // or
    this.capacity = capacity; // to initialize the field with the value of the constructor argument
}

有<每个种类的em>影子规则变量,方法和类型。

There are Shadowing rules for every kind of variable, method, and type.

我的建议是尽可能使用唯一的名称,以避免这种行为。

My recommendation is that you use unique names wherever possible so as to avoid the behavior altogether.

这篇关于当我在类的构造函数中声明并初始化它们时,为什么我的字段初始化为null或默认值为零?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆