RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1563303
Accepted
johnymkp
johnymkp
Asked:2024-01-26 15:13:22 +0000 UTC2024-01-26 15:13:22 +0000 UTC 2024-01-26 15:13:22 +0000 UTC

为什么创建子实例时需要调用父实例的构造函数?

  • 772

我们正在谈论Java。从技术上讲,什么解释了创建子级时总是需要调用父级构造函数的事实?例如,我们有两个类:

public class Asset {
    public String name;

    public Asset(String name) {
        this.name = name;
    }
}

public class House extends Asset {
    public long cost;

    public House(String name, long cost) {
        this.name = name;
        this.cost = cost;
    }
}

在 House 中会出现错误“‘Asset’中没有可用的默认构造函数”。为什么子级调用父级构造函数如此重要?在这种特殊情况下,乍一看,这根本没有必要,因为 对于 House,构造函数会自行填写所有字段。

UPD。该问题与评论中提供的任何链接都不重复。没有一个答案\评论\链接回答我的问题,因为答案归结为:

  • “要创建子类的实例,您需要顺序调用超类的构造函数。” 但没有写为什么需要这样做。

然而,根据答案中的短语(Upiter 1401 给出的链接),“超类必须在子类之前初始化,以便任何初始值、内存位置等都具有有效的起始值。”,我得出的结论是,我们需要挖掘父子类之间的技术组织关系。例如:

  • 子级中父成员的继承在技术上是如何实现的?那些。这是将成员复制到后代的平庸的幕后行为,还是连接内存中两个类的更狡猾的机制。
  • 创建子实例时,是否将父类的类加载到内存中。
  • 构造函数在创建实例时的技术作用是什么,因为字段是在构造函数执行之前初始化的。因此,这并不是它总是在实例创建过程结束时调用的主要原因。
  • ETC。
java
  • 4 4 个回答
  • 128 Views

4 个回答

  • Voted
  1. peterz
    2024-01-26T15:41:07Z2024-01-26T15:41:07Z

    因为您的字段Asset.name是公共的,如果它是私有的,则无法在子类构造函数中对其进行初始化。

    一般来说,子类的作者不应该考虑如何初始化父类的字段(这可能是由另一个人在另一个时间编写的,等等),并去弄清楚一切是如何工作的,特别是如果有不是一个父类,而是一整个链)。他只需要记住调用可用的父构造函数之一,这将完成所有工作。这需要显式或隐式调用父构造函数。

    • 3
  2. insolor
    2024-01-26T16:21:54Z2024-01-26T16:21:54Z

    简而言之,创建子类的对象时,无论如何都必须(显式或隐式)调用父类的构造函数。如果没有显式调用构造函数,仍然会发生不带参数的隐式调用super()。

    因此你需要:

    • 或者向父类添加适当的无参数构造函数
    • 或使用参数添加对所需构造函数的显式调用
    • 或者删除父类中的所有构造函数,那么会自动添加一个不带参数的空构造函数(默认构造函数)。那些。即使你自己不给类添加构造函数,类中仍然会有构造函数。

    为什么创建子实例时需要调用父实例的构造函数?

    因为这就是 Java 中的实现方式。无论如何,在继承时,都会调用从当前构造函数到“顶级”父级构造函数(一直到类构造函数java.lang.Object)的构造函数链。

    • 1
  3. Best Answer
    Alex Krass
    2024-01-27T16:47:22Z2024-01-27T16:47:22Z

    据我了解,您对语言结构原理的逻辑解释感兴趣。

    根据定义,构造函数用于将对象初始化为工作一致状态。这意味着在程序可以使用类对象之前,必须 100% 执行其中的关键代码。您仅将构造函数用作这个更大机制的一小部分。

    您仅使用它来初始化变量,但您可以执行更多操作:

    - создать устойчивое соединение с устройствами компьютера
    - найти и загрузить библиотеки в процессе работы
    - проверить все поля на валидность перед их использование внутри класса
    - создать соединение с той же базой данных перед работой
    - и т.д.
    

    构造函数还可以确保无论使用您的类的开发人员的意愿如何,都会执行此代码。这是必要的,以免从内部破坏他的逻辑。以及强制私有字段的要求、保护对内部方法的访问的封装等。也就是说,这增加了程序的可靠性,因为普通方法可能会被忘记被调用,或者由于无知而根本不被调用。

    第二部分,由于这是父类的关键代码,所以继承时必须调用它。否则,继承只会让它被忽略。这将意味着违反软件可靠性原则。特别是考虑到您可能拥有即使继承人也无法访问的私有字段和方法。您将如何私下检查和处理额外的逻辑?或者如果代码已经在父级中,则连接到设备(复制粘贴是最糟糕的选择)?

    第三部分,父类的数据必须初始化并准备好供其余代码使用。子类与其构造函数一样,在使用它之前也必须获取所有数据的正确状态。因此,一开始就调用了父类的构造函数。只有在此之后,程序才能确保您正在使用正确的数据,并在后续程序中补充和使用它。

    这还允许 JVM 在内部透明地处理代码,并在必要时将指令放置在它们所属的位置。JVM 开发人员不必考虑数据放置在何处以及如何放置、必须调用什么以及最终不应该调用什么。该语言的实现并不处理您想要在那里编写的内容的逻辑解释;它的任务是清楚地遵循说明。

    因此,继承的实现要求父类的关键代码必须在一开始就通过基类的构造函数之一来调用。

    UPD

    那些。这是将成员复制到后代的平庸的幕后行为,还是连接内存中两个类的更狡猾的机制。

    语言规范仅笼统地描述了它应该如何工作,但没有公开其实现。这意味着虚拟机创建者可以完全自由地决定操作。它可以首先创建父类的变量,调用构造函数,然后添加子类的数据。可以一次创建所有变量,然后依次调用两个构造函数。也许在开发过程中你可以完全改变实现。可以使用更聪明的东西。此类问题的答案在于特定开发人员对特定虚拟机的实现,并且可能会随着时间而改变。

    因此,通常会在特定的虚拟机(相同的 JVM HotSpot)上查看实现,但需要注意的是,在其他虚拟机中一切可能会有所不同。

    • 1
  4. AdamBeno
    2024-01-27T06:18:54Z2024-01-27T06:18:54Z

    当实例化一个类时,Java 执行以下步骤:

    1. 为对象保留内存以存储其状态(字段)。
    2. 对象的字段使用默认值进行初始化(例如,数值设置为 0,对象引用设置为 null)。
    3. 调用类构造函数来进一步初始化对象并设置初始字段值。

    OOP 中的封装原则意味着类的实现细节对外部使用是隐藏的。这意味着派生类不应直接初始化从基类继承的字段,因为这会破坏封装。

    • 0

相关问题

  • wpcap 找不到指定的模块

  • 如何以编程方式从桌面应用程序打开 HTML 页面?

  • Android Studio 中的 R.java 文件在哪里?

  • HashMap 初始化

  • 如何使用 lambda 表达式通过增加与原点的距离来对点进行排序?

  • 最大化窗口时如何调整元素大小?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5