前言

最近在看JavaGuide进行基础知识的回顾,注意到一个有意思的观点,原文如下

关于继承如下 3 点请记住

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有

于是我就开始尝试验证这一观点

对象初始化

首先从对象初始化开始思考,一般继承某个父类的子类对象初始化时是按照以下顺序

  1. 父类的静态变量和常量以及父类的静态代码块
  2. 子类的静态变量和常量以及子类的静态代码块
  3. 父类的变量赋默认值和父类代码块
  4. 父类的构造方法
  5. 子类的变量赋默认值和父类代码块
  6. 子类的构造方法

既然按照对象初始化顺序来说,子类的构造方法被调用的时候必然有一个父类对象被构造,那么必然有办法在子类构造时对父类的变量进行赋值。

这个时候我突然想到了Java如果不指定构造方法,会默认给每个类提供一个无参构造方法,那如果我的父类只有有参构造方法又会如何呢?

使用有参构造方法

首先我准备好了父类,这个父类里面只有一个parentName的属性,同时制定一个有参构造方法,代码如下:

public class Parent {

    private String parentName;

    public Parent(String parentName) {
        this.parentName = parentName;
    }
}

然后我准备了子类继承父类,这个时候有意思的事情发生了,子类必须提供一个带参的构造方法来构造父类对象,同时要在这个带参构造方法里面通过super关键字调用父类的构造方法,否则编译会失败,最终代码如下:

public class Child extends Parent{

    public Child(String parentName) {
        super(parentName);
    }
}

其实从这里就可以知道子类是拥有父类的私有属性的了,只是因为访问控制限制无法直接访问这个属性。

但是除了这个方法以外,我还有什么办法去佐证这个观点呢?当然有啦,而且还不止一种呢,我知道的就有两个办法

  1. 直接查看字节码
  2. 使用反射获取

使用反射获取

由于篇幅问题,这里就展示怎么使用反射获取父类的属性的示例代码,如果对通过字节码验证这个观点感兴趣可以给我留言:

Child child = new Child("David");
    try {
        // 获取父类Class对象
        Class parentClass = child.getClass().getSuperclass();
        // 获取父类的属性
        Field field = parentClass.getDeclaredField("parentName");
        // 解除访问限制
        field.setAccessible(true);
        // 输出
        System.out.println(field.get(child));
    } catch (NoSuchFieldException e) {
        // 这里只是演示用,实际开发时需要将错误信息打印到日志文件
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

输出结果

David