这是一个典型的问题和类似问题的答案,问题是一个阴影的结果。
我在类中定义了两个字段,一个是引用类型,一个是基元类型。在类的构造函数中,我尝试将它们初始化为一些自定义值。
当我稍后查询这些字段的值时,它们返回java的默认值, null
引用类型为0,基元类型为0。为什么会这样?
下面是一个可复制的示例:
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()
返回正确初始化的数组示例。
4条答案
按热度按时间j5fpnvbx1#
int capacity = 10;
在构造函数中声明一个局部变量capacity
阴影笼罩着整个班级。补救的办法是放下武器
int
:capacity = 10;
这将更改字段值。类中的另一个字段也是如此。你的ide没有警告你这个阴影吗?
pu82cl6c2#
另一个被广泛接受的约定是在类成员中添加一些前缀(或后缀,随您的喜好而定),以区别于局部变量。
例如,类成员
m_
前缀:大多数ide已经支持这种表示法,下面是eclipse
2exbekwf3#
在java/c/c++中使用变量有两个部分。一个是声明变量,另一个是使用变量(无论是赋值还是在计算中使用)。
声明变量时,必须声明其类型。所以你会用
使用变量时不必重新声明它:
如果变量在同一范围内,则会出现编译器错误;但是,正如您所发现的,如果变量在不同的范围内,您将屏蔽第一个声明。
2jcobegt4#
java程序中定义的实体(包、类型、方法、变量等)都有名称。它们用于指代程序其他部分中的实体。
java语言为每个名称定义了一个作用域
声明的范围是程序的一个区域,在该区域内,声明声明的实体可以使用一个简单的名称来引用,只要它是可见的(§6.4.1).
换句话说,作用域是一个编译时概念,它决定了在哪里可以使用一个名称来引用某个程序实体。
你发布的程序有多个声明。让我们从
这些是字段声明,也称为示例变量,即在类主体中声明的成员类型。java语言规范说明
成员声明的范围
m
在类类型中声明或由类类型继承C
(§8.1.6)是C
,包括任何嵌套类型声明。这意味着您可以使用这些名称
elements
以及capacity
在…体内StringArray
引用这些字段。构造函数体中的前两个语句
实际上是局部变量声明语句
局部变量声明语句声明一个或多个局部变量名。
这两个语句在程序中引入了两个新名称。碰巧那些名字和你的字段是一样的。在您的示例中,的局部变量声明
capacity
还包含一个初始化器,用于初始化该局部变量,而不是相同名称的字段。您的字段名为capacity
初始化为其类型的默认值,即0
.关于
elements
有点不同。局部变量声明语句引入了一个新名称,但是赋值表达式呢?什么是实体
elements
指的是什么?范围状态规则
块中局部变量声明的作用域(§14.4)是出现声明的块的其余部分,从它自己的初始值设定项开始,并在局部变量声明语句的右侧包括任何其他声明符。
在本例中,块是构造函数体。但是构造函数体是
StringArray
,这意味着字段名也在作用域中。那么java如何确定您所指的内容呢?java引入了阴影的概念来消除歧义。
某些声明可能在其部分范围内被另一个同名声明所掩盖,在这种情况下,不能使用简单名称来引用声明的实体。
(作为单一标识符的简单名称,例如。
elements
.)文件还指出
声明
d
局部变量或异常参数的n
阴影,在整个d
,(a)任何其他指定字段的声明n
在范围内d
和(b)任何其他名为n
在范围内d
发生但未在其中的最内层类中声明d
已声明。这意味着名为
elements
优先于名为elements
. 表达式因此正在初始化局部变量,而不是字段。字段初始化为其类型的默认值,即
null
.在你的方法里
getCapacity
以及getElements
,您在中使用的名称在各自的return
语句引用这些字段,因为它们的声明是程序中特定点作用域中唯一的声明。因为字段被初始化为0
以及null
,这些是返回的值。解决方案是完全去掉局部变量声明,因此让名称引用示例变量,就像您最初想要的那样。例如
带构造函数参数的阴影
与上面描述的情况类似,您可能有形式(构造函数或方法)参数来隐藏具有相同名称的字段。例如
阴影规则状态
声明
d
一个名为n
阴影,在整个d
,任何其他名为n
在范围内d
发生。在上面的示例中,构造函数参数的声明
capacity
隐藏示例变量(也称为capacity
. 因此,不可能用简单的名称引用示例变量。在这种情况下,我们需要用它的限定名来引用它。限定名由名称、“.”标记和标识符组成。
在这种情况下,我们可以使用主表达式
this
作为引用示例变量的字段访问表达式的一部分。例如每种变量、方法和类型都有阴影规则。
我的建议是尽可能使用唯一的名字,以避免这种行为。