类加载顺序?
一个类中有静态变量、静态方法、静态代码块、成员变量、成员方法、非静态代码块、构造器,那么他们执行的顺序是什么呢?如果该类继承了父类,那么执行顺序又是什么样的呢?
正常情况下:超类的静态变量和代码块 - - - > 父类的静态变量和代码块 - - - > 子类的静态变量和代码块 - - - > 超类的非静态变量和代码块- - - > 超类的构造器 - - - > 父类的非静态变量和代码块 - - - > 父类的构造器 - - - > 子类的非静态变量和代码块 - - - > 子类的构造器
前置知识:
- 当我们实例化对象时,jvm会先使用类加载器ClassLoader先将类加载至内存。然后再实例化,并且对一个类实例化多个对象时类只加载一次
- 类中有静态的变量、静态方法等静态资源和非静态的资源,静态的资源属于类所有,在类加载时就要分配内存空间。而非静态资源属于对象所有,只有在实例化时才会分配内存空间。这也就是为什么在静态方法中不能使用非静态变量的原因,因为两者加载的时机不一样。
public static String str; // 静态变量属于类所有,在类加载时就要分配内存空间
public int x; // 非静态变量属于对象所有,在类实例化时才会分配内存空间
- 类加载和实例化对象时,只有构造器的方法会执行,而静态方法和非静态方法不会自动执行,除非静态变量和非静态变量或者构造器中调用了方法,方法中的代码才会执行
// 只有当成员变量或者构造器中调用了方法f1,方法f1中的输出语句才能执行
public int x = f1();
public int f1(){
System.out.println("in f1()...");
return 5;
}
- 子类继承父类时,可以重写父类中的方法,但是静态方法没有重写,即使语法上是符合重写的定义的。
class A {
public static int f1() {
return 1;
}
public void f2() {
}
}
class B extends A {
// 该方法虽然与父类中的f1()在重写的语法上相同,但是并不属于方法的重写
public static int f1() {
return 5;
}
// 该方法是对父类中的 f2() 方法的重写。
public void f2() {
}
}
- 源代码只是草稿,真正执行的是经过编译生成的字节码,字节码也是一些指令,最终是由 0 1这些二进制数在计算机中执行的。所以源代码中有些代码是缺省的。如① 调用f1() 方法时省略了 this,而在构造器的第一行缺省了 super()。
默认情况下,子类的构造器执行时会先调用父类的无参构造器,因为子类的构造器第一行缺省了 super();,当父类中没有无参构造器时,子类中的构造器第一行必须显示的添加父类的构造器方法,如:super(5);
// A中有默认的无参构造器
class A {
public void f1() {
System.out.println("A.f1()");
}
}
class B extends A {
public B(int x) {
// 该处缺省了 super();
f1(); // 该处的代码实际为 this.f1(); 如果是new B(),此处输出:A.f1() 如果是 new C(),此处输出C.f1()
}
}
class C extends B{
// 由于B类中没有无参构造器,所以需要在第一行显示的添加 super(x);
public C() {
super(5);
}
public void f1() {
System.out.println("C.f1()");
}
}
1.加载一个类时的情况
实例化A的对象,A中定义了构造器,非静态的变量、方法、代码块,静态的变量、方法、代码块。
class A {
public A() {
System.out.println("A.A()");
}
String str = f2();
public String f2() {
System.out.println("A.f2()");
return "hello";
}
{
System.out.println("A.block()");
}
static int a = f1();
static {
System.out.println("A.static block()");
}
public static int f1() {
System.out.println("A.f1()");
return 1;
}
}
public class Test {
public static void main(String[] args) {
new A();
}
}
结果:
A.f1()
A.static block()
A.f2()
A.block()
A.A()
结论:静态变量 / 静态代码块 - - - > 非静态变量 / 非静态代码块 - - - > 构造器
2. 当要加载的类有父类或者超类时
C类继承了B,B类又继承了A类,A、B、C中均只有一个无参的构造器。
package com.woniuxy.staticDemo;
class A {
public A() {
System.out.println("A.A()");
}
}
class B extends A {
public B() {
System.out.println("B.B()");
}
}
class C extends B {
public C() {
System.out.println("C.C()");
}
}
public class Test {
public static void main(String[] args) {
new C();
}
}
结果:
A.A()
B.B()
C.C()
当执行 new C() 时,因为C继承了B,所以先去加载了B,而B又继承了A,所以先加载A,然后是B,最后是C
结论:先加载超类,在加载父类,最后加载子类
3. 父类和子类中均有静态及非静态的变量和代码块
B类继承A类,A、B中均有构造器,非静态的变量、方法、代码块,静态的变量、方法、代码块
class A {
public A() {
System.out.println("A.A()");
}
int a = f1();
public int f1() {
System.out.println("A.f1()");
return 1;
}
static int b = f2();
public static int f2() {
System.out.println("A.static f2()");
return 2;
}
static {
System.out.println("A.static block()");
}
{
System.out.println("A.block()");
}
}
class B extends A {
public B() {
System.out.println("B.B()");
}
int a = f3();
public int f3() {
System.out.println("B.f1()");
return 1;
}
static int b = f4();
public static int f4() {
System.out.println("B.static f4()");
return 2;
}
static {
System.out.println("B.static block()");
}
{
System.out.println("B.block()");
}
}
public class Test2 {
public static void main(String[] args) {
new B();
}
}
结果:
A.static f2()
A.static block()
B.static f4()
B.static block()
A.f1()
A.block()
A.A()
B.f1()
B.block()
B.B()
结论:父类的静态变量 / 静态代码块 - - - > 子类的静态变量 / 静态代码块 - - - > 父类的非静态变量和代码块 - - - > 父类的构造器 - - - > 子类的非静态变量和代码块 - - - > 子类的构造器
静态代码块一定比构造器先执行吗?
通过上面的结论我们知道,先执行静态变量和代码块,然后执行非静态变量和代码块,最后执行构造器,但是任何情况下的顺序都是这样的吗?静态代码块一定比构造器先执行吗?先看一个例子
代码同上面的例子,只是在A的静态代码块中 new B();
class A {
public A() {
System.out.println("A.A()");
}
int a = f1();
public int f1() {
System.out.println("A.f1()");
return 1;
}
{
System.out.println("A.block()");
}
static {
System.out.println("A.static block()");
new B();
}
static int b = f2();
public static int f2() {
System.out.println("A.static f2()");
return 2;
}
}
class B extends A {
public B() {
System.out.println("B.B()");
}
int a = f3();
public int f3() {
System.out.println("B.f3()");
return 1;
}
{
System.out.println("B.block()");
}
static {
System.out.println("B.static block()");
}
static int b = f4();
public static int f4() {
System.out.println("B.static f4()");
return 2;
}
}
public class Test4 {
public static void main(String[] args) {
new B();
}
}
结果:
A.static block()
A.f1()
A.block()
A.A()
B.f3()
B.block()
B.B()
A.static f2()
B.static f4()
B.static block()
A.f1()
A.block()
A.A()
B.f3()
B.block()
B.B()
结论:静态代码块不一定比构造器先执行
过程:new B()时,需要加载B类,因为B继承了A,所以先加载A类,因此先去执行了A的静态代码块,执行过程中遇到了 new B() 代码,所以先中断执行静态变量和代码块,去执行了A的非静态变量、代码块、构造器,再去执行了B的非静态变量、代码块、构造器,完成了B的实例化后,接着刚才中断的地方,继续执行A的静态变量,然后是B的静态变量、代码块,A的非静态变量、代码块、构造器,B的非静态变量、代码块、构造器。
联系:
class Fu {
public int a = f1();
public int f1() {
System.out.println("A");
return 5;
}
public static int f2() {
System.out.println("B");
return 5;
}
public static int b = f2();
static {
System.out.println("C");
}
}
class Zi extends Fu {
int a=10;
static int b=0;
{
System.out.println("1");
}
Zi() {
System.out.println("2");
}
static Fu fu = new Zi(100);
Zi(int a) {
System.out.println(a);
}
static {
b = 20;
System.out.println("3");
}
{
System.out.println(b);
}
}
public class Test5 {
public static void main(String[] args) {
new Zi();
}
}
执行结果:
B
C
A
1
0
100
3
A
1
20
2