java修饰符super_Java的protected修饰符——什么叫“访问从基类继承而来的protected方法“以及”访问基类实例的protected方法”...

引文

搜了很多篇文章,都说protected的本质是

1.基类的protected成员是包内可见的,并且对子类可见;

2.若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。

其中我特别疑惑的是第二种情况,什么叫做”访问从基类继承而来的protected方法“,什么叫做”访问基类实例的protected方法“,

特别是相对详细的这篇文章:Java 访问权限控制:你真的了解 protected 关键字吗?里提到了特别多的例子来解释,似乎关键在于是否重写,但是实际上第一个例子我就没理解明白,所以有了这篇文章。

首先抛出最终我得出的观点,再说明推导过程,如有错误希望能得到指正:

实例对象.f()是属于“通过父类的实例对象访问protected成员”

super.f()是属于“通过从父类继承的protected成员进行访问”

影响结果的始终是“到底是通过父类的实例变量访问,还是直接访问从父类继承的protected成员”

而重写带来了不同的结果是因为,当父类重写了祖先类的时候,子类调用的就是父类的f()方法,而不是祖先类的f()方法,这样带来的结果就是,包的可见性的变化,从而导致编译结果不一样

情况假设

Father和Son有两种可能性:

同包(由于我只是对第二种情况比较疑惑,所以同包可以不考虑了)

不同包

Clone方法的存在方式:

父子都不重写

父重写

子重写

父子都重写

Test:

单独,和Father同包;

单独,和Son同包;

单独,都不同包

为Father;

为Son;

总共有2×4×5种可能的结果,由于我们只考虑父子不同包,而且由于Test的五种情况可以一起测试,所以一共只有4种情况

父子都不重写

测试代码为:

public static void main(String[] args) {

Father father = new Father();

father.clone();//(1)

Son son = new Son();

son.clone();//(2)

}

测试结论为:

7c87c0c52039b8c3269122c8f24b204d.png

其中,除了Father的(1)和Son的(2),其他都是编译不通过的

public class Father(){

father.clone();//(1)

}

public class Son(){

son.clone();//(2)

}

父重写

测试代码不变,测试结论为:

7c87c0c52039b8c3269122c8f24b204d.png

和第一种情况一样,只是FTest的father.clone也可以通过了,即:

public class FTest {

father.clone();//(1)

}

子重写

测试代码不变,测试结论为:

7c87c0c52039b8c3269122c8f24b204d.png

和第一种情况一样,只是STest的son.clone也可以通过了,即:

public class STest {

son.clone();

}

父子都重写

结论是3和4的综合

初步猜测(其实是错误的)

我对1~4出现的情况进行以下猜测:

在"2.父重写"中,此时clone方法的来源是Father,由于FTest和Father是同一个包,因此可以访问father.clone();

public class FTest {

father.clone();//(1)

}

而在"1.父子都不重写"中,clone方法的来源是Object,FTest既不是Object的直接子类,和Object也不同包,因此不能访问

但是这么猜测很明显有个问题,Object是任何对象的超类,FTest既然没有继承别的类,那么根据Java单继承的特性,FTest应该直接继承于Object,那这样在1和2中都FTest应该都可以直接访问father.clone才对

引入GrandPa类模拟Object类

我考虑到可能是Object这个类比较特殊,所以我又创建了一个祖先类Grand进行测试,用来模拟Object

package grandp;

public class GrandPa {

protected void f() {

System.out.println("I'm GrandPa's protected method");

}

}

46e7734c8834987bfcef02a082178026.png

然后测试类改成:

public static void main(String[] args) {

Father father = new Father();

father.clone();

father.f();

Son son = new Son();

son.clone();

son.f();

}

结果我发现f()的测试结果和clone()是一致的,也就是特殊性不在于Object类,而是我理解出了问题

最终结论

不过这样对比就能理解到底是哪里出了问题了,实际上是,FTest确实是继承于GrandPa,但是依然不能通过father.f()访问f()方法,不过可以在方法重写中使用super.f()访问GrandPa的f()方法

public class FTest extends GrandPa{

@Override

protected void f() {

// TODO Auto-generated method stub

super.f();//编译通过,从父类继承而来的f

}

public static void main(String[] args) {

father.f();//编译不通过,父类实例的f

}

}

这样结论就出来了

father.f()是属于“通过父类的实例对象访问protected成员”

super.f()是属于“通过从父类继承的protected成员进行访问”

影响结果的始终是“到底是通过父类的实例变量访问protected成员,还是直接访问从父类继承的protected成员”

关于重写

而重写带来了不同的结果是因为,当父类重写了祖先类的时候,子类调用的就是父类的f()方法,而不是祖先类的f()方法,这样带来的结果就是,包的可见性的变化。

比如说,在“1.父子都不重写”中,FTest调用的father.clone()方法来自Object,因此可见包为Java.lang,由于FTest不在这个包中,所以编译不通过

而在“2.父重写”中,FTest调用的father.clone()方法来自Father,因此可见包为fatherp,由于Father和FTest在同一个包下,因此在2中father.clone()编译通过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值