子类到底能不能继承父类的私有属性?

Posted yujkss

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了子类到底能不能继承父类的私有属性?相关的知识,希望对你有一定的参考价值。

文章目录

extends概念

继承就像是我们现实生活中的父子关系,儿子可以遗传父亲的一些特性,在面向对象语言中,就是一个类可以继承另一个类的一些特性,从而可以代码重用,其实继承体现的是is-a关系,父类同子类在本质上还是一类实体;子类通过继承父类的属性的行为,我们称之为继承。Java只支持单继承,不支持多继承。因为多继承容易带来安全隐患:当多个父类定义相同的功能,当功能内容不同的时候,子类对象不确定要运行哪一个,在Java中用另一种形式体现出来,就是接口的多实现。

子类能否继承父类私有属性或方法?

为验证这个问题,我们接着往下看

观点一

Java官方文档的解释(标准):

A subclass does not inherit the private members of its parent class.
However, if the superclass has public or protected methods for
accessing its private fields, these can also be used by the subclass.
子类不能继承父类的私有属性,但是如果子类中公有的方法影响到了父类私有属性,那么私有属性是能够被子类使用的

详细链接:https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

观点二

其他人的理解

父类的任何成员变量都是会被子类继承下去的。子类继承父类,子类拥有了父类的所有属性和方法。父类的私有属性和方法子类是无法直接访问的。当然私有属性可以通过public修饰的get和set方法访问到的,但是私有方法不行。父类的private属性,会被继承并且初始化在子类父对象中,只不过对外不可见。

详解:分析内存后,会发现,当一个子类被实例化的时候,默认会先调用父类的构造方法对父类进行初始化,即在内存中创建一个父类对象,然后再父类对象的外部放上子类独有的属性,两者合起来成为一个子类的对象。

所以:子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访到的即只是拥有,但是无法使用。 (这里不考虑Java反射机制)

辨析

从继承的概念来说,private和final不被继承。Java官方文档上是这么说的。

从内存的角度来说,父类的一切都被继承(从父类构造方法被调用就知道了,因为new一个对象,就会调用构造方法,子类被new的时候就会调用父类的构造方法,所以从内存的角度来说,子类拥有一个完整的父类)。子类对象所引用的内存有父类变量的一份拷贝

如图所示,父类为Person类,子类为Student类。首先明确子类不能继承父类的构造方法。这就是为什么子类的默认的构造方法会自动调用父类的默认的构造方法。在子类的构造方法中通过super()方法调用父类的构造方法。也就是,在构造子类的同时,为子类构造出跟父类相同的域。如此就在子类的对象中,也拥有了父类声明的域了。


如果一个子类继承了父类,那么这个子类拥有父类所有的成员属性和方法,即使是父类里有private属性的变量,子类也是继承的,只不过不能使用,也就是说,它继承了,但是没有使用权,似乎又点矛盾,用我们通俗的说法就是 只能看,不能用,虽然是这样,但是,我们还是可以通过set和get的方法来间接的访问父类中的private属性的变量。

关于成员变量的继承,父类的任何成员变量都是会被子类继承下去的,这些继承下来的私有成员虽对子类来说不可见,但子类仍然可以用父类的函数操作他们。

这样的设计的意义就是我们可以用这个方法将我们的成员保护得更好,让子类的设计者也只能通过父类指定的方法修改父类的私有成员,这样将能把类保护得更好,这对一个完整的继承体系是尤为可贵的。

成员变量和方法

  • 子类只能继承父类的所有非私有的成员变量和方法。可以继承public protected 修饰的成员,不可以继承private修饰的

  • 但是可以通过父类中提供的public的setter和getter方法进行间接的访问和操作private的属性

  • 对于子类可以继承父类中的成员变量和成员方法,如果子类中出现了和父类同名的成员变量和成员方法时,父类的成员变量会被隐藏,父类的成员方法会被覆盖。需要使用父类的成员变量和方法时,就需要使用super关键字来进行引用。

  • 当创建一个子类对象时,不仅会为该类的实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存,即使子类定义了与父类中同名的实例变量。 即依然会为父类中定义的、被隐藏的变量分配内存

  • 如果子类中的实例变量被私有了,其父类中的同名实例变量没有被私有,那么子类对象就无法直接调用该变量,但可以通过先将对象变量强制向上转型为父类型,在通过该对象引用变量来访问那个实例变量,就会得到的是父类中的那个实例变量。

构造器

子类不能继承获得父类的构造方法,但是可以通过super关键字来访问父类构造方法。

在一个构造器中调用另一个重载构造器使用this调用完成,在子类构造器中调用父类构造器使用super调用来完成。

super 和 this 的调用都必须是在第一句,否则会产生编译错误,this和super只能存在一个。

不能进行递归构造器调用,即多个构造器之间互相循环调用。

总结

最后关于Java中子类能否继承父类的私有变量和方法?当然是以 Java 官方文档解释说明为准,这里我们明确一下“继承”一词的概念,在 Java 中,继承一词的意义是有限制的。一个子类只能继承其父类可访问的成员,并且该子类没有覆盖或者说隐藏父类中的那些可访问成员。所以,一个类的成员就是指在这个类中所声明的属性和方法,再加上从其父类继承而来的属性和方法。也就是说,子类是不能继承父类的私有成员的

虽然子类不继承父类中的私有成员,但是在父类中的这些私有成员仍然是子类对象的一部分

