- 博主简介:努力学习的预备程序媛一枚~
- 博主主页: @是瑶瑶子啦
- 所属专栏: Java岛冒险记【从小白到大佬之路】
学习了继承、多态
本节,将通过一个简单的例子,从概念上介绍原理(实际实现的细节与此有所差别),更好的清晰明了的掌握继承!
这里我们用两个类来演示一下:
public class Father {
public static int count;
private int a;
static {
System.out.println("Father类的静态初始化代码块被执行");
count = 1;
}
{
System.out.println("Father类的实例代码块被执行");
a = 1;
}
public Father() {
System.out.println("Father类的构造器被调用");
a = 2;
}
protected void enjoy() {
System.out.println("enjoy smoking");
}
public void action() {
System.out.println("Start");
enjoy();
System.out.println("Ended");
}
}
public class Child extends Father {
public static int count;
private int a;
static {
System.out.println("Child类的静态代码块被调用");
}
{
System.out.println("Child类的实例代码块被调用");
}
public Child() {
System.out.println("Child类的构造方法被调用");
}
@Override
protected void enjoy() {
System.out.println("enjoy studying");
}
}
当我们在测试类中创建子类Child c = new Child()
,由于类第一次被使用,类会被加载进内存。那类是怎么样被加载进内存的呢,又把哪些信息加载进了内存哪里?这之间发生了什么,我们接下来将进行系统的讲解。
在类第一次被使用的时候,类会被JVM加载进内存中的方法区。方法区中存储着类的信息,类的信息包括哪些呢?
知道了类在何时加载进内存、加载进内存的哪个位置、将哪些信息存储、加载流程之后,我们来看看此时方法区的内存分别是怎样的。
就我们的例子来说,完整加载流程走完之后,内存会保存三个类的信息,分别是:顶级父类Object、Father、Child
在new 对象
时,首先第一步是将相应的类及其父类加载进内存,并完成其初始化,第一步在Part2已经讲解完,所有类加载并初始化后内存布局已经给出。我们现在来看一下Child c = new Child()
JVM所要做的第二步:在堆内存中创建对象的过程。
关于实例方法调用,实例方法重写和动态绑定,已经在这两篇文章中详细叙述
但是这里就基本原理的角度,再次讲解一遍,使印象深刻
再次强调:静态属性、静态方法和非静态的属性都可以被继承和隐藏(hide),而不能够被重写!也更谈不上动态绑定。
Child c = new Child();
f = c;
c.action();
f.action();
c.action()
System.out.println("Start");
输出“Start"enjoy()
;到实际类型Child中寻找enjoy方法,找到了并且调用System.out,println("End");
f.action()
(节约时间的话,可以不用看了,和c,action()
一模一样)
System.out.println("Start");
输出“Start"enjoy()
;到实际类型Child中寻找enjoy方法,找到了并且调用System.out,println("End");
【总结】
这里f.action()
和c.action()
所执行的结果完全相同,为什么?因为f和c指向的是堆内存中同一对象,同一对象的实际类型唯一。而调用对象实例方法看的就是实际类型,所以自然方法调用所指向的结果相同!
这其实就是动态绑定的实现机制:根据对象的实际类型调用实例方法,在实际类型中找不到,就逐级向上(父类)中查找。
【补充】:虚方法表
根据上述讲解,到现在,我们堆方法重写,动态绑定已经非常清楚。
我们看到,在判断调用实例方法时,要做一个操作:向上查找,直到找到。如果继承只有一两层还会,如果继承层次太深,每一次都要进行这种查找,效率比较低。于是为了优化,提出了虚方法表的概念。
所谓虚方法表,就是在每个类在创建的时候,为其创建一个表,来记录该类对象所有动态绑定方法(包括从父类继承过来的方法)及其地址。一个方法只有一条,如果该类重写了从父类继承过来的方法,那么该方法记录的就是子类重写之后的那个方法。
有了虚方法表,只要我们确定了该对象的实际类型,就可以通过查该类型的虚方法表的方式来直接确定调用哪个实例方法。效率就会提高很多。
我们知道,只有方法才谈动态绑定,属性是不存在什么动态绑定的。一下理解都是正确的:
访问过程:
Java岛冒险记【从小白到大佬之路】
LeetCode每日一题–进击大厂
Go语言核心编程
算法