Java 程序中定义的实体(包、类型、方法、变量等)具有names。这些用于引用程序其他部分中的那些实体。
Java 语言为每个名称定义了一个scope
声明的范围是程序所在的区域
声明所声明的实体可以使用
简单的名称,只要它是可见的(§6.4.1)。
换句话说,作用域是一个编译时概念,它决定了一个名字可以用来指代某个程序实体。
您发布的程序有多个声明。让我们从
private String[] elements;
private int capacity;
这些是field 声明,也称为instance variables,即。在class body 中声明的成员类型。 Java 语言规范声明
成员 m 的声明范围在 a 中声明或继承
类类型 C (§8.1.6) 是 C 的整个主体,包括任何嵌套
类型声明。
这意味着您可以在StringArray 的正文中使用名称elements 和capacity 来引用这些字段。
构造函数主体中的前两个语句
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
其实是local variable declaration statements
局部变量声明语句声明一个或多个局部变量名称。
这两个语句在您的程序中引入了两个新名称。碰巧这些名称与您的字段相同。在您的示例中,capacity 的局部变量声明还包含一个初始化该局部变量的初始化程序,而不是同名字段。您的名为capacity 的字段被初始化为default value 的类型,即。值0。
elements 的情况略有不同。局部变量声明语句引入了一个新名字,但是assignment expression呢?
elements = new String[capacity];
elements 指的是什么实体?
范围状态的规则
局部变量在块中声明的范围(第 14.4 节)是
声明出现的块的其余部分,从它的开始
自己的初始化程序,并在右侧包括任何进一步的声明符
局部变量声明语句。
在这种情况下,块是构造函数体。但是构造函数主体是StringArray 主体的一部分,这意味着字段名称也在范围内。那么Java如何确定您指的是什么?
Java 引入了Shadowing 的概念来消除歧义。
某些声明可能在其部分范围内被另一个声明所覆盖
相同名称的声明,在这种情况下,不能使用简单名称
用于引用声明的实体。
(一个简单的名字是一个单一的标识符,例如elements。)
文档还说明
局部变量的声明d或名为n的异常参数
shadows,在d 的范围内,(a) 任何其他的声明
名为n 的字段在d 出现的位置范围内,以及(b)
任何其他名为 n 的变量的声明在范围内
d 出现但未在最里面的类中声明的点
其中声明了d。
这意味着名为elements 的局部变量优先于名为elements 的字段。表达式
elements = new String[capacity];
因此正在初始化局部变量,而不是字段。该字段被初始化为其类型的default value,即。值null。
在您的方法getCapacity 和getElements 中,您在它们各自的return 语句中使用的名称指的是这些字段,因为它们的声明是程序中该特定点范围内的唯一声明。由于字段已初始化为0 和null,因此这些是返回的值。
解决方案是完全摆脱局部变量声明,因此让名称引用实例变量,正如您最初想要的那样。例如
public StringArray() {
capacity = 10;
elements = new String[capacity];
}
带构造函数参数的阴影
与上述情况类似,您可能有formal (constructor or method) parameters 阴影字段具有相同的名称。例如
public StringArray(int capacity) {
capacity = 10;
}
阴影规则状态
一个名为n shadows 的字段或形参的声明d,
在d 的范围内,任何其他变量的声明
命名为 n 的名称在 d 出现的位置范围内。
在上面的示例中,构造函数参数capacity 的声明隐藏了实例变量的声明,也称为capacity。因此不可能用它的简单名称来引用实例变量。在这种情况下,我们需要使用它的qualified name 来引用它。
限定名称由名称、“.”组成。令牌和标识符。
在这种情况下,我们可以使用primary expression this 作为field access expression 的一部分来引用实例变量。例如
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
}
每个kind of variable、方法和类型都有阴影规则。
我的建议是尽可能使用唯一的名称,以完全避免这种行为。