引文
搜了很多篇文章,都说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)
}
测试结论为:
其中,除了Father的(1)和Son的(2),其他都是编译不通过的
即
public class Father(){
father.clone();//(1)
}
public class Son(){
son.clone();//(2)
}
父重写
测试代码不变,测试结论为:
和第一种情况一样,只是FTest的father.clone也可以通过了,即:
public class FTest {
father.clone();//(1)
}
子重写
测试代码不变,测试结论为:
和第一种情况一样,只是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");
}
}
然后测试类改成:
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()编译通过