前情引入

关于java的继承,初学的时候,一直认为父类中private修饰的东西时无法被子类继承。后来随着学习的深入,见得多了一些,看到一些文章博客的另一种观点:其实父类中的东西,除了构造方法之外,其它的全都是可以被继承的,包括私有的。但是由于private修饰的东西,只能在本类中可见。所以即便子类继承了,也是不可见的。

我一开始听着很玄乎,什么?我继承了我父亲的遗产,但是我自己却无法使用?这是什么逻辑。反正继承了private修饰的内容,也是不可见的,那和没继承也没啥区别,而且也没法验证子类是不是真的继承了private的内容,所以也没多想。

问题来了

今天我再重新温习多态的时候,遇到了问题。可能大家都已经知道了,在多态形式下,如果是访问实例方法,是遵循一条口诀的:编译看左右,运行看右边。(如果忘了多态具体的细节,可以先看看我的另一篇博客:多态细节)

就是说,编译的时候,必须父类(或接口)中有对应的方法,编译才能通过,但是运行的时候,就跑去运行子类中的具体方法了,如果重写了,那么运行的是子类中重写过的方法。如果没有重写看起来好像运行的是父类中的方法,但实际上运行的是子类中被继承下去的方法,因为没有重写,所以效果是和父类中方法效果是一样的。

如此一来,在多态形式下,就不能调用子类中特有的方法,如果要调用,就必须向下转型。但是我今天突发奇想:如果是父类中特有,而子类中没有的方法呢?(实际上这种情况是无法做到的,因为子类中将父类中所有的东西都继承下去,包括private) 我在此之前的理解,只要我把父类中的方法用private修饰,那子类就不会继承了。我猜测,在这样的情况下,应该是编译可以通过,运行的时候会报错。

然后我就开始代码测试:

  1. 父类,有一个private修饰的方法
package demo07.test;
//父类,有一个private修饰的方法
public class Father
{private void method(){System.out.println("父类private的方法");}
}
  1. 子类,什么都没写
package demo07.test;
//子类,什么都没写
public class Son extends Father
{}
  1. 测试类
