ChatGPT解决这个技术问题 Extra ChatGPT

子类是否继承私有字段?

这是一道面试题。

子类是否继承私有字段?

我回答“否”,因为我们无法使用“普通 OOP 方式”访问它们。但是面试官认为它们是继承的,因为我们可以间接访问这些字段或使用反射,它们仍然存在于对象中。

回来后,我在javadoc中找到了以下引用:

超类中的私有成员 子类不继承其父类的私有成员。

你知道面试官的观点有什么论据吗?

我曾经遇到过类似的情况,我意识到我什至不想在面试官对 Java 的了解比我少的公司工作。 :)
面试官有时会不同意你的观点,即使他知道你是对的。一个好的面试官会尝试更多地了解你,而不是你的技术知识。
@DigitalRoss Java 语言规范是否也写得不好?请参阅 RD01 答案:stackoverflow.com/questions/4716040/…
@Andy Thomas-Cramer 我也不想和故意撒谎的人一起测试我的反应。
好吧,我想我们应该先弄清楚Java中“继承”的含义。子类没有私有字段和子类有私有字段但不能访问是不同的,哪一种是指Java中继承的确切含义?

J
João Neves

这里的问题/答案中的大部分混淆都围绕着继承的定义。

显然,正如@DigitalRoss 解释的那样,子类的 OBJECT 必须包含其超类的私有字段。正如他所说,无法访问私人成员并不意味着它不存在。

然而。这与类的继承概念不同。与 Java 世界中的情况一样,存在语义问题的仲裁者是 Java Language Specification(当前为第 3 版)。

正如 JLS 所述 (https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.2):

声明为私有的类的成员不会被该类的子类继承。只有声明为protected 或public 的类的成员才能被声明在一个包中的子类继承,而不是声明该类的那个。

这解决了面试官提出的确切问题:“子类继承私有字段”。 (重点由我添加)

答案是否定的。他们没有。子类的对象包含其超类的私有字段。子类本身没有其超类的私有字段的概念。

它是迂腐性质的语义吗?是的。这是一个有用的面试问题吗?可能不是。但是 JLS 为 Java 世界建立了定义,并且它(在这种情况下)明确地这样做了。

已编辑(删除了 Bjarne Stroustrup 的平行引用,由于 java 和 c++ 之间的差异可能只会增加混乱。我会让我的答案停留在 JLS 上:)


@digital 为什么叹息。我明白你相信你是对的。我不同意你的观点,对象继承是大多数程序员都被教导/思考的。但是 JLS 定义直接适用于原始问题。是语义,但 JLS 确定定义,而不是你或我。
调和所有这些的一种方法是简单地认识到“继承”一词以两种截然不同的方式用于描述派生类和父类之间的关系,至少在 Java 世界中是这样。是的,JSL 是权威的。是的,这意味着您可以以这种不幸的方式使用“继承”。但是子类(因为现在我们没有一个词)它们父类的私有字段仍然是显而易见的。
@digital 他们在班级的对象中。不是班级本身。 Simula 将它们称为串联对象。创建子类的对象时,它由串联的“前缀对象”组成。超类对象是一个前缀对象,它本身可以包含其他前缀对象。我认为说 JLS 有“明显不好的措辞”是傲慢的。至于我们用什么词,当然是继承。使用稍微模棱两可的术语并没有错。它一直在发生。但这并不意味着没有精确的定义。
@digital我们当然可以同意这个词以不同的方式使用。 :) 我们也可能同意,依赖于模棱两可的术语的面试问题可能不是一个好问题。
任何人都有 Java/Oracle 的“子类对象包含其超类的私有字段”的参考?我同意这一点,但找不到任何官方文件这样说。
D
DigitalRoss

是的

重要的是要意识到虽然有两个类,但只有一个对象。

所以,是的,它当然继承了私有字段。它们大概是正确的对象功能所必需的,虽然父类的对象不是派生类的对象,但派生类的实例大多肯定是父类的实例。如果没有所有领域,情况就不会很好。

不,您不能直接访问它们。是的,它们是遗传的。他们必须是。

这是个好问题!

更新:

错了,“不”