。因为在实例化对象的时候,只初始化在当前类中所声明的属性明显是不足够的,还需要初始化其父类中所有声明的属性。在实例化的过程中,JVM 需要为对象的类及其父类中所有定义的属性分配空间,包括父类中声明的私有成员。

所以,我们可以说:子类不能从父类继承私有成员,但是子类的对象是包括子类所不能从父类中继承的私有成员的

继承中的访问级别(三十七)

        我们在上篇博客中介绍了 C++ 中的继承,那么我们想想既然是继承了,子类是否可以直接访问父类中的私有成员呢?根据面向对象理论子类拥有父类的一切属性和行为,自然子类也就能直接访问父类的私有成员!但是根据 C++ 语法外界不能直接访问类的 private 成员,因此子类就不能直接访问父类的私有成员!那么到底哪种是正确的呢?我们来编程实验下

#include <iostream>
#include <string>

using namespace std;

class Parent
{
    int mv;
public:
    Parent()
    {
        mv = 100;
    }
    
    int value()
    {
        return mv;
    }
};

class Child : public Parent
{
public:
    int addValue(int v)
    {
        mv = mv + v;
    }
};

int main()
{
    
    return 0;
}

        我们来编译下看看是否可以通过

技术分享图片

        编译报错了,说明我们的第二种分析是正确的。那么不是说继承可以获得父类的全部资源嘛,它的私有成员在子类中就不能被访问。接下来我们就要介绍一个概念了,在面向对象中的访问级别不只是 public 和 private,还有个 protected 访问级别。那么 protected 关键字的意义是什么呢?被它修饰的成员不能被外界直接访问,但是它修饰的成员可以被子类直接访问。接下来我们体验下 protected 。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
protected:
    int mv;
public:
    Parent()
    {
        mv = 100;
    }
    
    int value()
    {
        return mv;
    }
};

class Child : public Parent
{
public:
    int addValue(int v)
    {
        mv = mv + v;
    }
};

int main()
{
    Parent p;
    
    cout << "p.mv = " << p.value() << endl;
    
    Child c;
    
    cout << "c.mv = " << c.value() << endl;
    
    c.addValue(50);
    
    cout << "c.mv = " << c.value() << endl;
    
    return 0;
}

        我们来试下将 private 属性换成 protected,看看编译可以通过吗?

技术分享图片

        我们看到编译通过了,也正常运行了。那么我们在 main 函数中直接给 p.mv 和 c.mv 赋值为 1000 呢?我们看看编译结果

技术分享图片

        我们看到编译失败了,因为成员变量 mv 是 protected 的,所以在外界不能直接访问。那么为什么在面向对象中需要 protected 呢?我们来想想现实生活中,比如小明要去医院看病。有些隐秘的病史,小明肯定不想让别人知道,但是这时必须得给医生坦白,不然没法治病哈。这时便有了 protected 这个需求了,这个病史不是纯私有的,但又不是暴露在大众之下的。我们面向对象的思想中,为了更完美的贴合生活,便有了 protected 这个关键字。那么我们在定义类时访问级别是怎样选择的呢?我们来看看下面这张表。注:表是由唐长老总结出来的!

技术分享图片

        关系已经很明确了,我就不多废话了。下来我们来看个示例,关系如下

技术分享图片

        用程序描述出来就是下面这

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

class Object
{
protected:
    string mName;
    string mInfo;
public:
    Object()
    {
        mName = "Object";
        mInfo = "";
    }
    
    string name()
    {
        return mName;
    }
    
    string info()
    {
        return mInfo;
    }
};

class Point : public Object
{
private:
    int mX;
    int mY;
public:
    Point(int x = 0, int y = 0)
    {
        ostringstream s;
        
        mX = x;
        mY = y;
        mName = "Point";
        
        s << "P(" << mX << ", " << mY << ")";
        
        mInfo = s.str();
    }
    
    int x()
    {
        return mX;
    }
    
    int y()
    {
        return mY;
    }
};

class Line : public Object
{
private:
    Point mP1;
    Point mP2;
public:
    Line(Point p1, Point p2)
    {
        ostringstream s;
        
        mP1 = p1;
        mP2 = p2;
        mName = "Line";
        
        s << "Line form " << mP1.info() << " to " << mP2.info() << "!";
        
        mInfo = s.str();        
    }
    
    Point begin()
    {
        return mP1;
    }
    
    Point end()
    {
        return mP2;
    }
};

int main()
{
    Object o;
    Point p(1, 2);
    Point pn(5, 6);
    Line l(p, pn);
    
    cout << o.name() << endl;
    cout << o.info() << endl;
    
    cout << endl;
    
    cout << p.name() << endl;
    cout << p.info() << endl;
    
    cout << endl;
    
    cout << l.name() << endl;
    cout << l.info() << endl;

    return 0;
}

        我们看看编译结果

技术分享图片

        我们看到在 Object 类中没有相应的描述,所以它的信息为空,别的打印是正确的。通过对继承中的访问级别的学习,总结如下:1、面向对象中的访问级别不只是 public 和 private;2、protected 修饰的成员不能被外界访问,它使得子类能够访问父类的成员;3、protected 关键字是为了继承而专门设计的;4、没有 protected 就无法完成真正意义上的代码复用。


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

以上是关于子类到底能不能继承父类的私有属性?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中,子类将继承父类的所有属性和方法吗?为啥?

Java中子类能够继承父类的private属性或方法吗

JAVA中,子类将继承父类的所有属性和方法么~?为啥?

Java中子类继承了父类的私有属性及方法吗?

JAVA子类继承父类后是不是有父类的属性

Java中子类能继承父类的私有属性吗?