Z次元

文章讨论了Java中私有成员的继承问题,强调Java官方文档指出私有成员(属性和方法)不会被继承,但子类可以通过间接访问。通过实例分析,文章展示了子类对象实例化时调用父类构造器并初始化私有成员到内存中的过程,进而探讨了子类是否“真正”继承了父类的私有成员。最终得出结论:子类不直接继承私有成员,但可通过特定方式访问。

首先,Java官方文档中明确表示私有成员【即私有属性(field)和私有方法(method)】不会被继承。
图片

Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared.
Constructors, static initializers, and instance initializers are not members and therefore are not inherited.

图片

但是对于那句话,不同的人有不同的理解;
举个例子:

import org.openjdk.jol.info.ClassLayout;
import org.junit.Test;
import org.openjdk.jol.info.ClassLayout;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Apptext{
	@Test
    public void toTest(){
        ExtendPerson extendPerson = new ExtendPerson();
        System.out.println(ClassLayout.parseInstance(extendPerson).toPrintable());
    }
}
class Person {
    private String name ="ahzoo";
    public Person() {}
    public Person(String name){System.out.println(name);}
    private String getName() {return name;}
}
class ExtendPerson extends Person {
    public ExtendPerson() {}
}

ClassLayout需要引入依赖:

    <dependency>
      <groupId>org.openjdk.jol</groupId>
      <artifactId>jol-core</artifactId>
      <version>0.8</version>
    </dependency>

图片
从上面的例子中可以看出,父类的私有成员在子类的内存中是出现了的

再举个例子:

 public class AppTest{
    @Test
    public void toTest2() throws Exception {
        ExtendPerson extendPerson = new ExtendPerson();
        extendPerson.doGet();
    }
}
class Person {
    private String name ="ahzoo";
    public Person() {}
    private String getName() {
        return name;
    }
    private void setName(String name) {
        this.name = name;
    }
    public void doGet(){
        System.out.println(getName()); //ahzoo
    }
}

class ExtendPerson extends Person {
    public ExtendPerson() {}
}

图片
从这个例子中可以看出,子类是可以获得父类的私有成员的
其它操作父类私有成员的方法:

public class AppTest{
     /**
     * 使用反射获取父类私有属性
     */
    @Test
    public void myTest() {

        Class clazz = ExtendPerson.class;
//        ExtendPerson person = (ExtendPerson) clazz.newInstance();
        List fieldsList = new ArrayList<Field>();
        // 获遍历所有父类
        for(; clazz != Object.class ; clazz = clazz.getSuperclass()) { 
            Field[] declaredFields = clazz.getDeclaredFields();
            // 保存得到的属性
            fieldsList.addAll(Arrays.asList(declaredFields));  
        }
    
        // 遍历获得的属性
        for (Object field : fieldsList) {  
            System.out.println(field); // private java.lang.String org.example.Person.name
        }
    }
    /**
     * 使用super获取操作父类私有成员
     */
	@Test
    public void toTest2() {
        ExtendPerson extendPerson = new ExtendPerson("ouo"); //将打印 ---ouo---
    }
}
class Person {
    private String name ="ahzoo";
    public Person() {}
    public Person(String name){System.out.println("---" + name + "---");}
    private String getName() {return name;}
    private void setName(String name) {this.name = name;}
    public void doGet(){System.out.println(getName());}
}

class ExtendPerson extends Person {
    public ExtendPerson() {}
    public ExtendPerson(String name) {super(name);}
}

总结下上面的例子:

  1. 子类的内存中出现了父类的属性
  2. 虽然子类不能直接操作父类属性,但是可以通过间接的方式操作(父类提供的公共方法、super方法或者反射)

以次可以推断出,子类确实是继承了父类的私有成员的。

但是我还是更偏向于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.

即:子类不会继承其父类的私有成员。但是,如果父类中具有能访问其私有字段的public或project修饰的方法,则子类也可以使用这些方法获取其私有成员。
图片

回顾下继承性:
子类继承父类时会在子类的构造器会在执行之前,使用super方法调用父类的构造器;
同理,在调用父类构造器时,父类构造器又会在执行之前调用子类父类的父类的构造器……以次造成连锁反应,直到调用到Object类的构造器为止;
每个类都会在其自身构造器执行之前完成父类构造器的执行,而构造器的作用就是获取其所在类的所有属性和方法,这其中自然也就包括了私有的。
对此,我的理解是:
子类对象在实例化时,调用到了父类的构造器,而父类构造器在执行时自然也就把其成员给初始化了,初始化到哪里了呢?自然是内存中,这也是为什么子类的内存中可以看到父类的私有成员;
但是由于封装性的原理,私有成员只能在自己的类中使用,所以子类是无法在自己的类中使用父类的私有成员的,既然无法使用,那我说他没有继承,也没什么问题吧;
这不就完美体现了继承性吗,所以再回到刚才那句Java官方文档对继承性就很合理了。

再举个通俗的例子:
儿子从父亲那里得到(继承)了很多东西,包括一栋房子,房子里有父亲的房间,由于父亲的房间属于私人空间,所以不能随意进入,需要从父亲那里拿到钥匙才能进入(钥匙就相当于父类的get方法),或者是父亲带你进去(super方法),亦或是找个开锁师傅(反射);那么儿子到底有没有继承这个房间呢?我的观点是没有。
图片

Java文档:
https://docs.oracle.com/javase/specs/index.html

https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html
https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

评论区

这里还没有评论哦

快来发一条评论抢占前排吧

目录