class A {
static {
System.out.print("a");//只有在类加载时候会执行一次
}
public A() {
System.out.print("x");
}
}
class B extends A {
static {
System.out.print("b");
}
public B() {
System.out.print("y");
}
}
public class Test {
public static void main(String[] args) {
A ab = new B();
System.out.println();
ab = new B();
}
}
输出:abxy xy
执行过程:
A ab = new B();
执行时候第一使用到A、B类,JVM发现没有加载A、B的信息,故先加载,由于B继承了A类,所以会先加载A再加载B,在加载的过程中会执行static块完成类的初始化。所以会先输出ab,此时创建B对象时候,会先执行A的构造方法在执行B的构造方法,故在输出xy,所以最后输出abxy
当执行到:
ab = new B();
时候发现已经加载过A、B的字节码了,故不会再加载了,所以不会输出ab,直接输出xy即可.
如果代码修改为:
class A {
static {
System.out.print("a");// 只有在类加载时候会执行一次
}
public A() {
System.out.print("x");
}
}
class B extends A {
static {
System.out.print("b");
}
public B() {
System.out.print("y");
}
}
public class Test {
public static void main(String[] args) {
B ab = new B(); // 由于A ab = new B()=> B ab = new B()
System.out.println();
ab = new B();
}
}
输出还是:
abxy
xy
当执行B ab = new B()时候,由于JVM事先就会知道B是继承至A的,所以需要先加载A,如果不先加载A的话,无法完成子类B的加载!所以类的加载顺序和构造器执行顺序一致,先父类再子类!
注意:容易糊涂的地方是,以为B ab =new B();以为创建B所以先加载B之后再加载A,这种理解就错误了,因为对于对象的创建是从父类开始的!
拓展:非静态代码块的执行顺序也是先父类,在子类!但是非静态代码块执行顺序在静态代码块之后,构造器之前!静态代码块只会执行一次(类加载时候执行),而非静态代码块会多在创建对象时候多次执行!
实践代码:
class A {
static {
System.out.print("a");// 只有在类加载时候会执行一次
}
{
System.out.print("a-a");
}
public A() {
System.out.print("x");
}
}
class B extends A {
static {
System.out.print("b");
}
{
System.out.print("b-b");
}
public B() {
System.out.print("y");
}
}
public class Test {
public static void main(String[] args) {
B ab = new B(); // 由于A ab = new B()=> B ab = new B()
System.out.println();
ab = new B();
}
}
输出:
aba-axb-by
a-axb-by
如果两个类没有继承关系的话,类的加载顺序就是按照代码的执行顺序进行加载的。
class C {
static {
System.out.print("c");
}
public C() {
System.out.print("x");
}
}
class D {
static {
System.out.print("d");
}
public D() {
System.out.print("y");
}
}
public class Test1 {
public static void main(String[] args) {
C c=new C();
System.out.println();
D d=new D();
}
}
输出:
cx
dy