好吧,我想我们都学到了一些东西。由于 JLS 起源于确切的“非继承”措辞,因此回答 “否” 是正确的。由于子类不能访问或修改私有字段,因此,换句话说,它们不能被继承。但确实 is 只是 一个 对象,它确实 确实包含 私有字段,因此如果有人以错误的方式使用 JLS 和教程措辞,很难理解 OOP、Java 对象以及实际发生的事情。

更新更新:

这里的争议涉及一个基本的模糊性:到底在讨论什么?物体?还是我们在某种意义上谈论班级本身?在描述类而不是对象时,允许有很大的自由度。所以子类不继承私有字段,但是作为子类实例的对象肯定包含私有字段。


@Ma99uS。当然,它们会被重复使用。这就是继承的全部意义。没有它们,派生类型就不会也不能成为父类型的实例。 OOP 将毫无意义。多态类型将停止工作。了解只有一个对象并且您是父类型的实例对于理解 OOP 至关重要。你必须克服这个问题才能完全理解它。
不确定父亲的例子是否很好,因为可以在父类仍然存在并且还具有该字段的情况下继承字段。如果继承这样的话,我可以在我父亲活着的时候继承他的钱,他也可以保留同样的钱。我的孩子每个人都有他的钱和我的钱。
@Peter Lawrey 没有争论或其他任何事情,但这就是我的想法。父母有一个car,他把它放在孩子没有钥匙的private储物柜里。你确实继承了 car 但它对你没用。因此,实际上,您并没有从继承中受益。
-1,Java 语言规范清楚地说明它们不是继承的。没有如果,没有但是。他们根本不是。在 Java 的上下文中,任何其他继承定义都是错误的。
我同意你的观点,只需要确认:超类的私有方法也包含在子类的对象中,对吧?
T
Tarun

不,私有字段不会被继承......这就是发明 Protected 的原因。这是设计使然。我想这证明了受保护修饰符的存在。

现在进入上下文。您所说的继承是什么意思——如果它存在于从派生类创建的对象中?是的。

如果您的意思是派生类是否有用。嗯,不。

现在,当您进行函数式编程时,超类的私有字段不会以对子类有意义的方式继承。对于子类,超类的私有字段与任何其他类的私有字段相同。

从功能上讲,它不是继承的。但理想情况下,确实如此。

好的,刚刚查看了他们引用的 Java 教程:

超类中的私有成员 子类不继承其父类的私有成员。但是,如果超类具有访问其私有字段的公共或受保护方法,则子类也可以使用这些方法。

参考:http://download.oracle.com/javase/tutorial/java/IandI/subclasses.html

我同意,该领域就在那里。但是,子类在该私有字段上没有任何特权。对于子类,私有字段与任何其他类的任何私有字段相同。

我相信这纯粹是观点问题。你可以塑造任何一方的论点。最好双向证明。


这是不正确的。您无法访问它们,这是正确的。但正如我所解释的,它们必须被继承。
很好的答案!为 I believe it's purely matter of point-of-view.justified the existence of protected modifier. +1
u
user541686

这取决于您对“继承”的定义。子类是否还有内存中的字段?确实。它可以直接访问它们吗?不,这只是定义的微妙之处;关键是要了解真正发生的事情。


正确的。但我认为在这样的基本问题中应该有共同的答案)
我认为这是Java对继承的定义。
否则,这取决于您对“字段”的定义。定义一个整数字段“foo”就是租一个整数大小的储物柜,并在上面放一个“foo”标志。如果该字段被声明为私有,则派生类继承一个未标记的整数大小的存储锁。派生类是否继承“字段”取决于是否将该未标记的存储柜称为“字段”。
E
Erran Morad

我将用代码演示这个概念。子类实际上继承了超类的私有变量。唯一的问题是子对象无法访问它们,除非您为超类中的私有变量提供公共 getter 和 setter。

考虑包 Dump 中的两个类。子扩展父。

如果我没记错的话,内存中的子对象由两个区域组成。一个只是父部分,另一个是子部分。子只能通过父级中的公共方法访问其父级代码中的私有部分。

这样想吧。波拉特的父亲博尔托克有一个装有 10 万美元的保险箱。他不想共享他的“私有”变量安全。所以,他没有提供保险箱的钥匙。波拉特继承了保险箱。但是,他连打开都打不开又有什么用呢?如果只有他的父亲提供了钥匙。

家长 -

package Dump;

public class Parent {

    private String reallyHidden;
    private String notReallyHidden;

