当我在类的构造函数中声明并初始化字段时,为什么它们被初始化为 null 或默认值零?

2024-01-17

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


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

当我稍后查询这些字段的值时,它们会返回 Java 的默认值,null为引用类型,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()返回一个正确初始化的数组实例。


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、方法和类型。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我在类的构造函数中声明并初始化字段时,为什么它们被初始化为 null 或默认值零? 的相关文章

  • Eclipse 中的 Java 简单电子邮件程序

    我想制作一个简单的程序 您可以从其中发送电子邮件命令行 我找到了这个教程 http www tutorialspoint com java java sending email htm http www tutorialspoint com
  • 为什么在使用 repaint() 而不是使用 getParent().repaint() 时会出现此 Swing 错误?

    这个问题是基于我不久前在一个简单的 Swing 骰子程序中遇到的问题 我发布的原始问题是here https stackoverflow com questions 22306637 mystery concurrency componen
  • 如何根据 JComboBox 选择动态地将控件添加到表单?

    我正在尝试使用 Swing 创建一个简单的 java 表单 这个想法的基本思想是用户将在 JComboBox 中选择 0 到 5 然后 通过 ItemStateChanged 侦听器 将动态添加几个面板 每个面板包含 4 个控件 因此 如果
  • 如何在 Android 中恢复我的音频?

    我必须实现用于创建具有暂停和恢复状态的音频的应用程序 当我的应用程序作为启动时音频启动 当我按下模拟器上的后退按钮时 音频音乐处于暂停状态 但是当我的活动回来时从停止状态到前台我的音频音乐未恢复 这是我的代码 public class Au
  • Java:BufferedInputStream 的 available() 方法存在问题

    我正在处理以下代码 用于将大文件拆分为一组较小的文件 FileInputStream input new FileInputStream this fileToSplit BufferedInputStream iBuff new Buff
  • SimpleDateFormat 无法正确处理 DD

    我正在尝试获得这样的格式 2013 06 15 17 45 我在代码中执行以下操作 Date d new Date SimpleDateFormat ft new SimpleDateFormat YYYY MM DD HH mm Stri
  • BigDecimal 中 Divide 方法的 Scale()

    new BigDecimal 37146555 53880000 divide new BigDecimal 1000000 scale 这返回10 但根据API divide method 返回一个 BigDecimal 其值为 这个 除
  • 使用用户名进行 Java LDAP 身份验证

    好吧 这让我发疯 我正在尝试使用 Java 创建 LDAP 身份验证 如果我在 SECURITY PRINCIPAL 中使用我的名字和姓氏 一切都很好 这是我的代码 try Hashtable
  • 使用 Thymeleaf 时我们应该删除 HTML 属性吗?

    我正在研究 Thymeleaf 发现几乎所有示例中都有 Thymeleaf 的标签值以及标准 HTML 值 例如 这些
  • 在Tomcat中设置环境变量TESSDATA_PREFIX

    我们正在使用名为 Tess4J 的 Tesseract OCR Java 库 如果作为独立应用程序运行 它可以正常工作 它需要一个名为 TESSDATA PREFIX 的变量 其中包含 tessdata 配置和其他字符集相关文件 它也可以与
  • 从另一个类添加 Swing 组件

    我正在学习java 我正在尝试从另一个类向我的框架添加一个菜单栏 练习将代码划分为多个类以更好地组织程序 这是我的代码示例 public class MainApp public static void main String args C
  • 当用户使用相同的凭据登录两次时如何使用户会话无效

    我正在使用带有 Richfaces 和 Facelets 的 JSF 1 2 我有一个应用程序 其中包含许多会话范围的 Bean 和一些应用程序 Bean 假设用户使用 Firefox 登录 创建一个会话 ID A 然后他打开 Chrome
  • 如何在开头时解析 json 文件

    我想解析以下 JSON 文件 但以 向我表明这是一个数组 然后继续 对象 我当前的解析器返回一个 JSON 对象 我的问题是 如何修改解析器来解析这个文件 这样解析器将为我提供其他 JSON 文件 从对象或排列开始 JSON 文件 codi
  • 如何在Android中使用资源

    一个人如何使用资产 我有这个代码 AssetManager assets getAssets InputStream stream assets open test txt 看起来它只能在 Activity 类中使 用 如果我尝试在另一个类
  • 不想保留一对一的实体

    假设我有两节课Employee and Department In Employee我已经写了 OneToOne fetch FetchType EAGER cascade CascadeType ALL JoinColumn name d
  • 如何设置 commons-logging 来使用 logback?

    我们使用 slf4j logback 并且碰巧有一些使用 commons logging 的第三方库 如何设置它以使用 logback 答案是不要使用 commons logging jar 因为 SLF4J 的设计目的与 commons
  • 用什么? MVC、MVP 或 MVVM 还是……?

    我将启动一个 Java 项目来开发桌面应用程序 使用什么作为表示层模式 MVC MVP MVVM 或 如果可能的话 举一些可行的小例子 Actually the ultimate post you re looking for is thi
  • 如何使用 Kafka 发送大消息(超过 15MB)?

    我发送字符串消息到Kafka V 0 8使用 Java Producer API 如果消息大小约为 15 MB 我会得到MessageSizeTooLargeException 我尝试过设置message max bytes到 40 MB
  • Java分数计算器

    我对 Java 编程还很陌生 我的 AP 计算机编程课程有作业要完成 所以请耐心等待 我必须弄清楚如何将两个分数相乘 我想知道是否有任何方法可以在方法内部声明变量并在该方法外部使用它 我在介绍方法中的 while 循环 谢谢您 希望这不会令
  • 使用 Spring Batch 将文件中的日期解析为 LocalDateTime

    我正在尝试使用 Spring Batch 读取包含日期的 CSV 文件 但在将日期解析为LocalDateTime Object 字段 日期 上的对象 目标 中的字段错误 拒绝值 2017 07 20 04 15 25 0 代码 typeM

随机推荐