static静态代码块,一般用于初始化。或者加载比较大的,耗时的,比方说静态资源,节省运行时调用初始化时间。
Class对象仅在需要的时候才被加载,static初始化是在类加载时进行的。
一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象,Class.forName("")同new Object(),只会在运行需要时被加载,加载在内存,除非被回收,否则不会重复加载。
下面的例子说明,使用new对象的方式和Class.forName()加载类的方式,都只会加载一次,一旦存在,不会重复加载。
package org.mj.typeinfo; /** * @author: jing.ming * @version 创建时间:2015年10月31日 下午8:29:40 * 类说明 */ class Candy{ static{ System.out.println("Loading Candy!"); } } public class LoadClass { public static void main(String[] args) { new Candy(); new Candy(); try { Class.forName("org.mj.typeinfo.Candy"); } catch (ClassNotFoundException e) { System.out.println("Could't find Candy!"); } } }
运行console结果输出:
Loading Candy!
可见重复生成实例,只会加载一次类,就是在第一次加载。
=============萌萌哒分隔线=================================
但是,有趣的一点是,当使用类字面常量方式生成对Class对象的引用时,即使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。
我们都知道,java中为了使用类而做的准备工作实际上包括三个步骤:
1.加载,这是由类加载器执行的。该步骤将查找字节码(通常在classpath所指定的路径中查找,但这并不是必须的。),并从这些字节码中创建一个Class对象。
2.链接。该阶段验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
3.初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行;
package org.mj.typeinfo; import java.util.Random; /** * @author: jing.ming * @version 创建时间:2015年10月31日 下午9:17:44 * 类说明 */ class Initable{ static final int staticFinal = 47 ; static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable") ; } } class Initable2{ static int staticNonFinal = 147; static{ System.out.println("Initializing Initable2") ; } } class Initable3{ static int staticNonFinal = 74; static{ System.out.println("Initializing Initable3") ; } } public class ClassInitialization { public static Random rand = new Random(47) ; public static void main(String[] args) throws ClassNotFoundException { Class initable = Initable.class ; System.out.println("After creating Initable ref"); //Does not trigger initialization System.out.println(Initable.staticFinal); //Does trigger initialization System.out.println(Initable.staticFinal2); //Does trigger initialization System.out.println(Initable2.staticNonFinal); Class initable3 = Class.forName("org.mj.typeinfo.Initable3"); System.out.println("After creating Initable3 ref") ; System.out.println(Initable3.staticNonFinal) ; } }
console输出:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
从上面的输出可以看到:
java初始化有效的实现了惰性,从对initable引用的创建中可以看到,仅使用.class语法来获得对类的引用不会引发初始化。但是,为了产生Class引用,Class.forname()立即就进行了初始化,就像在initable3引用的创建中所看到的。
如果一个static final值是“编译期常量”,就像Initable.staticFinal那样,那么这个值不需要对Initable类进行初始化就可以被读取。但是,如果只是将一个域设置成static和final的,还不足以确保这种行为,例如,对Initable.staticFinal2的访问将强制进行类的初始化,因为它不是一个编译期常量。
如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间),就像在Initable2.staticNonFinal的访问中看到的那样。