    public String getNotReallyHidden() {
        return notReallyHidden;
    }

    public void setNotReallyHidden(String notReallyHidden) {
        this.notReallyHidden = notReallyHidden;
    }

}//Parent

孩子 -

package Dump;

public class Child extends Parent {

    private String childOnly;

    public String getChildOnly() {
        return childOnly;
    }

    public void setChildOnly(String childOnly) {
        this.childOnly = childOnly;
    }

    public static void main(String [] args){

        System.out.println("Testing...");
        Child c1 = new Child();
        c1.setChildOnly("childOnly");
        c1.setNotReallyHidden("notReallyHidden");

        //Attempting to access parent's reallyHidden
            c1.reallyHidden;//Does not even compile

    }//main

}//Child

C
Community

不,他们不会继承它。

其他一些类可能间接使用它的事实并没有说明继承,而是说明了封装。

例如:

class Some { 
   private int count; 
   public void increment() { 
      count++;
   }
   public String toString() { 
       return Integer.toString( count );
   }
}

class UseIt { 
    void useIt() { 
        Some s = new Some();
        s.increment();
        s.increment();
        s.increment();
        int v = Integer.parseInt( s.toString() );
        // hey, can you say you inherit it?
     }
}

您还可以通过反射在 UseIt 中获取 count 的值。这并不意味着,你继承它。

更新

即使值在那里,它也不会被子类继承。

例如一个子类定义为:

class SomeOther extends Some { 
    private int count = 1000;
    @Override
    public void increment() { 
        super.increment();
        count *= 10000;
    }
}

class UseIt { 
    public static void main( String ... args ) { 
        s = new SomeOther();
        s.increment();
        s.increment();
        s.increment();
        v = Integer.parseInt( s.toString() );
        // what is the value of v?           
     }
}

这与第一个示例的情况完全相同。属性 count 是隐藏的,并且完全由子类继承。尽管如此,正如 DigitalRoss 指出的那样,价值是存在的,但不是通过继承来实现的。

这么说吧。如果你父亲很有钱,给你一张信用卡,你仍然可以用他的钱买东西,但这并不意味着你继承了所有的钱,不是吗?

其他更新

不过,这很有趣,to know why the attribute is there.

坦率地说,我没有确切的术语来描述它,但它是 JVM 和它的工作方式,它也加载了“未继承”的父定义。

我们实际上可以更改父类,子类仍然可以工作。

For instance

//A.java
class A {
   private int i;
   public String toString() { return ""+ i; }
}
// B.java
class B extends A {}
// Main.java
class Main {
   public static void main( String [] args ) {
      System.out.println( new B().toString() );
    }
}
// Compile all the files
javac A.java B.java Main.java
// Run Main
java Main
// Outout is 0 as expected as B is using the A 'toString' definition
0

// Change A.java
class A {
   public String toString() {
      return "Nothing here";
   }
}
// Recompile ONLY A.java
javac A.java
java Main
// B wasn't modified and yet it shows a different behaviour, this is not due to 
// inheritance but the way Java loads the class
Output: Nothing here

我想确切的术语可以在这里找到:The JavaTM Virtual Machine Specification


:) 下次你可以借此机会向你的面试官解释他/她哪里错了,这可能会给你加分 ;) 显然你应该以外交正确的方式来做这件事。
它们必须被继承才能使多态类型具有任何意义。看我的解释。的确,你不能摆弄它们,但它们就在那里。他们必须是。
您的代码中没有继承(扩展/实现)关键字,因此这不是继承示例。
呃,如果他们在那里,他们是怎么到那里的?因为子类定义了它们?不,因为他们是,呃,嗯,错误,继承?
encapsulationinherit 的好点,我想这个答案值得更多投票。
d
dganesh2002

好吧,我对面试官问题的回答是 - 私有成员不会在子类中继承,但子类或子类的对象只能通过公共 getter 或 setter 方法或原始类的任何此类适当方法访问它们。通常的做法是让成员保持私有并使用公共的 getter 和 setter 方法访问它们。那么,当他们处理的私有成员对对象不可用时,只继承 getter 和 setter 方法有什么意义呢?这里的“继承”只是意味着它可以直接在子类中使用,以供子类中新引入的方法使用。

将以下文件另存为 ParentClass.java 并自己尝试 ->

