类加载执行顺序:
public class ClassLoaderOrder {
public static void main(String[] args) {
new Child();
}
}
class Base{
public static String str = "父类静态全局属性";
public String str1 = "父类普通全局属性";
static{
System.out.println("第一步:");
System.out.println("父类静态代码块");
System.out.println(str+"&&&&&&&&&&&&&&&&&&&&&&&&&");
}
{
System.out.println("第二部");
System.out.println("父类普通代码块");
System.out.println(str+"$$$$$$$$$$$$$$$$$$$$$"+str1);
}
public Base(){
System.out.println("第三部");
System.out.println("父类构造方法");
}
}
class Child extends Base{
public static String str3 = "子类静态全局属性";
public String str4 = "子类普通全局属性";
static{
System.out.println("子类静态代码块");
System.out.println(str3+"&&&&&&&&&&&&&&&&&&&&&&&&&");
}
{
System.out.println("子类普通代码块");
System.out.println(str3+"$$$$$$$$$$$$$$$$$$$$$"+str4);
}
public Child(){
System.out.println("子类构造方法");
}
}
1、首先当我们从最简单的执行main()方法开始,当我们运行时,虚拟机会将类进行编译并加载到内存之中。
2、在创建对象时,系统会先查看该类是否有父类,如果有则执行顺序为:
父类静态成员(Object的最先)和父类静态代码块(它俩顺序按书写顺序) → 子类静态成员和静态代码块 →
父类非静态成员和代码块 → 子类非静态成员和代码块 → 父类构造方法 → 子类构造方法
上面打印结果:
第一步:
父类静态代码块
父类静态全局属性&&&&&&&&&&&&&&&&&&&&&&&&&
子类静态代码块
子类静态全局属性&&&&&&&&&&&&&&&&&&&&&&&&&
第二部
父类普通代码块
父类静态全局属性$$$$$$$$$$$$$$$$$$$$$父类普通全局属性
第三部
父类构造方法
子类普通代码块
子类静态全局属性$$$$$$$$$$$$$$$$$$$$$子类普通全局属性
子类构造方法
上面的结果显示先调用的父类构造方法再调用的子类代码块,这点有点和上面的顺序不同,查找原因中。
下面上一组挺有趣的java问题,下面代码不遵循java代码编写规则,仅仅为了构建这样题目:
public class ClassLoaderDemo {
public static void main(String[] args) {
new Son();
}
}
//父类
class Father{
private int i = 2;
public Father(){
System.out.println("构造方法"+i);
display();
}
public void display(){
System.out.println(i+"父类");
}
}
//子类
class Son extends Father{
private int i = 22;
public Son(){
System.out.println("构造方法子类");
i = 222;
}
public void display(){
System.out.println(i+"子类");
}
}
感觉结果是多少呢?
答案为:0
刚开始想了很久,但是如果懂java的动态绑定就好理解多了。
程序执行顺序:
①:为父类的非静态成员变量分配内存空间,并初始化为默认值【默认值不是你指定的值,而是数值类型就是0,引用类型就是null,boolean就是false
②:为子类的非静态成员变量分配内存空间,并初始化为默认值【默认值不是你指定的值,而是数值类型就是0,引用类型就是null,boolean就是false````】
③:默认调用父类的无参构造器【如果用super()显示调用父类的其他构造器,则就不调用该无参构造器了】,注意:在执行构造器中的代码的时候,会先为父类的非静态成员变量赋值,此处赋的值就是你指定的那个值了,
如private int i = 2; 就把2赋值给变量了,并且还执行非静态代码块,至于先赋值还是先执行代码块,取决于你写的先后顺序;做完这些之后,才执行构造器中的代码;
④:执行子类构造器,在执行之前,也是和③一样,先处理本类中的非静态变量和代码块
所以,在第三部调用父类构造器的时候,会执行构造器中的
this.display();
方法,但是这个this到底指谁呢?是Base,还是Sun呢?是这样的,当用this调用变量时,就是编译时的类型,是Base【如果你在构造器中加一句输出this.i,打印出的就是Base的2】,当用
this调用方法时,就是当前正在引用对象的实例,此时由于,它动态绑定到子类(new的是子类),所以去输出子类的i,但是,此时还没有到达第④步,子类中的i只是被分配了内存,默认了0值,所以输出是0;