Java继承中的私有成员

18

有人告诉我,Java子类可以继承其超类的所有成员。那么这是否意味着甚至可以继承私有成员?我知道它可以继承受保护的成员。

有人能向我解释一下吗?我现在完全困惑了。

5个回答

31
这有点取决于你对继承这个词的确切用法。我将通过示例进行解释。
假设你有两个类:ParentChild,其中Child扩展了Parent。此外,Parent有一个名为value的私有整数。
现在问题来了: Child是否继承了私有的value? 在Java中,继承是被定义为“否”的。但是,在一般的面向对象编程语言中,存在轻微的歧义。
你可以说它没有被继承,因为Child无处明确引用value。也就是说,在Child中不能使用任何像this.value的代码,也不能从某些调用代码(显然)使用obj.value
但是,在另一方面,你可以说value被继承了。如果你认为每个Child的实例也是Parent的实例,则该对象必须包含在Parent中定义的value。即使Child类不知道它,每个Child实例中仍然存在名为value的私有成员。因此,在这种情况下,你可以说Child中继承了value
所以,不要使用“继承”一词,只需记住子类不知道父类中定义的私有成员,但也要记住这些私有成员仍然存在于子类的实例中。

30
不,私有成员因其作用域仅限于定义它的类,所以不会被继承。只有公共成员和受保护的成员会被继承。
Java文档中:
“超类的私有成员不会被子类继承。但是,如果超类具有用于访问其私有字段的公共或受保护方法,则子类也可以使用这些方法。嵌套类可以访问其封闭类的所有私有成员,包括字段和方法。因此,由子类继承的公共或受保护嵌套类间接地访问超类的所有私有成员。”
JLS中:
“声明为私有的类成员不会被该类的子类继承。只有在一个与类声明不同的包中声明的子类才会继承类的声明为受保护或公共的成员。”
一个有用的链接:子类是否继承私有字段?

7
请问是否需要逐句翻译呢? - Peter Lawrey
1
@PeterLawrey 这对于那些渴望深入了解语言架构的人来说是非常重要的一点。我的猜测是,所有超类型(包括私有类型)的成员都分配在堆中,进入同一个子类实例的地址/范围(调试器显示超类私有成员作为子类的一部分..就我所知,在实例化子类时,并不会单独创建相应的超类实例..而是将它们的成员包含在子类实例中)..它们只是直接不可用。 - Giorgi Tsiklauri

6

您将会100%满意。我在我的电脑上测试过了,我将我的结论发布在这里。只需查看下面编写的程序、查看程序输出并阅读最后给出的结论即可。

如果您想要自己测试,请复制整个程序并将其保存到名为"InheritanceTest.java"的文件中,然后编译并运行它。

程序

// Testing if a subclass can access the private members of a superclass

class Class1 {
    private String name;

    public void setName(String name) {
        this.name = name;
        System.out.println("The name has been set successfully.");
    }

    public void showName() {
        System.out.println("The name is: " + name);
    }
}

class Class2 extends Class1 {
    private int age;

    public void setAge(int age) {
        this.age = age;
        System.out.println("The age has been set successfully.");
    }

    public void showAge() {
        System.out.println("The age is: " + age);
    }

    public void displayName() {
        //Accessing the private member of superclass here
        //System.out.println("The name is: " + name); //error, can't compile because access to the private member name of the superclass Class1 is not permitted here.
    }
}

class InheritanceTest {
    public static void main(String[] args) {

        Class1 c1 = new Class1();
        Class2 c2 = new Class2();

        c1.setName("Name_C1");
        c2.setName("Name_C2"); //No error, setName() is a public member of the superclass which indirectly gives access to the private member "name".

        c1.showName();
        c2.showName(); //No error, showName() is a public member of the superclass which indirectly gives access to the private member "name".

        c2.setAge(25);
        c2.showAge();

        //c2.displayName(); //error
    }
}

输出

The name has been set successfully.
The name has been set successfully.
The name is: Name_C1
The name is: Name_C2
The age has been set successfully.
The age is: 25

结论

是的,子类可以间接访问超类的私有成员。但是子类不能直接访问超类的私有成员。

所有公共、私有和受保护的成员(即所有字段和方法)都会被子类继承,但是子类只能直接访问超类的公共和受保护成员。如果从超类继承的成员可以访问超类的私有成员,则子类可以使用该继承的成员来访问超类的私有成员。


3
我认为这不是一个定义问题。基于类的继承意味着将行为传递给后代,因此私有成员确实会被继承。私有成员隐藏在类的内部,对其行为、对象大小等都可能产生严重影响。对于新手开发人员来说,“不会被继承”这个答案很危险,他们不能立即理解私有成员的存在。在计算机科学中,“开发优先于理解”很常见,但我们应该避免根据某些技术人员编写的手册中采用的错误“定义”来构建(或破坏)我们对面向对象编程的概念。抱歉在这篇旧帖子中提出这个问题,但这个问题始终存在。

归根结底是关于“继承”的含义。@roadRunner链接的文档两种说法都有:一开始它说“子类从其超类继承所有成员(字段、方法和嵌套类)。”然后又自相矛盾地说它不继承私有成员。个人认为它们被继承了,因为它们在那里,只是不能直接访问。我很好奇Java或者面向对象编程是否有一个真正的“继承”定义。 - sharakan
@sharakan:今天我们只有一种事实上的定义。 - gkakas
因此,如果我们比较似乎成为事实上定义的两种替代方案,可以看到以下内容:假设一个声明私有成员不会被继承的陈述是完全误导和危险的,对于认为超类私有成员(状态和行为元素)消失的新开发人员来说。因此,只有一个定义是安全的。因此,“其他定义”的问题不成立,这是我原始消息的基础(“无论定义如何”)。另一种定义不做任何假设;-) - gkakas

2
尽管https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.2中显示私有成员不会被继承,但实际上它被子类继承了。当我们使用调试器跟踪变量时,它将在“继承”标签下显示私有成员,因此可以试试。还有另一篇帖子讨论了这个问题,其中大多数人认为私有成员不会继承,这误导了很多人,包括我最初。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接