package demo07.test;
//测试类
public class Test01
{public static void main(String[] args){Father father = new Son();father.method();//报错:'method()' has private access in 'demo07.test.Father'}
}

但是有问题,因为父类中方法是private的,无法在其它的类中使用,所以在测试类中是无法访问到的。编译就报错

只对本类可见,那好,我就把main方法放在父类中,在父类中进行测试。子类不变,父类改成如下:

package demo07.test;public class Father
{private void method(){System.out.println("父类private的方法");}public static void main(String[] args){Father father = new Son();father.method();}
}

这下果然不报错了,编译可以通过。但是程序的运行结果,却让我大吃一惊。程序运行不但没出错,还输出了:父类private的方法。什么??这是什么情况,为什么可以成功执行,子类中明明没有这个method方法的,为什么可以成功运行???(为了便于后面理解,我将这个问题命名为问题1

我自己想不通,也不知道怎么去搜索这个问题的关键词。然后我去请教了老师,老师好像也不是很清楚,或者说不知道怎么给我解释。但是他说:可能和权限修饰符的可见性有关

真相大白之前

在听了老师的建议之后,我就往这方面思考,如果真是权限修饰符可见性造成的。也就是说,父类这个private修饰的method方法,其实是被子类继承下去了的,只是对子类不可见。但是还是有个问题:我在父类的main方法中调用了父类的method方法的,private可以对本类可见,所以没有问题。但是:运行的时候,是在父类中运行子类的private修饰的方法,那也应该不能成功运行才对,但实际上却成功运行了,这又是为什么?(为了便于后面理解,我将这个问题命名为问题2

由此,我又想到了一个问题:父类中一个方法被子类继承下去,那么这个方法的权限修饰符是针对哪一个类而言的呢?

大伙可能都见过和这张差不多的图:

我上面那个问题的意思就是:如果父类中有一个protected修饰的方法被子类继承下去了,那么这些本类、同包、子类 是针对父类而言还是针对子类而言呢?
比如说,我的父类和子类不在同一个包,被继承的方法权限修饰符是protected。如果是针对父类而言,那么我在子类所在的包下建一个测试类,创建子类对象,调用这个方法,应该是不能调用这个方法的,因为和父类不在同一个包下。而在父类所在的包下新建一个测试类,创建子类对象,调用这个方法,应该就可以访问,因为和父类在同一个包中。
而如果是针对子类而言的,情况就和上面是相反的。在父类所在的包下建测试类,就不能调用。而在子类所在的包下建测试类,就可以调用。

上面这段有点饶,但是其实想表达的意思很简单。多读几遍,就能理解了

但其实答案可以简单推测出:如果是针对子类而言的,那么被继承下来的private修饰的内容,在子类中应该是可以被访问的,但是实际上并不能被访问,所以应该是针对父类而言的。
但是不能这样推测:因为我们现在就是在验证private修饰的内容是否可以被继承,像上面那样推测,就代表了我们已经认定了private修饰的内容是可以被继承的。所以还是得用代码先证明权限修饰符是针对父类而言。

验证权限修饰符是针对父类还是子类

用代码验证,思路就是上面叙述那样:父类和子类分别在不同的包中,父类中有一个protected修饰的方法。然后分别在父类所在的包下创建测试类,在测试类中创建子类对象,看子类对象是否能调用从父类中继承下来的那个protected修饰的方法
代码如下:注意所在包

  1. test包下的父类:
package test;
//test包下的父类
public class Father
{protected void method(){System.out.println("父类protected修饰的方法");}
}
  1. test.test包下的子类,什么都没写,但是继承了test包下的父类
package test.test;
import test.Father;
//test.test包下的子类,什么都没写,但是继承了test包下的父类
public class Son extends Father
{}
  1. test.test包下的测试类Test01(和子类同包)
package test.test;
//test.test包下的测试类(和子类同包)
public class Test01
{public static void main(String[] args){Son son = new Son();son.method();//报错:'method()' has protected access in 'test.Father'}
}
  1. test包下的测试类Test02(和父类同包)
package test;
import test.test.Son;
//test包下的测试类(和父类同包)
public class Test02
{public static void main(String[] args){Son son = new Son();son.method();}
}

果然,在和子类同包的测试类Test01中,无法调用这个方法,并且告诉我:method这个方法在父类中是被protected修饰的。而在和父类同包的测试类Test02中,就能正常调用method方法。

至此,我们独立的通过代码验证了,在继承关系中,子类从父类中继承下来的方法,它的权限修饰符是针对父类而言的!
注意:如果是重写了,那么这个方法的权限修饰符就是针对子类而言的

水落石出

证明了:“在继承关系中,子类从父类中继承下来的方法,它的权限修饰符是针对父类而言的!”,我们就能解释问题2了。被子类继承的private方法,也是只对本类(父类)可见的,所以当然就可以在父类的main方法中运行被子类继承下去的private方法了。
问题2解决了,那问题1也就解决了,因为子类继承了父类中被private修饰的方法。所以,方法当然可以被正常调用。

至此,就证明了,在继承关系中,父类中的private方法也是可以被子类继承的,只是这个被继承下来的方法,只针对父类可见。
当然,这个结论正确的前提是:在多态形式下,调用实例方法的时候,最终运行的是子类中的方法。 这个结论是正确的。

再次佐证

当初写了博客之后,很久一段时间也没有再想过这个问题,今天再次复习的时候,同学说到了反射,恍然大悟,反射可以无视封装的嘛,

开始尝试

那么可以尝试用反射,获取子类中的所有方法,查看是否能获取到从父类中继承的私有方法。然后,我对反射有多了一层认识:反射要么获取所有public的方法(getMethods),包括继承下来的方法,要么就只能获取子类声明的方法(getDeclaredMethods),根本无法获取从父类中继承下来的非public方法,所以从父类继承的private方法自然也无法获取到。

转机

但是我想到一个问题:虽然无法从子类获取到这个方法,那我为什么非要从子类里面获取呢?我只要获取到这个方法,然后用子类实例去调用就行了。
那我就用父类的字节码对象,去获取到父类的这个私有的方法,然后用方法对象调用invoke方法,传入一个子类实例,如果能够成功调用,一样也可以说明子类是继承了父类中private方法的嘛。

着手尝试
  1. 父类,只声明了一个private的方法
public class Parent
{private void mehotd1(){System.out.println("父类中的私有方法");}
}
  1. 子类,继承了父类,什么方法都没有声明
public class Child extends Parent
{
}
  1. 测试类,尝试用反射调用方法
import java.lang.reflect.Method;/*** @author ql* @date 2020/10/30* @time 17:32**/
public class Test01
{   // 采用了两种不同的方式获取了这个方法对象,不过原理都差不多public static void main(String[] args) throws Exception{test01();test02();}public static void test01() throws Exception{Class parentClass = Parent.class;Method mehotd1 = parentClass.getDeclaredMethod("mehotd1");//System.out.println(mehotd1);mehotd1.setAccessible(true);Child child = new Child();mehotd1.invoke(child);}public static void test02() throws Exception{Child child = new Child();Class superclass = child.getClass().getSuperclass();Method mehotd1 = superclass.getDeclaredMethod("mehotd1");//System.out.println(method01);mehotd1.setAccessible(true);mehotd1.invoke(child);}
}
结果

程序完全没问题,成功调用,控制台打印了两句“父类中的私有方法”,所以这也再次佐证了java中私有的内容也是可以被继承的

分割线---------------------------------------------------------------------------------------------
2023.01.03更新:

人是不断成长的,对事物的看待、认识也是如此。今日在学习jvm相关内容时,又联想到了这个问题,所以又来更新了。想想自己原本的问题:父类private修饰的内容能否被继承?其实是有纰漏的,既然在说能不能被继承,那么“继承父类中的内容”中“继承”的定义又是什么呢?如果按照通常的认为:能够直接访问即为继承,那确实是不能直接访问,但却能通过反射访问又该如何解释?到底是能被继承呢还是不能被继承呢?

小时候,我们在看电视或者日常生活中,总会把自己或者电视剧中的主角当作正义的一方,而反派或者跟自己有过节的人就是那种坏的透透的人。但其实这种看法很片面,每个人,有自己的立场、想法,并不不是大多数人所认为的就一定是对的,应该站在不同的角度去分析,去思考,看透本质,而不是浮于表面。

回到这个问题上来也是如此,我们不应该执着与“是否能被继承”,而应该去理解,代码纠结是怎样运行的,以此来解释这个问题。在尚硅谷宋红康老师jvm课程中有这么一个观点:

这个逻辑与我对java的认知不冲突,反而还能解释很多问题,应该是没错的。关于编译看左、运行看右这句顺口溜,其实也没错,但是他只是初学者用于加深记忆的,并经不起深入的引申。我自己在分析过程中,将这句助记的顺口溜引申为:多态最终运行的方法一定是子类中,这样的引申并不正确。

所以我们在面对这个问题的时候,不应该简单的描述为:能不能继承,而是应该描述为:父类private修饰的内容,的确不能直接使用子类对象去访问,但通过反射,还是能够访问。并且把上面重写的本质解释出来,才是这个问题的最佳答案。

评论区有个兄弟的评论,也很合理:

有什么不对或不懂的地方,欢迎讨论交流

java继承关系中,父类private修饰的内容真的不能被继承吗?NO相关推荐

  1. Java继承关系中,父类不可以强制转换成子类

    总结: 父类不可以强转为子类 父类中只存放共性抽象类方法,子类可以存放特性方法,所以子类 首先构建好两个类 构建继承类方法-点击查看 在Main中强制将父类转换为子类 Student a = (Stu ...

  2. 在java继承机制中 父类中的私有_Java中子类能继承父类的私有属性吗?

    前段时间去听老师讲课的时候,老师告诉我子类是可以继承父类所有的属性和方法的.当时我是极其疑惑的,因为之前学校考试时这个考点我记得很清楚:子类只能继承父类的非私有属性和方法.老师给我的解释是这样的--先 ...

  3. java 实例方法直接调用超类的实例方法_Java继承关系中,父类方法使用实例变量和调用实例方法的探究...

    面向对象编程中,某一个实例方法使用实例变量和调用其它实例方法的情况是常见的.当存在继承关系时,这种情况就变得复杂起来.以下就对继承关系中,父类的某实例方法使用实例变量和其它实例方法的情况进行探究.因为 ...

  4. java 继承 extends_java中的继承 (extends) 详解

    继承(extends) 1. 父类与子类 父类也叫基类.超类  子类也叫派生类  在继承的而关系中,"子类就是一个父类".也就是说,子类可以被当做父类来看待.例如父类是员工,子类是 ...

  5. c++ map 析构函数_C++|类继承关系中的虚函数、虚析构函数、虚基类

    在继承关系中,虚函数.虚析构函数.虚基类中使用的关键字virtual都是在告诉编译器,此处要进行特殊处理: 虚函数:函数重写时的要求编译器动态绑定来实现多多态 : 虚析构函数:当基类指针指向在堆内实现 ...

  6. 继承关系中的拷贝构造函数和赋值操作重载函数分析

    文章目录 1 继承关系中的拷贝构造函数和赋值操作重载函数分析 1 继承关系中的拷贝构造函数和赋值操作重载函数分析 在继承关系中,如果子类未实现拷贝构造函数,那么在子类进行拷贝构造操作时,会直接调用父类 ...

  7. 【转】继承过程中 父类子类的 字段方法 内存分配 (非java语言)

    名人名言:思想好比火星:一颗火星会点燃另一颗火星.一个深思熟虑的教师和班主任,总是力求在集体中创造一种共同热爱科学和渴求知识的气氛,使智力兴趣成为一些线索,以其真挚的.复杂的关系--即思想的相互关系把 ...

  8. 我的心像洋葱,需要一层一层剥开,你才明白这颗心多爱你:Abstract中继承关系中,变量初始化与构造方法的关系

    package cm.abstracts.limit;//这个类用来财务室:在任何一个类的构造执行完之前,所有属性的内容都是其对应数据类型的默认值 public class CaseFfourtyFo ...

  9. java继承music_java中的继承

    什么是继承 继承就是子类继承父类的特征与行为.特征即成员属性,行为即成员方法 为什么需要继承? 提高代码复用性,减少代码冗余,提高开发效率. 比如Dog类里有姓名和年龄,Cat类也有姓名和年龄,这时就 ...

最新文章

  1. camelot工具进行pdf表格解析重建
  2. 高压线下,恶俗短视频为何仍在批量生产?
  3. 数据库读写分离(aop方式完整实现)
  4. boost::compressed_sparse_row_graph用法的测试程序
  5. 精选 GitHub 值得收藏的100个前端项目
  6. 总共4行代码使用fastxml.json实现Java对象的序列化和反序列化
  7. 判断linux进程是否存在
  8. 你以为我确定能解决难题?也是硬着头皮向前冲
  9. 中国大陆IP过滤器-Java实现
  10. Ueditor编辑器修改字体和字号?
  11. Windows服务器双网卡冲突
  12. php显示2038年,PHP转换超过2038年日期出错如何解决
  13. 社交网络的发展及趋势
  14. 苏宁0元撸货福利来了!只限新人!老用户放弃吧!便宜只能占一次
  15. linux系统怎么设置ftp账号密码错误,linux系统FTP设置账号密码
  16. 如何去掉a标签的下划线及伪类样式
  17. chp02-01文本文件的读写
  18. mysql导出nb3文件_MySQL导入导出.sql文件
  19. android webview 找不到网页,webview loadUrl 显示“找不到网页”
  20. 从零开始开发IM(即时通讯)服务端附源码

热门文章

  1. SAP MM学习总结
  2. Qt操作excel时文件路径问题
  3. 微软开源 AirSim,训练无人机和无人车
  4. 写一篇进入“航校”半年,校园学习生活的心得体会和收获感悟,要求字数不少于500字...
  5. 【PC】关掉Chrome浏览器“由贵单位管理”
  6. 103 保序回归 isotonic regression
  7. H5网页版聊天之websocket
  8. U盘安装Ubuntu失败
  9. VLOOKUP函数用法(1)将两个表按相同数据进行匹配
  10. Access to script at ‘file:///C:/study...‘ from origin ‘null‘has been blocked by CORS policy