子类是否继承私有字段?


245

这是一个面试问题。

子类是否继承私有字段?

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

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

超类私人成员

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

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


33
曾经有过类似的情况,我意识到我什至不想在一家面试官对Java的了解比我少的公司工作。:)
biziclop 2011年

48
即使面试官知道您是对的,有时也会与您不同意。优秀的面试官将尝试了解有关您的知识,而不是您的技术知识。
安迪·托马斯

4
@DigitalRoss Java语言规范是否也写得不好?见RD01答案:stackoverflow.com/questions/4716040/...
OscarRyz

9
@安迪·托马斯-克拉默(Andy Thomas-Cramer)我也不想和故意撒谎的人一起测试我的反应。
biziclop 2011年

4
好吧,我认为我们应该首先弄清楚Java中“继承”的含义。子类不具有私有字段,而子类具有私有字段,但是无法访问它,这是不同的,哪个引用了Java中继承的确切含义?
MengT 2014年

Answers:


238

这里的问题/答案中的大多数困惑都围绕着继承的定义。

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

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

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

声明为私有的类的成员不会被该类的子类继承。只有声明为protected或public的类的成员才能被声明为非包中声明的包的子类继承。

这解决了面试官提出的确切问题:“做子继承私有字段”。(我加强调)

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

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

编辑(从Bjarne Stroustrup中删除了一个并行引用,由于Java和c ++之间的差异,这可能只会增加混乱。我将答案放在JLS上:)


3
@digital为什么叹气。我了解您认为您是对的。我并不反对您认为对象继承是大多数程序员所教/想到的。但是JLS定义直接适用于原始问题。这是语义肯定的,但JLS确定的定义,而不是你或I.
robert_x44

4
调和所有这些的一种方法是简单地认识到,至少在Java世界中,“继承”一词以两种非常不同的方式来描述派生类和父类的关系。是的,JSL是权威的。是的,这意味着您可以通过这种不幸的方式使用“继承”。但是子类froggle的子类(因为现在我们没有一个词)仍然是其父类的私有字段,这显然仍然是正确的。
DigitalRoss

1
@digital它们是该类的对象。不是班级本身。Simula称它们为连接对象。创建子类的对象时,它由串联的“前缀对象”组成。超类对象是一个前缀对象,它本身可以包含其他前缀对象。我认为说联合检查组的措词“显然很差”是自大的。至于我们用什么词,当然是继承。使用略带歧义的术语没有错。它一直在发生。但这并不意味着没有精确的定义。
robert_x44 2011年

1
@digital我们当然可以同意,该词的使用方式有所不同。:)我们也可以同意,取决于一个模棱两可术语的面试问题可能不是一个好问题。
robert_x44 2011年

2
有人从Java / Oracle获得了“子类的对象包含其超类的私有字段”的引用吗?我同意这一点,但是找不到任何官方文件说明这一点。
MengT 2014年

78

认识到,虽然有很重要两个班,只有一个对象。

因此,是的,它当然继承了私有字段。大概它们对于正确的对象功能是必不可少的,并且父类的对象不是派生类的对象,而派生类的实例绝大部分肯定是父类的实例。如果没有所有领域,那就不是很好。

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

这是一个好问题!


更新:

错误,“不”

好吧,我想我们大家都学到了一些东西。由于JLS起源于确切的“未继承”用语,因此回答“否”是正确的。由于子类无法访问或修改私有字段,因此,它们不会被继承。但是,真的只是一个对象,它真的确实包含了私人领域,因此,如果有人花JLS和教程措辞错误的方式,这将是相当困难的理解面向对象,Java对象,什么是真正发生。

更新更新:

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


2
@ Ma99uS。当然,它们被重用了。这就是继承的全部要点。没有它们,派生类型将不会也不会成为父类型的实例。OOP将毫无意义。多态类型将停止工作。理解只有一个对象并且您是父类型的实例对于理解OOP至关重要。您必须克服这个问题才能完全理解它。
DigitalRoss 2011年

2
不确定父级示例是否很好,因为可以在父类仍然存在且具有该字段的同时继承字段。如果继承以这种方式起作用,我可以在父亲还活着的时候继承他的钱,他也可以保留同样的钱。我的孩子们每个人都有他的钱和我的钱。
彼得·劳瑞

2
@Peter Lawrey没有争论或者什么,但是我是这样想的。父母有一个car,他把它放在private孩子没有钥匙的储物柜中。您确实继承了,car但是对您没有用。因此,实际上,您没有从继承中受益。
Nishant

1
@DigitalRoss。我明白你的意思。嗯,我们可以说该值在那里,因为还加载了未继承的父定义。:)我认为JVM规范将为我们正在寻找的“无字”提供正确答案。 java.sun.com/docs/books/jvms
OscarRyz 2011年

5
-1,Java语言规范清楚地说明了它们不是继承的。没有,没有。他们根本不是。在Java上下文中,继承的任何其他定义都是错误的。
biziclop 2011年

21

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


现在来看上下文。继承是什么意思-如果在派生类创建的对象中存在继承?是的。

如果您的意思是对派生类有用。好吧,不。

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

从功能上讲,它不是继承的。但理想情况下是这样。


好的,只是看了看Java教程,他们引用了这一点:

超类私人成员

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

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

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

我认为这纯粹是观点问题。您可以在任一侧修改参数。最好同时证明这两种情况。

 


2
这是不正确的。您无法访问它们,这是正确的。但是正如我所解释的,它们必须被继承。
DigitalRoss 2011年

