java中静态数据(块)、main方法、构造块、构造函数的运行顺序

本文深入探讨Java中构造函数的执行顺序,包括单一类、带继承类的构造函数及构造块的运行机制。通过实例演示了静态数据块、构造块和构造函数的执行流程,揭示了Java继承中构造函数调用的细节。

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

前言

最近在啃《Java编程思想》,其中有很多东西,以前学过,有些都忘了,于是乎,我就想着捡起来,看到第七章《复用类》,文章中提出通过组合、继承的方式来进行类的复用,当然还可以通过代理(虽然java不直接支持代理)。关于“组合”稍微提下,就是在新的类中产生现有类的对象,通俗点就是将一个的类对象作为新类的成员变量。关于"继承",也稍微提下,就是一个类使用extends 继承父类,其中继承的类具有父类所有非私有方法调用权限,和非私有成员变量的访问权限。关于访问权限分四种,private 、protected、包访问权限、public 权限。private 修饰词修饰变量或者是方法,表示只有当前类才有权限进行变量/方法的访问和修改。这个广泛应用构造函数和类成员变量。这个可以防止某些不正当的错误发生。protected修饰词,表示只有该类和该类的子类才有权限进行访问和修改。不加修饰词,默认就是包访问权限,就是同一个包下的所有类都具有对该变量/方法 权限访问。public 就是同一个项目工程所有的类都具有对该变量/方法权限。

相关概念

demo:
其中这下面的 static{ } 这块就是静态数据块。没有static修饰的{ }包裹的就是构造块。构造函数就是以类名字命名的方法.下面的就是Father()、Father(final Person p),两个构造函数.main方法就是下面的public static void main(String [] args)静态方法,这个是整个程序开始运行的入口.

public static class Father{
		
		private Person p;		
		{
			Person pp=new Person("Father ");
		}
		Father(){
		}		
		Father(final Person p){ 		
			this.p=p;
			System.out.println("this is Father Constructor!");
		}
		static {	
		   Person person=new Person("father",44,"male");
		   int i=1;
		}
		public static void main(String[] args) {
			Father father = new Father(new Person());
		}
		}
实验,观察运行顺序.

附上Person类

public  class Person {
	
	private String name;
	private int age;
	private String sex;
	
	public Person(String name, int age, String sex) {
		super();
		this.name = name;
		this.age = age;
		this.sex = sex;
		System.out.println("this is  Person Constructor with All Arguments ");
	}
	public Person() {
		super();
		System.out.println("this is Person Constructor without Arguments!");
	}
	public Person(String name) {
		super();
		System.out.println("this is Person  Constructor  with One Arguments");
	}
	
}

执行Father类的main方法。观看运行顺序.

public static class Father{
		
		private Person p;		
		{
			Person pp=new Person("Father ");  // 第三步执行
		}
		Father(){
		}		
		Father(final Person p){ 		
			this.p=p;
			System.out.println("this is Father Constructor!");  // 第四步执行
		}
		static {	
		   Person person=new Person("father",44,"male");  // 第一步执行
		   int i=1;
		}
		public static void main(String[] args) {
		
			Father father = new Father(new Person());  //第二步执行
		}
		}

结果如下:

this is Person Constructor with All Arguments
this is PersonConstructor without Arguments!
this is Person Constructor with OneArguments
this is Father Constructor!

第一步,找到Father类,并运行其中的static数据块,有且只运行一次,后面在调用构造方法创建对象的时候,不会再次运行.
第二步:按照顺序运行mian方法的方法
第三步: 调用构造函数,先去执行构造块,这个构造块会在每次调用构造函数时,都会运行一次。
第四步; 这才是真正的执行构造函数了。

观察在无论创建多少个对象的时候static块只运行一次,以及构造块是不是每次在创建对象的时候,都会运行一次?
这里使用Father类来创建两个对象来观察结果.

public static class Father{
		
		private Person p;		
		{
			Person pp=new Person("Father ");  
		}
		Father(){
		}		
		Father(final Person p){ 		
			this.p=p;
			System.out.println("this is Father Constructor!");  
		}
		static {	
		   Person person=new Person("father",44,"male"); 
		   int i=1;
		}
		public static void main(String[] args) {
		
			Father father1 = new Father(new Person());  
			Father father2 = new Father(new Person());  
		}
		}

结果如下:

this is Person Constructor with All Arguments // static数据块运行结果
this is Person Constructor without Arguments! //构造方法中参数运行结果
this is Person Constructor with One Arguments // 构造块运行结果
this is Father Constructor! // 构造函数运行结果
this is Person Constructorwithout Arguments! //构造方法参数运行结果
this is Person Constructor with One Arguments // 构造块运行结果
this is Father Constructor! // 构造函数运行结果

可以清晰的看到 static{ } 静态块中的内容只运行了一次。构造块运行了两次。

