1. java程序的类初始化以及方法的执行顺序
首先应该明白java的类加载机制有关变量赋值的过程:
在连接阶段的准备阶段为静态变量分配内存并设置初值;对于被final修饰的静态变量,则会直接赋常量值。
而对于一个没有父类的类,其加载过程应该为下面的顺序:
- 首先加载静态变量
- 然后加载静态代码块
- 然后加载实例变量
- 然后加载普通代码块
- 最后则加载构造函数
public class test{
char num ='j';
static final test2 staticFiled = new test2();
test3 notStatic = new test3();
public static void main(String[] args)
{
new test();
test.staticMethod();
}
static
{
System.out.println("静态代码块");
}
{
System.out.println("匿名代码块1");
}
{
System.out.println("匿名代码块2");
}
test(){
System.out.println(num);
System.out.println("构造函数");
}
static void staticMethod()
{
System.out.println("静态方法");
}
}
public class test3{
test3(){
System.out.println("非静态成员变量");
}
}
public class test2{
public test2(){
System.out.println("静态成员变量");
}
}
/**output:静态成员变量
静态代码块
非静态成员变量
匿名代码块1
匿名代码块2
j
构造函数
静态方法
* /
1.1. 对于有父类的子类进行类加载的顺序
初始化的过程其实就是一个执行类构造器< clint>方法的过程,类构造器执行的特点和注意事项:
1).类构造器< clint>方法是由编译器自动收集类中所有类变量(静态非final变量)赋值动作和静态初始化块(static{……})中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定。静态初始化块中只能访问到定义在它之前的类变量,定义在它之后的类变量,在前面的静态初始化中可以赋值,但是不能访问。
2).类构造器< clint>方法与实例构造器< init>方法不同,它不需要显式地调用父类构造器方法,虚拟机会保证在调用子类构造器方法之前,父类的构造器< clinit>方法已经执行完毕。
3).由于父类构造器< clint>方法先与子类构造器执行,因此父类中定义的静态初始化块要先于子类的类变量赋值操作。
4). 类构造器< clint>方法对于类和接口并不是必须的,如果一个类中没有静态初始化块,也没有类变量赋值操作,则编译器可以不为该类生成类构造器< clint>方法。
在深入理解java虚拟机中有上面的一段话,所以对于有父类的子类的加载顺序,应该为先加载父类,然后再加载子类。
public class test extends testFather{
test3 notStatic = new test3();
public static void main(String[] args)
{
new test();
test.staticMethod();
}
static
{
System.out.println("静态代码块");
}
{
System.out.println("匿名代码块2");
}
test(){
System.out.println("构造函数");
}
static void staticMethod()
{
System.out.println("静态方法");
}
}
public class testFather {
test2 testaa = new test2();
testFather(){
System.out.println("我是test父类");
}
}
public class test3{
test3(){
System.out.println("我是静态成员变量");
}
}
public class test2{
public test2(){
System.out.println("我是test父类的成员变量的构造函数");
}
static {
System.out.println("我是test父类的成员变量静态方法块");
}
{
System.out.println("我是test父类的成员变量的代码块");
}
}
/**
静态代码块
我是test父类的成员变量静态方法块
我是test父类的成员变量的代码块
我是test父类的成员变量的构造函数
我是test父类
我是静态成员变量
匿名代码块2
构造函数
静态方法
*/
由此可见,在导出类构造器构造前总会默认调用基类构造器(从Object类开始调用),并且父类的静态代码块以及静态变量也会先于构造器进行初始化。