public class ParentClass {
  private int x;

  public int getX() {
    return x;
  }

  public void setX(int x) {
    this.x = x;
  }
}

class SubClass extends ParentClass {
  private int y;

  public int getY() {
    return y;
  }

  public void setY(int y) {
    this.y = y;
  }

  public void setXofParent(int x) {
    setX(x); 
  }
}

class Main {
  public static void main(String[] args) {
    SubClass s = new SubClass();
    s.setX(10);
    s.setY(12);
    System.out.println("X is :"+s.getX());
    System.out.println("Y is :"+s.getY());
    s.setXofParent(13);
    System.out.println("Now X is :"+s.getX());
  }
}

Output:
X is :10
Y is :12
Now X is :13

如果我们尝试在 SubClass 的方法中使用 ParentClass 的私有变量 x,那么任何修改都不能直接访问它(意味着不继承)。但是 x 可以通过原始类的 setX() 方法在子类中修改,就像在 setXofParent() 方法中所做的那样,或者可以使用 ChildClass 对象使用 setX() 方法或最终调用 setX() 的 setXofParent() 方法进行修改。所以这里的 setX() 和 getX() 是一种通往 ParentClass 的私有成员 x 的门。

另一个简单的例子是 Clock 超类将小时和分钟作为私有成员,并将适当的 getter 和 setter 方法作为公共成员。然后是作为时钟子类的 DigitalClock。在这里,如果 DigitalClock 的对象不包含小时和分钟成员,那么事情就搞砸了。


根据 Oracle 文档 - 子类不继承其父类的私有成员。但是,如果超类具有访问其私有字段的公共或受保护方法,则子类也可以使用这些方法。
B
Bhavesh Agarwal

好的,这是一个非常有趣的问题,我研究了很多并得出结论,超类的私有成员在子类的对象中确实可用(但不可访问)。为了证明这一点,这是一个包含父类和子类的示例代码,我正在将子类对象写入 txt 文件并读取文件中名为“bhavesh”的私有成员,因此证明它确实在子类中可用类,但由于访问修饰符而无法访问。

import java.io.Serializable;
public class ParentClass implements Serializable {
public ParentClass() {

}

public int a=32131,b,c;

private int bhavesh=5555,rr,weq,refw;
}

import java.io.*;
import java.io.Serializable;
public class ChildClass extends ParentClass{
public ChildClass() {
super();
}

public static void main(String[] args) {
ChildClass childObj = new ChildClass();
ObjectOutputStream oos;
try {
        oos = new ObjectOutputStream(new FileOutputStream("C:\\MyData1.txt"));
        oos.writeObject(childObj); //Writing child class object and not parent class object
        System.out.println("Writing complete !");
    } catch (IOException e) {
    }


}
}

打开 MyData1.txt 并搜索名为“bhavesh”的私有成员。请让我知道你们的想法。


F
Flanker

似乎子类确实继承了私有字段,因为这些字段被用于子类的内部工作(从哲学上讲)。子类在其构造函数中调用超类构造函数。如果超类构造函数在其构造函数中初始化了这些字段,则超类私有字段显然由调用超类构造函数的子类继承。这只是一个例子。但是,如果没有访问器方法,子类当然不能访问超类的私有字段(这就像无法弹出 iPhone 的后面板以取出电池来重置手机......但电池仍然存在)。

PS 我遇到的许多继承定义之一:“继承——一种编程技术,它允许派生类扩展基类的功能,继承它的所有状态(重点是我的)和行为。”

私有字段,即使子类无法访问,也是超类的继承状态。


z
zhqyvvn

例如,

class Person {
    private String name;

    public String getName () {
        return this.name;
    }

    Person(String name) {
        this.name = name;
    }
}
public class Student extends Person {

    Student(String name) {
        super(name);
    }
    
    public String getStudentName() {
        return this.getName(); // works
        // "return this.name;" doesn't work, and the error is "The field Person.name is not visible"

    }
}

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Bill");

        String name = s.getName(); // works
        // "String name = s.name;" doesn't work, and the error is "The field Person.name is not visible"

        System.out.println(name);
    }
}

s
somenath mukhopadhyay

https://i.stack.imgur.com/NuSQn.jpg

https://i.stack.imgur.com/jY9Y5.jpg