1
很好的答案!+1 I believe it's purely matter of point-of-view.justified the existence of protected modifier.
拉维

14

这取决于您对“继承”的定义。子类中是否仍存在字段?绝对是 它可以直接访问它们吗?不。这只是定义的微妙之处。关键是要了解实际情况。


正确。但我认为,在这样的基本问题中应该有共同的答案)
Stan Kurilin

我认为这是Java继承的定义。
OscarRyz 2011年

否则,这取决于您对“字段”的定义。定义整数字段“ foo”是租用一个整数大小的存储柜,并在其上放置“ foo”符号。如果将该字段声明为私有,则派生类将继承未标记的整数大小的存储柜。派生类是否继承“字段”取决于是否将未标记的存储柜称为“字段”。
supercat 2012年

10

我将通过代码演示该概念。子类实际上继承了超类的私有变量。唯一的问题是,除非您为超类中的私有变量提供公共获取器和设置器,否则子对象无法访问它们。

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

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

这样想吧。Borat的父亲Boltok拥有一个装有$ 100,000的保险箱。他不想共享自己的“私有”变量安全。因此,他没有提供保险箱的钥匙。Borat继承了保险柜。但是,如果他什至不能打开它有什么好处?如果只有他父亲提供了钥匙。

父母-

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

10

不。他们不继承它。

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

例如:

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?
     }
}

您还可以通过反射获得count内部的值UseIt。这并不意味着您可以继承它。

更新

即使存在该值,它也不会被子类继承。

例如,一个子类定义为:

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所指出的那样,值仍然存在,但不是通过继承来实现的。

这样说。如果您父亲有钱并且给您一张信用卡,您仍然可以用他的钱来买东西,但这并不意味着您已经继承了所有的钱,对吗?

其他更新

但是,知道为什么存在该属性非常有趣

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

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

例如

//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

我猜确切的术语可以在这里找到: JavaTM虚拟机规范


:)下次您可以借此机会向您的面试官解释他/她错在哪里,这可能会给您额外的分值;)显然,您应该以外交上正确的方式来做。
OscarRyz 2011年

1
他们必须要继承的多态类型有根本没有任何意义。见我的解释。的确,您不能摆弄它们,但它们在那里。他们必须是。
DigitalRoss 2011年

您的代码中没有继承(扩展/实现)关键字,因此这不是继承示例。
fmucar 2011年

1
呃,如果他们在那里,他们是怎么到达那里的?因为子类定义了它们?不,因为它们是,是不是被继承了
DigitalRoss

1
对大一点encapsulationVS inherit,我想这个答案应该多了起来投票。
埃里克·王

6

好吧,我对访调员问题的回答是:私有成员不继承于子类,但子类或子类的对象只能通过公共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()方法在SubClass中进行修改,就像在setXofParent()方法中所做的一样,也可以使用ChildClass对象使用setX()方法或setXofParent()方法进行修改,最终调用setX()。因此,这里的setX()和getX()是对ParentClass的私有成员x的一种门。

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


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

4

好的,这是我研究的一个非常有趣的问题,得出的结论是,超类的私有成员确实可以在子类的对象中使用(但不能访问)。为了证明这一点,这是一个带有父类和子类的示例代码,我将子类对象写入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”的私人成员。请让我知道你们的想法。


3

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

PS我遇到的许多继承定义之一:“继承-一种编程技术,它允许派生类扩展基类的功能,继承其所有STATE(强调我的)和行为。”

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


1

我将不得不回答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”,所以人们认为它不是继承的。但是,类中的每个非静态事物,无论是字段还是方法,都是继承的。


3
“包含”并不意味着“继承”;)
Stan Kurilin

1

Java可视继承中的内存布局

在此处输入图片说明

不考虑填充位/对齐和在VTABLE中包含对象类。因此,子类的对象确实为Super类的私有成员提供了位置。但是,不能从子类的对象访问它。


1

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


0

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

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

然后答案是“ 否”,如果提到访问说明符details,则提到私有成员只能在类本身内访问。

但是,如果问题是

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

也就是说,访问私有成员将执行什么操作都没有关系。在这种情况下,我们可以在超类中设置公共方法,而您可以访问私有成员。因此,在这种情况下,您将创建一个接口/桥来访问私有成员。

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


0

我们可以简单地声明,当继承超类时,那么超类的私有成员实际上就成为子类的私有成员,并且不能被进一步继承,或者对于子类的对象来说是不可能的。



-1

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


-2

私有成员(状态和行为)被继承。它们(可以)影响由类实例化的对象的行为和大小。更不用说它们通过所有可用的或可以由其实现者假定的破译机制对子类很好地看到。

尽管继承具有“事实上的”定义,但它绝对没有指向“可见性”方面的链接,这些方面由“否”答案来承担。

因此,无需外交。JLS在这一点上是错误的。

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

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


1
-1。JLS 定义了语言,JLS不可能是“错误的”。同样,如果存在破坏封装的机制,则并不意味着该字段被继承;仅仅是存在破坏封装的机制。
SL Barth-恢复莫妮卡2012年

定义本身可能有几种错误。我不想再对此进行讨论。这里的论点不是关于破坏封装的机制(可能是坏的),而是关于字段/方法在那里,影响子类的行为和状态的事实。因此它是“继承的”。一个人可以在一个类中使用一个100kb的私有字节数组,并假设他的(后代)后代不继承它。不要错过要点,并认为这是好事还是坏事(夸大有助于提出要点):这是可预见的合法行动。私有成员是“继承”的。
gkakas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.