运行class文件时内存加载顺序。
测试程序一:
class Father
{
Father(){}
Father(int age)
{
System.out.println("father age:"+age);
}
}
class Son extends Father
{
static int i = 5;
static {
System.out.println("静态代码块"+i);
}
{
System.out.println("构造代码块");
}
Son()
{
//默认这里有一个东西 super();
//this(20);
System.out.println("son");
}
Son(int age)
{
super(age);
System.out.println("son age:"+age);
}
}
public class Test1 {
public static void main(String[] args) {
Son s = new Son(); //father;son
Son s1 = new Son(20);//father;son age:20
}
}
测试程序二:
class Person {
int i = 5;
int j;
int k;
{
System.out.println("这是构造代码块"+i+j+k);
}
Person(int j, int k) {
System.out.println("i="+i);
System.out.println("j="+j);
System.out.println("ceshiyuju");
System.out.println("k="+k);
this.k = k;
System.out.println("k="+k);
this.j = j;
}
}
public class Test1 {
public static void main(String[] args) {
Person p = new Person(10,20);
}
}
运行class文件时内存加载顺序。
类加载的过程:类的成员是按代码顺序加载的,静态代码块随着类的加载而被执行,也就是说一旦加载上独立代码块就立即执行。故静态代码块中的语句只能访问写在它前面的静态成员,若访问后面的会编译报错。类加载完了独立代码块也就执行完了。
整个类加载完毕完毕后,先从主函数开始执行。若其中的语句调用了别的类,则继续加载别的类。
这里重点介绍一下:主函数中有创建对象的语句,则对象的初始化过程如下:
以“Person p = new Person(10,20);”为例
这句话的一系列执行顺序:
0. 先在占内存中开辟一个空间给类类型变量P;
1. 因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中。
2. 执行该类中的static 代码块(如果有的话),给Person.class进行初始化。
3. 在堆内存中开辟空间,分配内存地址。
4. 在堆内存中建立对象的特有属性。并进行默认初始化。
5. 对属性进行显示初始化。(即在 类 中定义变量时指定的初始化值,如程序二中的i)
6. 对对象进行构造代码块初始化。
7. 对对象进行对应的构造函数初始化。(即执行构造函数中的初始化语句对对象特有属性进行初始化,如下面程序中的j,k)
8. 执行构造函数中除赋值外的其他语句(如程序2中Person()方法内的打印语句);
9. 将内存地址赋给栈内存中的变量P。
10. 另外,第4步之前应先执行本构造方法中调用的父类构造方法。其执行方法与Person()构造方法相似。
弄清了这些过程,你就再也不用为所有执行顺序的问题而纠结了。
|