【未解决】为何子类对象引用可以间接访问父类的私有方法?

首先请看如下代码

/**
 * Father.java
 */
public class Father {
    public int age = 40;

    public String getDescription(){
        return "I'm the father.";
    }

    @Override
    public String toString() {
        // 通过Son调用时,这里的两个this分别是谁?访问的方法或域又是谁?
        System.out.println(this instanceof Son);
        return "[" + this.getDescription() + /*Father.*/this.age + "]";
    }
}

/**
 * Son.java
 */
public class Son extends Father {
    public int age = 20;

    @Override
    public String getDescription(){
        return "I'm the son.";
    }

    @Override
    public String toString() {
        return super.toString() +
                "[" + this.getDescription() + this.age + "]";
    }

    public static void main(String[] args) {
        System.out.println(new Son());
    }
}

运行Son.main之后得到输出如下

true
[I'm the son.40][I'm the son.20]

Process finished with exit code 0

1.第一个问题

可以看到,在Son.toString()里调用super.toString()方法,而super.toString()中调用的this.getDescription()居然实际上是Son.getDescription()!

这个原因可以作如下解释:
super关键字和this关键字不同,它并不是一个引用,仅仅指示编译器去调用超类的方法。
所以调用super.toString()时传递过去的对象引用仍然是Son,即此时super.toString()中的this是一个Son对象引用[1],自然会优先调用子类的覆盖方法。

[1] instanceof语句为ture也可以说明这一点。

2.第二个问题

那么问题来了,为什么super.toString()中的this.age却访问的是Father对象的age域呢?

可以作如下解释:
Son类在继承Father类时,当然也会把域给继承了过去,也就是说Son对象中的数据域有ageFather.age。这里的this虽然是Son对象的引用,但使用超类的方法访问的数据域将会是超类的域,即Father.age

此说法为个人理解,存疑。事实上,将Father.age改为private也能被Son的对象引用访问到,这和下面的问题如出一辙。

3.新的问题

接下来将Father.getDescription()方法改为私有的,同时去除Son.getDescription()@Override覆盖注解。(注意:此时已不是覆盖了)

/**
 * Father.java
 */
public class Father {
    public int age = 40;

    private String getDescription(){
        return "I'm the father.";
    }

    @Override
    public String toString() {
        System.out.println(this instanceof Son);
        return "[" + this.getDescription() + /*Father.*/this.age + "]";
    }
}

/**
 * Son.java
 */
public class Son extends Father {
    public int age = 20;

    public String getDescription(){
        return "I'm the son.";
    }

    @Override
    public String toString() {
        return super.toString() +
                "[" + this.getDescription() + this.age + "]";
    }

    public static void main(String[] args) {
        System.out.println(new Son());
    }
}

运行结果如下:

true
[I'm the father.40][I'm the son.20]

Process finished with exit code 0

观察输出可以看到,此时竟然调用到了Father类的私有方法,我们可以看到这里的this仍然是Son对象的引用,那就很奇怪了,为什么子类的引用可以访问到超类的私有方法呢?

此问题待解决,有一个帖子也讨论了这个问题,但是并没有给出能说服我的解答。等学了JVM之后看能不能解惑。

另一个存疑,第一个问题里,如果我就是想调用到通过子类调用到被覆盖的Father.getDescription(),该怎么做?(不要说在子类方法里用super.getDescription(),我指的是子类调用父类方法Father.toString(),然后在Father.toString()中调用父类被覆盖的方法Father.getDescription())

要是有高手知道答案欢迎在评论区指教一下

posted @ 2020-03-13 17:08  Recycer  阅读(289)  评论(0编辑  收藏  举报