java继承细节,继承内存布局和访问权限规则
访问权限
java继承细节
1.子类会继承父类所有的属性和方法,private的属性和方法不能直接访问,子类要通过父类提供的方法来访问到private属性和方法。
2.子类在创建对象,调用构造器时,肯定会连带父类的构造器也一起进行,即父类也要初始化,顺序是先进行父类构造器,后进行子构造器,不管是使用子类的哪一个构造器,都会先执行父类的默认无参构造器,如果没有默认无参构造器,那么必须使用super(形参列表)去指明是使用父类的哪一个构造器来完成父类的初始化,否则编译会报错。个人理解为,之所以这样做,是因为子类不能访问到private以及某些默认的属性,不能保证完成初始化,而用父类就肯定能完成初始化。
3.由于子类父类是相对而言的,所以一个子类在创建对象后要初始化时,进行的父类初始化不只是直接父类要进行,父类的父类,父类的父类的父类,一直往上到顶级父类object(java所有类都继承于它),这条线路上所有的父类都要进行初始化,并且是从顶级父类开始初始化,然后一直往下,直到当前正在初始化对象的子类。
4.super必须放在构造器第一行,this也是,所以同一个构造器这俩不能共存。
5.java只支持单继承,要实现继承多个类,只能是一层一层继承,比如A想继承B,C,那么可以让B继承C,然后A继承B,或者C继承B,然后A继承C
6.继承不能滥用,继承的初衷,是为了提高代码复用性,拓展性和可维护性,这个复用性是指:当遇到一些有共同部分的类时,不用在每个类都重复写这些共同部分的代码
比如篮球运动员和田径运动员
class BaskballPlayer { String name; int age; int finalChampion; int allStar; } class Athlete{
String name; int age; int olympicChampion; }
虽然他们有所区别,但也有很多同样的地方,比如名字年龄,那么如果在开发时还要写到其他种类的运动员,那么将会写很多的名字和年龄,这很花时间还没有什么意义,而且以后要是不需要显示运动员的年龄,那岂不是还要一个个去删?
而且修改或者删除时万一出错了,那更是崩溃中的崩溃,所以我们直接抽象出名字和年龄这两个属性,设一个运动员类,这样,每种运动员直接继承就行了,能少写很多代码,要是需要增删改查这两个属性也只需要直接在运动员类中操作即可,大大提高了维护性和拓展性。
class Player { String name; int age; } class BasketballPlayer extends Player{ int finalChampion; int allStar; } class Athlete extends Player{ int olympicChampion; }
所以,继承是要子类和父类有相关性才继承,遵循“is-a”原则
比如person和music
person is a music?
no
music extends person / /不合理
animal
cat is a animal?
yes
cat extends animal // 合理
java继承内存布局
以如下代码为例
public class Test { public static void main(String[] args) { Son son = new Son(); } } class GrandPa { String name = "爷爷"; String hobby = "看黄书"; } class Father extends GrandPa{ String name = "爸爸"; int age = 42; } class Son extends Father{ String name = "儿子"; }
当
Son son = new Son();
的时候
第一步,现在方法区加载类的信息,从顶级父类Object开始
ps:个人对这个加载顺序的理解为,如果是先从最后的son开始加载,那他是继承前面的父类的,现在父类没加载,他没办法加载完毕自己的,那么就要预留空间,等前面的加载完,再去补充完整自己的,这样的步骤非常琐碎,而且也不知道预留多少空间好,效率低,而从顶级父类开始,在子类加载信息时,直接就能继承过来,一步到位
第二步,在堆中开辟Son对象的内存空间,从父类到子类的顺序
第三步,返回堆中地址给栈中的son对象引用
当Son对象访问一个属性时,按son-->Father-->GrandPa---->Object类的顺序来对着名字找,当找到相同属性名就停止,哪怕Son对象对当前找到的这个属性没有访问权限,也不会再找下去。