【JAVA中的那些事】init与clinit的区别

【JAVA中的那些事】的区别

背景

那天我在进行代码测试,debug操作,看到如下情况:
【JAVA中的那些事】init与clinit的区别_第1张图片
发现在执行类ParentBean的static代码块的时候,出现了:ParentBean.()
那疑问来了:是什么东东?

是什么东东

带着疑问我进行了疯狂google(技术的路上万事都是问自己,自己不懂问google,哈哈)

在oracle的官网有这么一段文献:

At the level of the Java Virtual Machine, every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name . This name is supplied by a compiler. Because the name is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Instance initialization methods may be invoked only within the Java Virtual Machine by the invokespecial instruction (§invokespecial), and they may be invoked only on uninitialized class instances. An instance initialization method takes on the access permissions (JLS §6.6) of the constructor from which it was derived.

A class or interface has at most one class or interface initialization method and is initialized (§5.5) by invoking that method. The initialization method of a class or interface has the special name , takes no arguments, and is void (§4.3.3).

Other methods named in a class file are of no consequence. They are not class or interface initialization methods. They cannot be invoked by any Java Virtual Machine instruction and are never invoked by the Java Virtual Machine itself.

In a class file whose version number is 51.0 or above, the method must additionally have its ACC_STATIC flag (§4.6) set in order to be the class or interface initialization method.

This requirement is new in Java SE 7. In a class file whose version number is 50.0 or below, a method named that is void and takes no arguments is considered the class or interface initialization method regardless of the setting of its ACC_STATIC flag.

The name is supplied by a compiler. Because the name is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java Virtual Machine; they are never invoked directly from any Java Virtual Machine instruction, but are invoked only indirectly as part of the class initialization process.

A method is signature polymorphic if and only if all of the following conditions hold :

It is declared in the java.lang.invoke.MethodHandle class.

It has a single formal parameter of type Object[].

It has a return type of Object.

It has the ACC_VARARGS and ACC_NATIVE flags set.

In Java SE 7, the only signature polymorphic methods are the invoke and invokeExact methods of the class java.lang.invoke.MethodHandle.

The Java Virtual Machine gives special treatment to signature polymorphic methods in the invokevirtual instruction (§invokevirtual), in order to effect invocation of a method handle. A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation (§5.4.3.5), with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. See the java.lang.invoke package in the Java SE platform API for more information.

从以上文献可以看出来几点:

  • 都是由编译器提供
  • 它们不是一个有效的标识符,所以它不能直接用Java程序使用
  • 对应着实例初始化方法(构造器)
  • 对应着类或接口的初始化方法(静态、无参)

is the (or one of the) constructor(s) for the instance, and non-static field initialization.
are the static initialization blocks for the class, and static field initialization.

用代码形象的表示:

class X {

   static Log log = LogFactory.getLog(); // 

   private int x = 1;   // 

   X(){
      // 
   }

   static {
      // 
   }

}

代码实践

父类ParentBean.java

package com.zfy.core.test;

public class ParentBean {
	
	public static int i = 1;
	static {
		System.out.println("ParentBean:static代码块执行");
	}
	public static int j = 2;
	
	/**
	 * 构造器
	 */
	public ParentBean() {
		System.out.println("ParentBean:构造器执行");
	}
}

子类ChildBean.java

package com.zfy.core.test;

public class ChildBean extends ParentBean{
	
	static {
		System.out.println("ChildBean:static代码块执行");
	}
	
	/**
	 * 构造器
	 */
	public ChildBean() {
		System.out.println("ChildBean:构造器执行");
	}
}

其中Bean的初始化配置:

	@Bean
	@Lazy(true)
	public ParentBean parent() {
		return new ParentBean();
	}
	
	@Bean
	public ChildBean child() {
		return new ChildBean();
	}

DEBUG一下:
【JAVA中的那些事】init与clinit的区别_第2张图片
F6下一步:
【JAVA中的那些事】init与clinit的区别_第3张图片
F6下一步:
【JAVA中的那些事】init与clinit的区别_第4张图片
通过上面三步的debug,你会发现static变量赋值和静态代码块都会被编译器收集在里执行,而且是按照类文件中的上下顺序依次执行!

如果再F6下一步,则会依次进入到ChildBean的static块、ParentBean的构造器、ChildBean的构造器

控制台输出:

ParentBean:static代码块执行
ChildBean:static代码块执行
ParentBean:构造器执行
ChildBean:构造器执行

从debug的结果和控制台的输出可以得出

  • Jvm是先执行的父类的(父类static代码块)
  • 再执行的子类的(子类static代码块)
  • 再执行的父类的(父类的构造器)
  • 再执行的子类的(子类的构造器)

参考资料:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html
https://stackoverflow.com/questions/8517121/java-what-is-the-difference-between-init-and-clinit/8517163

你可能感兴趣的:(JVM)