Java 程序中定义的实体(包、类型、方法、变量等)有names http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.2。它们用于引用程序其他部分中的实体。
Java 语言定义了一个scope http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.3对于每个名字
The scope声明的区域是程序的区域,其中
声明所声明的实体可以使用
简单的名称,只要它是可见的(第 6.4.1 节)。
换句话说,scope是一个编译时概念,它确定名称可用于引用某些程序实体的位置。
您发布的程序有多个声明。让我们从
private String[] elements;
private int capacity;
这些都是field http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3声明,也称为实例变量 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.1.1, IE。在 a 中声明的成员类型类体 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.6。 Java 语言规范指出
成员声明的范围m
在 a 中声明或继承
班级类型C
(§8.1.6)是整个主体C
,包括任何嵌套的
类型声明。
这意味着您可以使用这些名称elements
and capacity
的体内StringArray
来引用这些字段。
构造函数主体中的前两个语句
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
实际上是局部变量声明语句 http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.4
A 局部变数声明语句声明一个或多个局部变量名。
这两条语句在您的程序中引入了两个新名称。碰巧这些名称与您的字段名称相同。在您的示例中,局部变量声明为capacity
还包含一个初始化程序初始化该局部变量,不是同名字段。您的字段名为capacity
被初始化为默认值 http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.5对于它的类型,即。价值0
.
案例为elements
有点不同。局部变量声明语句引入了新的名称,但是局部变量声明语句又如何呢?赋值表达式 http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26?
elements = new String[capacity];
实体是什么elements
指的是?
的规则scope state
一的范围局部变数块中的声明 (§14.4) 是
声明出现的块的其余部分,从其开始
自己的初始化程序,并在右侧包含任何进一步的声明符
局部变量声明语句。
在本例中,块是构造函数主体。但构造函数主体是主体的一部分StringArray
,这意味着字段名称也在范围内。那么Java如何确定你所指的内容呢?
Java引入了这个概念影子 http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.3消除歧义。
某些声明的部分范围可能会被另一个声明所遮蔽
声明相同的名称,在这种情况下不能使用简单的名称
用于引用声明的实体。
(a 简单的名字是单个标识符,例如elements
.)
该文档还指出
一份声明d
of a 局部变数或名为的异常参数n
shadows,在整个范围内d
, (a) 任何其他人的声明
字段命名n
在该点的范围内d
occurs,和(b)
任何其他名为的变量的声明n
范围内的
的点d
发生但未在最内部类中声明
其中d
被宣布。
这意味着名为的局部变量elements
优先于名为的字段elements
。表达方式
elements = new String[capacity];
因此初始化局部变量,而不是字段。该字段被初始化为默认值 http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.5对于它的类型,即。价值null
.
在你的方法中getCapacity
and getElements
,您在各自的名称中使用的名称return
语句引用这些字段,因为它们的声明是程序中该特定点的范围内的唯一声明。由于字段被初始化为0
and null
,这些是返回的值。
解决方案是完全摆脱局部变量声明,因此让名称引用实例变量,正如您最初想要的那样。例如
public StringArray() {
capacity = 10;
elements = new String[capacity];
}
使用构造函数参数进行遮蔽
与上述情况类似,您可能有形式(构造函数或方法)参数 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.1遮蔽同名字段。例如
public StringArray(int capacity) {
capacity = 10;
}
影子规则状态
一份声明d
名为的字段或形式参数n
阴影,
整个范围内d
,任何其他变量的声明
命名的n
在该点的范围内d
occurs.
在上面的例子中,构造函数参数的声明capacity
隐藏实例变量的声明,也命名为capacity
。因此不可能用简单的名称来引用实例变量。在这种情况下,我们需要用它的引用来引用它合格名称 http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5.6.2.
限定名称由名称、“.”组成。令牌和标识符。
在这种情况下,我们可以使用主要表达this http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.3作为一个字段访问表达式 http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.11.1来引用实例变量。例如
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
}
有影子每个规则变量的种类 http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.3、方法和类型。
我的建议是您尽可能使用唯一的名称,以便完全避免这种行为。