类中静态块初始化顺序

类中变量的初始化顺序。变量这个词不一定准确,但本文的思路主要解释一个java类中初始化的一个顺序问题。例子:
public  class A 
{
public A(){
System.out.println("constructor");
}
public static void main(String[] a)
{
A tt=new A();
A.d();//
tt.d();//类方法既可以用类直接访问,也可以用对象访问,但是两者最终的实现却都是用类访问。
s();
}
static {
System.out.println("static block");
}
public static void d()
{
System.out.println("d");
}
public static void s()
{
System.out.println("s");
}
}

输出结果:
static block
constructor
d
d
s
这个结果通过程序运行很容易得出来,但是原因这个好像没有太多人去注意,这里我也是大概弄了个半懂。对于所有的类变量初始化语句和类型的静态初始化器被Java编译器收集在一起,放到一个特殊的方法中,对于类来说,这个方法称作类初始化方法;对于接口来说,它被称为接口初始化方法。在类和接口的Java class 文件中,这个方法被称为"".通常的Java程序方法是无法调用这个方法的。这中方法只能被Java虚拟机调用。专门用把类型静态变量设置为它们的正确初始值。

初始化一个类包含两个步骤:
1)如果类存在直接超类的话,且直接超类还没有被初始化,就先初始化直接超类。这个与类的加载机制,双亲委托机制是相通的,类加载器的默认顺序是引导类加载器->标准扩展类加载器->系统类加载器->用户自定义加载器。在加载过程中必须保证类全名与类加载器的完全一致,才能够产生相同的class文件,否则将会出错。对于类加载机制为什么采用双亲委托机制,Java1.2开始使用双亲委托机制。为什么了?思考中!
2)如果一个类存在一个类初始化方法 方法,就执行此方法。
对于< clinit >方法并非所有的类都在他们的class文件中有一个这个方法。如果类没有任何声明类变量,也没有静态初始化语句,那么它就不会有< clinit ()>方法,这就是我们通常程序运行的顺序。但如果类变量是final变量,并且这种final类变量初始化语句采用编译时常量表态式,类也不会有< clinit >()方法。所以通常的说,< clinit ()>方法应该在类运行是最先加载。顺便讲一句类变量理论上放在jvm的方法区,但是类变量如果又是final型,并且使用一个编译型常量表达式,Java编译器把这样的字段解析成对常量的本地拷贝,该常量那么就存在引用者类的常量池中或者字节码流中,或者二者都有.举个列:
static final int a=4;//这就是上面所讲的类变量,final,并且编译型常量;

对于成员变量,不是static型的,理论上放在堆上,在new出来对象后,每个对象都拥有一份成员变量(非静态)。对于静态变量一个类拥有一个。扯远点,谈谈Java对象的分配。java虚拟机规范并没有具体给出java对象在堆中是如何表示。所以对于具体分配我们无法知道,不过给出了两种可能的设计情况。 一个可能的堆的设计是将堆分为两个部分:引用池和对象池。一个对象的引用就是指向引用池的本地指针。每一个引用池中的条目都包含两个部分:指向对象池中对 象数据的指针和方法区中对象类数据的指针。这种设计能够方便Java虚拟机堆碎片的整理。当虚拟机在对象池中移动一个对象的时候,只需要修改对应引用池中 的指针地址。但是每次访问对象的数据都需要处理两次指针。 如下图:
[img]http://dl.iteye.com/upload/attachment/337793/c850741c-ec9b-3e07-a190-563890c5a50c.jpg[/img]


另一种堆的设计是:一个对象的引用就是一个指向一堆数据和指向相应对象的偏移指针。这种设计方便了对象的访问,可是对象的移动要变的异常复杂。如下图:

[img]http://dl.iteye.com/upload/attachment/337795/44ea23c6-545e-340f-b0e9-9559e313a541.jpg[/img]


Java虚拟机里面的东西实在是比较复杂。涉及到太多东西,并且很多东西都是连在一起的,一通全通。对于static块的主动使用和被动使用,以后有时间再具体讨论。

