彻底理解Java中的访问修饰符

一道简单的题目

看到这个标题时可能很多朋友会嗤之以鼻,难不成简单的访问修饰符还有什么新花样吗?别急,麻烦您先看一下这个简单的题目。


OverrideTrap.png

这无非就是一个简单运行时多态问题,众所周知,Java的多态分为编译时多态和运行时多态两种。其中, 编译时多态主要指方法的重载,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。 运行时多态有三个条件:

  1. 继承

  2. 方法重写(覆盖)

  3. 向上转型

main函数中,base指向的实际上是Impl对象,因此在调用sayHi()方法时,会执行Impl对象的sayHi()方法而非Base对象的sayHi()方法,因此,输出的内容显然是Impl:Hi。

实际上是这样的吗?然而很不幸,输出的却是 Base:Hi。问题出在哪儿?难道这不满足运行时多态的条件?不应该啊,首先,Impl类继承了Base类,其次,Impl类重写了Base类的sayHi()方法,最后,调用时进行了向上转型。三个貌似条件都满足,但是等等,回忆一下方法重写的要求:

  1. 子类方法的访问权限必须大于等于父类方法;

  2. 子类方法的返回类型必须是父类方法返回类型或为父类方法返回类型的子类型。

    貌似也满足啊,我们再看一下JLS中对方法重写的规定:

Overriding.png

翻译过来大概为:

  1. A是C的超类

  2. C没有继承mA

  3. mC是mA的签名的子签名

  4. 下面的多个条件之一要满足:

    mA是public的

    mA是protected的 mA在C所处的包中具有包访问权限,且mC覆盖了来自C的某个超类中的mA
    ...

    访问修饰符

    看来我们忽略了方法的访问修饰符的问题。Java中访问修饰符规定及其访问范围如下表所示:

    访问权限 当前类 同包 子类 其他包
    public
    protected ×
    default × ×
    private × × ×

    那么,当子类位于当前类内部、同一包下、其他包下时访问权限会发生什么变化呢?是否还遵循表格中的规定呢?对于这个问题,我们只需要记住最大访问权限原则即可,所谓最大访问权限原则即子类的在不同位置时访问权限修饰符表现的实际权限以最大的那个为准。依据该原则,子类在不同位置时对父类中的方法及变量的访问权限如下表所示:

    子类中访问权限 当前类 同包 其他包
    public
    protected
    default ×
    private × ×

    回到题目中,由于子类Impl与父类Base位于同一包下,而Base中的sayHi()方法的修饰符为private,对子类不可见,因此不满足方法重写的要求,因此调用的仍然是Base中的方法,而非子类中的方法。

    这个细节看起来很不起眼,但实际上却包含了访问修饰符的权限及方法重写、多态等细节,算得上是一道好题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 面向对象No6 面向对象 OO Object Oriented 编程时以对象为单元,封装数据和逻辑,以此提...
    征程_Journey阅读 1,210评论 0 2
  • 访问修饰符 字段、方法都可以叫做类的成员Member,它们都需要定义访问级别。 访问级别的用处在于控制成员在哪些地...
    沉麟阅读 820评论 0 0
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 1,236评论 0 5
  • 我至今还记得, 童年时候,有一天 我吃零食 从那吃着。 突然,我姐姐走过,吓我一跳! 我只觉得一阵不知所措 啪了一...
    嗯嗯2580阅读 200评论 0 0
  • 今天又被路人甲问到一个女孩子干嘛跑这么远,说大雁最终也要南飞,人最终也要落叶归根。其实,即使是流浪,有梦想有伙伴...
    小易杜阅读 183评论 0 0