带有继承的类的构造函数的运行顺序。

以上讲述的都是单一的类的运行顺序,试想,如果一个带有extends关键字的类,它在运行构造函数创建对象的时候,是怎么运行的呢?这里我们以三级关系为例子,就是父类 、子类、 子类的子类。
demo:
先附上用于测试输出结果的Person类

package thinkingInJava;

public  class Person {
	
	private String name;
	private int age;
	private String sex;

	public Person(String name, int age, String sex) {
		super();
		this.name = name;
		this.age = age;
		this.sex = sex;
		System.out.println("this is  Person Constructor with All Arguments "+name+" "+age+" "+sex);
	}
	public Person() {
		super();
		System.out.println("this is Person Constructor without Arguments!");
	}
	public Person(String name) {
		super();
		System.out.println("this is Person Constructor with One Arguments"+" "+name);
	}
	
}


		public static class Father{
		
		{
			Person pp=new Person("Father");
		}
		Father(){
		}		
		Father(final Person p){ 		
			this.p=p;
			System.out.println("this is Father Constructor!");
		}
		static {
		   Person person=new Person("father",44,"male");
		}
	}
	public static class Son extends Father{
	
		private  final String ST="China";		
		
		private static final Person person=new Person("Son",21,"male");
		
		Person p=new Person("Son");
		
		Son(){
			super();
		}
		// 构造方法是隐式的无返回值的静态方法
		Son(Person p){
			//父级的构造优先于该子类的构造方法执行。因此结果就是先执行父类的构造后在继续执行子类的构造方法。
			super(p);
			System.out.println("this is Son Constructor");				
		}
				
	}	
			
//			先去加载该类的所有父类,依次从该类的最高的父类的static静态数据/块依次执行到该类本身。
				
			//调用了构造函数,由于里面调用了父类的构造函数,因此先执行完成父类的构造函数,在执行子类的构造函数.
					
			// 所有的构造函数的运行都是先初始换成员变量或者是构造块。然后执行构造函数本身。


	public static class GrandSon extends Son{
	
		// 编译器在开发人员不写默认构造函数的时候,自动会创建一个无参构造函数.
		// 注意,一旦开发人员书写了有参构造函数,没有书写无参构造函数,那么在调用无参构造函数来创建对象的时候就会出错!
		GrandSon(){
			super();
		}
		Person pp=new Person("GrandSon");
		
		GrandSon(Person p){
			super(p); 
			System.out.println("this is GrandSon Constructor");
		}
		
		static Person p=new Person("GrandSon",3,"female");
				
		public static void main(String[] args) {
					
			 GrandSon grandSon = new GrandSon(new Person());
}
}

运行GrandSon 的main方法用于观察结果.其中我这里是GrandSon 继承于Son类,Son类继承于Father类。
先看运行结果来进行分析。

this is Person Constructor with All Arguments father 44 male // 父类的static 数据块运行结果
this is Person Constructor with All Arguments Son 21 male // 子类的static 数据块运行结果
this is Person Constructor with All Arguments GrandSon 3 female // 子类的子类的static 数据块运行结果
this is Person Constructor without Arguments! // 构造方法参数运行结果
this is Person Constructor with One Arguments Father // 父类构造块
this is Father Constructor! // 父类构造函数
this is Person Constructor with One Arguments Son // 子类构造块
this is Son Constructor // 子类的构造函数
this is Person Constructor with One Arguments GrandSon // 子类的子类构造块
this is GrandSon Constructor //子类的子类的构造函数

分析:调用构造函数,先去加载该类,如果发现该类有extends关键字,即有继承关系,则会继续向上寻找该类的父类,如果该类的父类也有extends关键字,继续递归寻找,直到找到没有extends的祖类。然后依次按照继承顺序,分别执行类的static数据块。以三级结构为例子,先执行祖父类的static数据块,然后执行父类的static数据块,最后才执行自己的数据块.调用构造函数的时候,如果子类构造调用了父类构造函数,则先去执行父类构造函数,如果父类构造调用父类的父类构造,则继续递归直到不在调用父类递归的构造为止,然后按照祖类 父类 子类的顺序一次执行构造函数。当然执行构造函数的时候,都是先去执行该类的构造块。

总结:
第一步===>加载类,执行类的static数据块【如果该类有父类,则优先执行该类的父类的static数据块】,其中static数据块只会运行一次,无论之后创建多少个类对象。
第二步===>执行main方法中的逻辑。
第三步===>执行构造函数,如果该构造调用父类的构造,则优先执行父类的构造函数,其中调用构造函数时,执行结构块,初始化成员变量。
第四部===>这才到了运行构造函数内部逻辑。

结束语:以上都是个人参考《Java编程思想》实验得出。如果问题,欢迎各位在下面留言,一起交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值