来自:
http://www.cnblogs.com/xing901022/p/4574961.html
![](https://box.kancloud.cn/852728f2350aeff9341fe536f6aea11a_1001x758.png)
##类加载发生的时机:
1)
下面四条指令:
`new`,
`getstatic`,
`putstatic`,
`invokestatic`
如果:类没有进行过初始化,则需要出发其初始化。
这4条字节码出现的情况:
『使用new关键字实例化对象的时候』
『读取或设置一个累的静态字段(被final修饰,已在编译器把结果放入常量池的静态字段除外)』
『调用一个类的静态方法』
2)使用java,lang.reflect包的方法对类进行**反射**调用,如果类没有被初始化,则先触发其初始化。
3 )初始化一个类,发现其父类还没有进行初始化,则需要先触发器父类的初始化。
4)当虚拟机启动时候,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
初始化过程:
![](https://box.kancloud.cn/2b8288f43d1a604e3285e34ccec05655_511x243.png)
装载:
虚拟机外部的二进制字节流按照虚拟机所需的格式存储在方法区中,方法区中的数据存储格式由虚拟机实现自行定义。
然后在java堆中实例化一个java.lang.Class类的对象,这个对象将作为程序访问方法区的这些类型数据的外部接口。
验证:
1.文件格式验证。
2.元数据验证。
3.字节码验证。
4.符号引用验证。
准备:
准备阶段,正式为类变量分配内存,并设置类变量的初始值。这些内存将在方法区中进行分配。
###初始值“通常情况”
下是数据类型的零值。假设一个类变量的定义为:
`public static interesting value = 123;`
那么准备阶段以后,初始值为0而不是123.
而把value赋为123的putstatic指令是程序被编译后,存放于类构造器`<clinit>()`方法中.
所以把value赋值为123的动作在初始化阶段才会被执行。
###初始值“特殊情况”
public static final int value =123;
编译时javac将会为value生成ConstantValue属性,在准备阶段,虚拟机就会直接赋值。
---
#<clinit>()
`<clinit>()`方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序一定是先变量赋值,再静态语句块,因此在静态语句块中可以访问到类变量的初始值。
`<clinit>()`方法与类的构造函数不通,它不需要显式调用父类构造器,虚拟机会保证在子类`<clinit>()`方法执行之前,父类的`<clinit>()`方法已经执行完毕。因此在虚拟机中第一个被执行的`<clinit>()`方法的类肯定是java.lang.Object.
由于父类的`<clinit>()`方法先执行完毕,也就意味着
`父类中定义的静态语句块` 要优先于`子类的变量赋值操作`。
`<clinit>()`方法对于类或接口来说并不是必须的,如果一个类中没有静态语法块,也没有对变量的赋值操作,那么编译器可以不为这个类生成`<clinit>()`方法。
接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口和类一样都会生成<clinit>()方法。
接口与类不同点:
执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。只有当父接口中定义的变量被使用时,父接口才会被初始化。另外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁和同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,知道活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,那就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。
```
所以其实所有的类,都会把代码收集整理。
整理为
声明变量
静态块
实例代码块
```
代码示例:
##例子1:
```
public class SuperClass{
static{
System.out.println("SuperClass init");
}
public static int value= 123;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init");
}
}
public class Test {
public static void main(String[] args){
System.out.println(SubClass.value);
}
}
输出:
SuperClass init
123
```
并没有执行subClass的类加载。
##例子2:
```
class SuperClass{
static{
System.out.println("SyperClass init");
}
public static int value= 123;
}
public class Test {
public static void main(String[] args){
SuperClass[] sp =new SuperClass[10];
}
}
没有输出
```
所以并没有加载类。
##例子3:
```
class SuperClass{
static{
System.out.println("SyperClass init");
}
public final static int value= 123;
}
public class Test {
public static void main(String[] args){
System.out.println(SuperClass.value);
}
}
输出:
123
```
因为value被final修饰了,所以对SuperClass.value的引用被转化为对自身常量池的引用了。也就是说,这两个类,在编译成Class文件以后其实已经没有任何联系了。
##实例4:
![](https://box.kancloud.cn/8f94f53a8f3149aab7948261e7513f33_516x207.png)
-
![](https://box.kancloud.cn/246164cd46dc91bb6ad283f4a94761c9_440x203.png)
-
![](https://box.kancloud.cn/3d7149f333bee7dfdd8d47e846b46b42_470x211.png)
两个都去掉static 也是类似的情况。
例子5:
```
class SuperClass{
static {
System.out.println("SuperClass init");
value = 2;
}
public static int value= 123;
}
class SubClass extends SuperClass{
public static int s = value;
}
public class Test {
public static void main(String[] args){
System.out.println(SubClass.s);
}
}
输出:
SuperClass init
123
---------------
class SuperClass{
static {
System.out.println("SuperClass init");
value = 2;
}
public static int value= 123;
}
class SubClass extends SuperClass{
static {
System.out.println(value);
}
public static int s = value;
static {
System.out.println("SubClass init");
System.out.println(value);
}
}
public class Test {
public static void main(String[] args){
System.out.println(SubClass.s);
System.out.println(SuperClass.value);
}
}
输出:
SuperClass init
123
SubClass init
123
123
123
---------------
class SuperClass{
{
System.out.println("SuperClass init");
value = 2;
}
public static int value= 123;
}
class SubClass extends SuperClass{
static {
System.out.println(value);
}
public static int s = value;
static {
System.out.println("SubClass init");
System.out.println(value);
}
}
public class Test2 {
public static void main(String[] args){
System.out.println(SubClass.s);
System.out.println(SuperClass.value);
}
}
输出:
123
SubClass init
123
123
123
```
- 参考资料
- 容器的实现
- ArrayList、LinkedList与Vector的区别
- Map,Set,List,Queue,Stack的特点与用法
- HashMap的实现
- HashMap和ConcurrentHashMap差别
- HashMap和HashTable的区别
- fast fail
- java 实用方法
- Collections中实用的函数
- ArrayList中实用的函数
- Integer和Character
- Properties类的简单使用
- XML实用解析
- 从jar包中读取文件信息
- java自带base64加密解密
- java机制
- 分派
- 反射
- 类加载机制
- java中一个对象的初始化
- 泛型
- 自动装箱,拆箱与遍历循环
- 偏向于语法
- new int[]
- new boolean[]
- Switch能否用string做参数
- equals与==的区别
- 泛型对象数组
- Enum的用法
- String、StringBuffer与StringBuilder的区别
- try catch finally
- finalize方法
- object有哪些公用方法
- Java的四种引用,强弱软虚,用到的场景
- java访问修饰符
- Hashcode的作用
- 九种基本数据类型
- java对象大小
- 数组长度
- 动态代理的一个例子
- java.lang.NoClassDefFoundError
- ThreadLocal