类加载顺序?静态代码块一定比构造器先执行吗?

类加载顺序?

一个类中有静态变量、静态方法、静态代码块、成员变量、成员方法、非静态代码块、构造器,那么他们执行的顺序是什么呢?如果该类继承了父类,那么执行顺序又是什么样的呢?
正常情况下:超类的静态变量和代码块 - - - > 父类的静态变量和代码块 - - - > 子类的静态变量和代码块 - - - > 超类的非静态变量和代码块- - - > 超类的构造器 - - - > 父类的非静态变量和代码块 - - - > 父类的构造器 - - - > 子类的非静态变量和代码块 - - - > 子类的构造器

前置知识:

  1. 当我们实例化对象时,jvm会先使用类加载器ClassLoader先将类加载至内存。然后再实例化,并且对一个类实例化多个对象时类只加载一次
  2. 类中有静态的变量、静态方法等静态资源和非静态的资源,静态的资源属于类所有,在类加载时就要分配内存空间。而非静态资源属于对象所有,只有在实例化时才会分配内存空间。这也就是为什么在静态方法中不能使用非静态变量的原因,因为两者加载的时机不一样。
public static String str; // 静态变量属于类所有,在类加载时就要分配内存空间
public int x; // 非静态变量属于对象所有,在类实例化时才会分配内存空间
  1. 类加载和实例化对象时,只有构造器的方法会执行,而静态方法和非静态方法不会自动执行,除非静态变量和非静态变量或者构造器中调用了方法,方法中的代码才会执行
// 只有当成员变量或者构造器中调用了方法f1,方法f1中的输出语句才能执行
public int x = f1();
public int f1(){
	System.out.println("in f1()...");
	return 5;
}
  1. 子类继承父类时,可以重写父类中的方法,但是静态方法没有重写,即使语法上是符合重写的定义的。
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() {
		
	}
}
  1. 源代码只是草稿,真正执行的是经过编译生成的字节码,字节码也是一些指令,最终是由 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

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值