< clinit >()方法
之前讨论过类加载过程的初始化。(加载–>链接–>初始化),实际上该初始化过程是指JVM的类初始化过程。由JVM生成初始化方法 < clinnt >(),执行该方法执行初始化过程。
关于< clinit >()方法:
- 1.由静态类变量显示赋值代码和静态代码块。
- 2.执行顺序从上至下。
其执行时机如之前所说(类加载发生的时机):
- 1.使用new实例化对象(数组对象不会)或者调用一个类的静态字段(static final类型的字段不会触发类加载)
- 2.使用java.lang.reflect中的方法对类进行反射调用
- 3.初始化一个子类,发现其父类未初始化,加载其父类。
- 4.main方法所在的类(public修饰的类)会在虚拟机启动时被加载。
- 5.但是用动态语言支持时,如果一个java.lang.invoke.MethodHandle实例后解析结果REF_putStatic,REF_getStatic, REF_invokeStatic的方法句柄时,当改方法句柄对应的类没有初始化时,需要初始化该类。(动态语言支持详情请查看)
< init >()方法
创建实例时会进行初始化(构造器的主要作用是进行初始化,指的便就是该过程)
关于< init >()方法:
- 1.有多少个构造器就会有多少个< init >()方法
- 2.< init >()具体执行内容包括非静态变量赋值操作,非静态代码块,对应的构造器代码。
- 3.非静态代码赋值操作和非静态代码块执行顺序从上至下,构造器最后执行。
- 4.每次创建对象,调用的都是对应的< init >()方法
- 5.< init > 方法的首行是super(),即父类对应的< init >方法。
关于< clinit >和< init >方法的总结:
- 1.两种方法的执行时机不同。< clinit > 方法在进行类加载时执行,< init >在进行实例初始化时执行。
- 2.执行目的不同。< clinit > 自行静态变量的赋值和静态代码块,< init >执行非静态变量的赋值和非静态代码块以及构造器内部代码。
以下通过一个实例进行分析:
public class Father {
private int i = test();
private static int j = method();
static {
System.out.print("(1)");
}
public Father() {
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test() {
System.out.print("(4)");
return 1;
}
public static int method() {
System.out.println("(5)");
return 1;
}
}
public class Son {
private int i = test();
private static int j = method();
static {
System.out.print("(6)");
}
public Son() {
System.out.print("(7)");
}
{
System.out.print("(8)");
}
public int test() {
System.out.print("(9)");
return 1;
}
public static int method() {
System.out.println("(10)");
return 1;
}
public static void main(String[] args) {
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
}
还有一个需要注意的点: 关于override和多态性
哪些方法不能被重写:
- 1 final修饰的方法(重写直接报错)。
- 2.static修饰的方法(重写无效)。
- 3.private修饰的方法。
关于对象的多态性:
- 1.子类如果重写了父类方法,通过子类调用的一定时重写后的代码
- 2.非静态方法默认调用的this
- 3.this对象在构造器或者说< init >中就是正在创建的对象。