当我在我的类的构造函数中声明和初始化它们时,为什么我的字段被初始化为 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?

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

问题描述

这是针对类似问题的规范问答,其中问题是阴影的结果.

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

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 主体中的名称 elementscapacity 来引用这些字段.

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.

elements 的情况略有不同.局部变量声明语句引入了一个新名称,但是 赋值表达式?

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];

elements 指的是什么实体?

范围状态的规则

块中局部变量声明的范围(第 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 引入了阴影的概念em> 来消除歧义.

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.

(简单名称是单个标识符,例如elements.)

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

文档还说明

局部变量或名为n的异常参数的声明dshadows,在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 的局部变量优先于名为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.

在您的方法 getCapacitygetElements 中,您在它们各自的 return 语句中使用的名称指的是这些字段,因为它们的声明是程序中那个特定点的唯一范围.由于字段被初始化为 0null,这些就是返回的值.

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,在 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 qualified name consists of a name, a "." token, and an identifier.

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

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
}

每个变量种类、方法和类型.

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天全站免登陆