一旦一个类被初始化,它就随时都可能使用了,可以创建它的实例了。类的实例化有四种途径。明确的使用new操作符,调用class或者java.lang.reflect.constructor对象的newInstance()方法;调用现有对象的clone()方法。或者通过java.io.ObjectInputStream类的getObject()方法反序列化。这里谈谈Java的构造方法。Java编译器为它编译的每一个类都至少生产一个实例初始化方法,Java编译器都产生一个()方法,这个方法针对每一个类的构造方法,对于每一个类的构造方法,Java编译器都产生一个()方法,如果类没有明确声明任何构造方法,编译器默认产生一个无参数的构造方法,它仅仅调用超类的无参数构造方法。和其他的构造器方法一样,编译器在class文件中创建一个()方法。对应它的默认构造方法。一个方法可能包含三种代码:调用另一个()方法。实现对任何实例变量的初始化,构造方法体的代码。如果构造方法通过明确地调用同一个类中的另一个构造方法开始(一个this()调用)开始,

补充一下,谈了静态初始化块,也要谈谈非静态初始化块。在上面的程序增加一个方法,顺序随意。这里只有一个的时候是随意,但是有多个初始化块的时候,初始化块之间是顺序的。如下:
{
System.out.println("non static block");
}
程序输出结果如下:
static block
non static block
constructor
d
d
s
很明显静态初始化块最先,接着非静态初始化块,接着构造器,但是我们通过反编译会发现一个结果。构造器字节码如下:
public A();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3; //String non static block
9: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #5; //String constructor
17: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
20: return

考虑到篇幅的原因,我没有全部把字节码拷过来,但是我们明显的看的出,在编译期。非静态初始化块的代码放到了构造器代码中,放到了方法中。这里要谈谈方法,前面谈到了方法,说到方法有3中可能,结合具体情况来讲讲。如果构造方法通过明确地调用同一个类中的另一个构造方法(一个this()调用)开始。它对应的方法由两部分组成:
1) 一个同类的方法调用。
2) 实现了对应的构造方法的方法体字节码。
举例说就是,有两个构造方法,一个用this(),调用另一个。
如果构造方法不是通过一个this()调用开始的,而且这个对象不是object,方法则由三部分组成:
1) 一个超类的方法的调用;
2) 任意实例变量初始化方法的字节码;
3) 实现了对应的构造方法的方法体的字节码
很明显我们的非静态初始化块就是放到方法中的。并且顺序在当前类的构造方法之前。

综合来说静态语句块,是在初始化时完成的,构造器的调用是在实例化时调用的,所以静态语句在构造器实例化之前访问。虚拟机通过调用某个指定类的方法main启动,在可以调用main之前,必须初始化类。Main方法是虚拟机默认调用的指定静态方法。
对于静态方法为何不能访问实例方法,原因:类方法”(“静态方法”)与“实例方法”在概念中的JVM上的区别:在调用类方法时,所有参数按顺序存放于被调用方法的局部变量区中的连续区域,从局部变量0开始;在调用实例方法时,局部变量0用于存放传入的该方法所属的对象实例(Java语言中的“this”),所有参数从局部变量1开始存放在局部变量区的连续区域中。 从效果上看,这就等于在调用实例方法时总是把“this”作为第一个参数传入被调用方法。静态方法是不传this参数(当前对象的引用)的,所以实例方法不能在静态方法中访问。Java中静态的理解有有两种,一是对方法而言,分为静态和实例方法。二是对应与Java的动态绑定机制而言,有静态绑定。这是c语言等应用的的静态绑定。我理解Java里面的继承,多态。类加载机制。内存分配回收,等等都是连在一起的。谈到多态,我们如果不理解继承,那也没什么意义。谈到继承,如果对类加载机制不理解,也不能很好的理解继承。等等,而这些知识都同Java的内存分配回收,有关。知识点不是孤立的。现在理解还比较散。

你可能感兴趣的:(jvm虚拟机,Java,虚拟机,JVM,D语言)