Java类中各部件加载顺序,面试必问!!!

本文通过具体示例详细解析了Java中类加载的顺序,包括静态块、静态变量、普通代码块、构造器等组件的加载时机及顺序,并探讨了继承类间的加载差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    面试或者笔试中经常会遇到的一道题目就是Java类中各部件加载的顺序,通常会给你一段代码让你写出输入的结果,一般包含:静态块、静态变量、普通代码块、构造器、子类重写父类的方法等等。下面我们来看一段代码:

Father类代码:

package com.extend;

public class Father {
	public static int Father_A = getA(); 
			
	int Father_C = getC();
	
	{
		System.out.println("我是Father的第一个普通代码块");
	}
			
	static{
		System.out.println("我是Father的第一个静态块");
	}		
	
	{
		System.out.println("我是Father的第二个普通代码块");
	}
	
	static{
		System.out.println("我是Father的第二个静态块");
	}
			
	public static boolean Father_B = getB();
	
	public static int getA(){
		System.out.println("我是Father获取静态变量A的方法");
		return 100;
	}
	
	public static boolean getB(){
		System.out.println("我是Father获取静态变量B的方法");
		return true;
	}
	
	public int getC(){
		System.out.println("我是Father获取变量C的方法");
		return 100;
	}
	
	public Father(){
		System.out.println("我是Father的构造器");
	}
	
	public void hello(){
		System.out.println("Hello!我是Father");
	}
}

Son类代码:

package com.extend;

public class Son extends Father {
	public static int Son_A = getA(); 
	
	int Son_C = getC();
	
	{
		System.out.println("我是Son的第一个普通代码块");
	}
			
	static{
		System.out.println("我是Son的第一个静态块");
	}		
	
	{
		System.out.println("我是Son的第二个普通代码块");
	}
	
	static{
		System.out.println("我是Son的第二个静态块");
	}
			
	public static boolean Son_B = getB();
	
	public static int getA(){
		System.out.println("我是Son获取静态变量A的方法");
		return 100;
	}
	
	public static boolean getB(){
		System.out.println("我是Son获取静态变量B的方法");
		return true;
	}
	
	public Son(){
		System.out.println("我是Son的构造器");
	}
	
	public void hello(){
		System.out.println("Hello!我是Son");
	}
 }

接下来在看main方法(main方法在Son类中)如何执行:

public static void main(String[] args) {
		System.out.println("main start");
		System.out.println("-------分隔符--------");
		Father s = new Son();
		System.out.println("-------分隔符--------");
		s.hello();
		System.out.println("-------分隔符--------");
		Father f = new Father();
		System.out.println("-------分隔符--------");
		f.hello();
		System.out.println("-------分隔符--------");
	}

想清楚后来看答案,是否和预想的一致呢?




输出结果:

我是Father获取静态变量A的方法
我是Father的第一个静态块
我是Father的第二个静态块
我是Father获取静态变量B的方法
我是Son获取静态变量A的方法
我是Son的第一个静态块
我是Son的第二个静态块
我是Son获取静态变量B的方法
main start
-------分隔符--------
我是Father获取变量C的方法
我是Father的第一个普通代码块
我是Father的第二个普通代码块
我是Father的构造器
我是Father获取变量C的方法
我是Son的第一个普通代码块
我是Son的第二个普通代码块
我是Son的构造器
-------分隔符--------
Hello!我是Son
-------分隔符--------
我是Father获取变量C的方法
我是Father的第一个普通代码块
我是Father的第二个普通代码块
我是Father的构造器
-------分隔符--------
Hello!我是Father
-------分隔符--------

       到这里可能很多同学要问了,为什么“main start”不是第一行输出呢?这是因为main方法虽然是主进程的入口方法,但是它也需要在类加载完之后才会执行main方法,所以main方法的第一行输出是在所有的静态变量、静态块加载之后执行。这里还有一个容易搞混的地方,在第二次实例化Father类的时候并不会输出Father类静态变量以及静态块中的内容,也就是说静态变量以及静态块只是在第一次实例化时会被加载。

单一类加载顺序:

  1. 静态变量、静态块先执行,如果存在多个则顺序执行;只会加载一次
  2. 如果main方法第一行有输出,则输出第一行内容
  3. 接下来加载普通变量、普通代码块,如果存在多个则顺序执行;每实例化一次就会加载一次
  4. 接下来是调用该类的构造方法;每实例化一次就会加载一次

继承类之间的加载顺序:

  1. 先加载父类的静态变量、静态块,如果存在多个则顺序执行;只会加载一次
  2. 再加载子类的静态变量、静态块,如果存在多个则顺序执行;只会加载一次
  3. 如果main方法第一行有输出,则输出第一行内容
  4. 接下来是加载父类的普通变量、普通代码块,如果存在多个则顺序执行;每实例化一次就回加载一次
  5. 接下来是调用父类的构造器;每实例化一次就会加载一次
  6. 接下来是加载子类的普通变量、普通代码块,如果存在多个则顺序执行;每实例化一次就会加载一次
  7. 接下来是调用子类的子类的构造器;每实例化一次就会加载一次

总之实例化的时候先加载静态相关信息,再加载普通相关信息,再加载构造器

    静态块,静态变量  >  普通变量,普通代码块  >  构造器

       在写这些代码的时候发现一个比较疑惑的问题,我在Father类中定义了一个普通变量father_C对应了一个getC()的方法,无论这个方法是静态方法还是普通方法,编译器都不会报错并且执行也不受影响。代码如下:

	int Father_C = getC();
	int Father_D = new Father().getD();
	int Father_E = getD();
	
	public static int getC(){
		System.out.println("我是Father获取变量C的方法");
		return 100;
	}
	public int getD(){
		System.out.println("我是Father获取变量C的方法");
		return 100;
	}
这是编译器在编译阶段会优化还是什么原因呢?欢迎大家在留言区进行讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值