为什么子类中不能访问另一个包中父类中的protected方法?

前言

其实这个问题问法是错的,在子类中是可以访问另一个包中父类中的protected方法,能问出这样的问题,多半是在不同包的子类中创建了父类对象,通过父类对象去调用protected方法,结果发现编译不通过,所以才会萌生这样的疑问。

正确的访问方式:

1. super.method() //protected
2. 创建子类对象instance(且这个实例也得用子类类型的变量来“接收”),通过instance.method()访问

表面理解

为什么子类中不能访问另一个包中父类中的protected方法?

一般我们问这个这个问题呢,其实是没有明白protected权限到底control了什么,都知道protected修饰的成员变量和方法可以被包外的子类访问到,这里被包外子类访问实质并不是可以在子类中通过创建父类对象来访问这个protected方法, 而是可以通过创建子类对象来访问的。这也可以说是protected权限对包外访问时的一种限制,在包内则可以像public那样可以在任意地方访问。

产生这个问题的主要是我们把在包内访问的方式理解强加到了包外。其实我们又可以转换问题为什么跨包访问某个类的protected方法必须通过继承呢?答案很简单,通过继承呢,子类就可以获得了父类方法的地址信息并把这些信息保存到自己的方法区,这样就可以通过子类对象访问自己的方法区从而间接的访问父类的方法(重写的话,就直接访问子类自己重写后的方法)。

内部理解

首先可以先理解一下静态方法的访问和多态的实现:http://blog.csdn.net/dawn_after_dark/article/details/74357049;通过以上博客理解方法表~

不同包下的实例方法访问:

package com.learn.pra05;
/**
 * 父类Person在com.learn.pra05;
 * @author mengyi
 */
public class Person {
    /**
     * 保护类型的实例方法
     */
    protected void print(){
        System.out.println("Person!");
    }
}
package com.learn.pra06;

import com.learn.pra05.Person;
/**
 * 子类所在的包为com.learn.pra06
 * @author mengyi
 */
public class Worker extends Person{
    public static void main(String[] args){
        Person person = new Person();
        person.print();
    }
}

但是上面会在person.print();报编译错误,错误为:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    The method print() from the type Person is not visible
    at com.learn.pra06.Worker.main(Worker.java:8)

说Person 的print 方法在这里不可见,Worker确实继承了Person,但只是通过继承产生了自己能访问的方法表包括父类的保护区域(实例方法),并无权限访问父类对象的方法表中保护区域,就是只能通过Worker自己的对象去访问自己的方法表中保护区域来调用已继承的print方法,Worker是无权限查看Person方法表中保护区域信息,更别说用通过Person对象调用了。这里也就很好的解释了为什么继承的子类看不到private方法,和为什么包外无法访问父类的default方法,因为编译器规定不能继承过来,所以子类的方法表不存在这些修饰符修饰的方法,更别谈调用了。

这里再给出《java in a nutshell》中的一段话:

protected access requires a little more elaboration. Suppose class A declares a protected field x and is extended by a class B, which is defined in a different package (this last point is important). Class B inherits the protected field x, and its code can access that field in the current instance of B or in any other instances of B that the code can refer to. This does not mean, however, that the code of class B can start reading the protected fields of arbitrary instances of A! If an object is an instance of A but is not an instance of B, its fields are obviously not inherited by B, and the code of class B cannot read them.

跟我上面说的一样,类B继承了类A,包括保护型方法区域,所以能通过B对象来调用这个区域访问该方法。在类B是无法查看对象A的保护型方法区域,是因为类B并没有继承对象A的保护型方法区域,类B只是继承了类A的保护型方法区域。这也很好解释了下面说到的静态方法的访问!

再举一个例子:

package com.learn.pra05;

public class Person {
    protected void print(){
        System.out.println("Person!");
    }
}
package com.learn.pra06;

import com.learn.pra05.Person;

public class Worker extends Person{

    public void personPrint(){
        super.print();
    }

    public static void main(String[] args){
        Worker worker=new Worker();
        worker.personPrint();
    }
}

上面却能运行,不是说Work类不能访问到person对象的方法区吗?但是super确实是特例,我的一种解释就是super为worker对象里的参数,该参数指向了person对象的方法区,所以我们通过super间接的访问到了,其实还是必须通过子类对象来调用父类的protected方法,符合protected权限的要求!

聪明的你肯定想到了若是protected static 修饰的保护型静态方法呢?

不同包下的静态方法访问:

package com.learn.pra05;

public class Person {
    protected static void speak(){
        System.out.println("Person speak!");
    }
}
package com.learn.pra06;

import com.learn.pra05.Person;

public class Worker extends Person{
    public static void main(String[] args){
        Person person = new Person();
        person.speak();
    }
}

上面这段代码不仅编译通过,而且运行结果为:Person speak!

是否吃惊呢?还是早已知晓呢?解释在上面已给出,此处再啰嗦一下,因为Work类继承了Person类,包括保护型区域,而该区域里含有protected static void speak()这个方法,又因为静态方法访问会被编译方法器静态绑定无需经过对象去访问方法表,比如此次的字节码为:INVOKESTATIC com/learn/pra05/Person.speak ()V,所以类Work可以访问对象person的静态区域(其实就是Person类的方法区域)!

总结

以上只是我个人的见解,欢迎指正,我相信在以后我会慢慢理解这一块,可能这块有的地方我写的是错的,但是不重要,重在思考与尝试!

参考文章

原文链接:https://blog.csdn.net/dawn_after_dark/article/details/74453915

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值