不考虑填充位/对齐和在 VTABLE 中包含对象类。所以子类的对象确实为超类的私有成员占有一席之地。但是,不能从子类的对象访问它...


N
Nolan Akash

我不得不回答 Java 中的私有字段是继承的。请允许我演示一下:

public class Foo {

    private int x; // This is the private field.

    public Foo() {
        x = 0; // Sets int x to 0.
    }

    //The following methods are declared "final" so that they can't be overridden.
    public final void update() { x++; } // Increments x by 1.
    public final int getX() { return x; } // Returns the x value.

}


public class Bar extends Foo {

    public Bar() {

        super(); // Because this extends a class with a constructor, it is required to run before anything else.

        update(); //Runs the inherited update() method twice
        update();
        System.out.println(getX()); // Prints the inherited "x" int.

    }

}

如果您在程序 Bar bar = new Bar(); 中运行,那么您将始终在输出框中看到数字“2”。因为整数“x”是用方法update()getX()封装的,所以可以证明整数是继承的。

令人困惑的是,因为您不能直接访问整数“x”,所以人们认为它不是继承的。但是,类中的每个非静态事物,无论是字段还是方法,都是继承的。


“包含”并不意味着“继承”;)
j
joooohnli

不,私有字段不会被继承。唯一的原因是子类不能直接访问它们。


R
Ravi

我相信,答案完全取决于已提出的问题。我的意思是,如果问题是

我们可以直接从子类访问超类的私有字段吗?

那么答案是,如果我们通过access specifier details,提到,私有成员只能在类本身内访问。

但是,如果问题是

我们可以从子类访问超类的私有字段吗?

这意味着,您将如何访问私有成员并不重要。在这种情况下,我们可以在超类中创建公共方法,您可以访问私有成员。因此,在这种情况下,您正在创建一个接口/桥来访问私有成员。

其他 OOP 语言如 C++,具有 friend function 概念,通过它我们可以访问其他类的私有成员。


A
Aamir wani

我们可以简单地说,当一个超类被继承时,那么超类的私有成员实际上变成了子类的私有成员,不能进一步被继承或不能被子类的对象访问。


m
mimibar

私有类成员或构造函数只能在包含成员或构造函数声明的顶级类 (§7.6) 的主体内访问。它不被子类继承。 https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6


N
Nitin jain

子类不继承其父类的私有成员。但是,如果超类具有访问其私有字段的公共或受保护方法,则子类也可以使用这些方法

参考:https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html#:~:text=Private%20Members%20in%20a%20Superclass,be%20used%20by%20the%20subclass


F
FateMintTrio

我可以试着帮助你。当子类(例如名为 B)扩展超类(例如名为 A)时,它会自动从其超类继承字段(例如属性和/或方法)。现在,B 在其内存布局中为 A 类中的每个字段(甚至是私有字段)提供了空间。事实上,Java 不允许子类 B 使用私有字段,因为它们是私有的。


s
suprinder

子类不继承其父类的私有成员。但是,如果超类具有访问其私有字段的公共或受保护方法,则子类也可以使用这些方法。


g
gkakas

私有成员(状态和行为)是继承的。它们(可以)影响由类实例化的对象的行为和大小。更不用说通过所有可用的或可以由其实现者假设的封装破坏机制,它们对子类非常可见。

尽管继承有一个“事实上的”定义,但它肯定与“可见性”方面没有联系,这些方面由“否”答案假设。

因此,没有必要外交。 JLS 在这一点上是错误的。

任何假设它们不是“继承”的都是不安全和危险的。

因此,在两个事实上(部分)冲突的定义(我不会重复)中,唯一应该遵循的是更安全(或安全)的定义。


-1。 JLS 定义了语言,JLS 不可能“错误”。此外,如果存在破坏封装的机制,这并不意味着该字段是继承的;只是存在破坏封装的机制。
一个定义本身可能在几个方面是错误的。进一步讨论这个不是我的意图。这里的论点不是关于打破封装的机制(可能是上帝或坏的),而是关于字段/方法存在的事实,影响子类的行为和状态。所以它是“继承的”。一个人可以在一个类中使用一个 100kb 的私有字节数组,并假设他的(巨型)后代不继承它。不要忽略这一点并将其判断为好或坏的做法(夸大其词有助于说明观点):这是可预见的合法行为。私人成员是“继承的”。