标签:java static块
1. java static块执行时机
java static块在类被初始化的时候被执行。
参考《深入Java虚拟机》中的描述,一个java class的生命周期:
装载
通过类的全限定名,产生一个代表该类型的二进制数据流;
解析这个二进制数据流为方法区内的数据结构;
创建一个表示该类型的java.lang.Class的实例。
如果一个类装载器在预先装载的时遇到缺失或错误的class文件,它需要等到程序首次主动使用该类时才报告错误。
连接
验证,确认类型符合Java语言的语义,检查各个类之间的二进制兼容性(比如final的类不用拥有子类等 ),另外还需要进行符号引用的验证;
准备,Java虚拟机为类变量分配内存,设置默认初始值;
解析(可选的 ) ,在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程。
初始化
当一个类被主动使用时,Java虚拟就会对其初始化,如下六种情况为主动使用:
当创建某个类的新实例时(如通过new或者反射,克隆,反序列化等)
当调用某个类的静态方法时
当使用某个类或接口的静态字段时
当调用Java API中的某些反射方法时,比如类Class中的方法,或者java.lang.reflect中的类的方法时
当初始化某个子类时
当虚拟机启动某个被标明为启动类的类(即包含main方法的那个类)
Java编译器会收集所有的类变量初始化语句和类型的静态初始化器,将这些放到一个特殊的方法中:clinit。
static块可以使用下述实例验证:
public abstract class AbstractTestStatic { static int count; static { System. out .println("AbstractTestStatic static block" ); count = 1; } public static int getCount(){ System. out .println("AbstractTestStatic getCount" ); return count ; } } public class TestStatic extends AbstractTestStatic{ static { System. out .println("TestStatic static block" ); count = 2; } public static int getCount() { System. out .println("TestStatic getCount" ); return count; } } public class Main1 { public static void main(String[] args) { Class[] classArray = new Class[1]; classArray [0] = TestStatic. class; } }
classArray [0] = TestStatic. class; 这个语句会引起类的装载和连接,但不会初始化。运行程序,可以看到,静态块没有被执行。
2. java static块在一个classloader中只会执行一次
同一个classloader中验证较为简单
不同的classloader中的验证方法如下:
public class DynamicClassLoader extends ClassLoader { public DynamicClassLoader(ClassLoader parent ) { super (parent ); } @SuppressWarnings ("unchecked" ) public Class loadClass(String classPath , String className ) throws ClassNotFoundException { try { String url = classPathParser(classPath ) + classNameParser(className ); System. out .println(url ); URL myUrl = new URL( url); URLConnection connection = myUrl .openConnection(); InputStream input = connection .getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = input.read(); while (data != -1) { buffer .write(data ); data = input .read(); } input .close(); byte [] classData = buffer.toByteArray(); return defineClass(noSuffix(className ), classData, 0, classData .length ); } catch (MalformedURLException e ) { e.printStackTrace(); } catch (IOException e ) { e.printStackTrace(); } return null ; } private String pathParser(String path ) { return path .replaceAll( "\\\\", "/" ); } private String classPathParser(String path ) { String classPath = pathParser(path ); if (!classPath .startsWith( "file:")) { classPath = "file:" + classPath; } if (!classPath .endsWith( "/")) { classPath = classPath + "/"; } return classPath ; } private String classNameParser(String className ) { return className .substring(0, className .lastIndexOf("." )).replaceAll( "\\.", "/" ) + className .substring(className .lastIndexOf( ".")); } private String noSuffix(String className ) { return className .substring(0, className.lastIndexOf( "." )); } } public class AClass { static { System. out .println("static in AClass" ); } } public class Main2 { @SuppressWarnings ("rawtypes" ) public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { DynamicClassLoader acl = new DynamicClassLoader(DynamicClassLoader. class.getClassLoader()); Class s1 = acl .loadClass("D:/workspaces/workspace-rabbit/MultiThread/target/classes" ,"com.vip.test.MultiThread.ch1.thread.staticorder.AClass.class" ); s1.newInstance(); DynamicClassLoader bcl = new DynamicClassLoader(DynamicClassLoader. class.getClassLoader()); Class s2 = bcl .loadClass("D:/workspaces/workspace-rabbit/MultiThread/target/classes" ,"com.vip.test.MultiThread.ch1.thread.staticorder.AClass.class" ); s2.newInstance(); } }
3. java static块执行顺序
java static块的执行顺序是按照父类静态块->子类静态块,一个类内部的静态块按照定义顺序执行。
public class Test { public static int i; static { i = 10; } public static void main(String[] args) { } static { i = 20; } }
经过编译后,效果和下面的代码一致:
public class Test { public static int _i; public static void main(String[] args) { } static { i = 10; i = 20; } }
4. java static块执行时多线程是安全的,但同一线程内不能保证安全
多线程安全性可以用下面的例子看出:
public class BClass { static Integer count; static BClass instance; static { System. out .println("Bclass statc block" ); try { TimeUnit. SECONDS .sleep(10); } catch (InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } count = new Integer(0); instance = new BClass(); } static BClass getInstance(){ return instance; } public Integer getCount(){ return count ; } } public class Main3 { public static void main(String[] args) { Thread threads [] = new Thread[2]; for (int i = 0; i < 2; i++) { threads [i ] = new Thread() { @Override public void run() { System. out .printf("Thread : %d started\n" , Thread.currentThread().getId()); BClass bc = BClass. getInstance(); System. out .printf("Thread : %d getcount\n", Thread. currentThread().getId()); System. out .printf("Thread : %d getcount %d \n", Thread. currentThread().getId(), bc.getCount()); } }; threads [i ].start(); try { TimeUnit. SECONDS .sleep(5); } catch (InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 2; i++) { try { threads [i ].join(); } catch (InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
执行后,输出:
Thread : 13 started
Bclass statc block
Thread : 14 started 在这里thread14会阻塞直到13的getInstance执行完成后,才继续执行。
Thread : 13 getcount
Thread : 14 getcount
Thread : 14 getcount 0
Thread : 13 getcount 0
单线程不安全的问题如下面这个例子,在static块中,又使用handler,触发static方法的执行。此时,JVM并不会阻塞同一个线程,从而引发NPE。
public class CfgLoader { ConfigContext newContext = new ConfigContext(); ConfigContext oldContext = new ConfigContext(); public ConfigContext initConfig(IConfigChangedHandler handler) { ConfigContext newContext = new ConfigContext(); handler .handle(oldContext , newContext ); return newContext ; } } public class ConfigContext { int count = 0; public int getCount() { return count ; } public void setCount( int count ) { this .count = count; } } public interface IConfigChangedHandler { void handle(ConfigContext previous , ConfigContext current); } public class TestBean implements IConfigChangedHandler { static ConfigContext context; static { context = new CfgLoader().initConfig( new TestBean()); } @Override public void handle(ConfigContext previous, ConfigContext current ) { context .getCount(); } public static int getCount() { return context .getCount(); } } public class TestStatic { public static void main(String[] args) { TestBean. getCount(); } }
这个demo在运行的时候,TestBean.getCount会注册handler,回调中又使用了静态方法,引起同一个线程的重入问题。
5. 内部类
内部类的初始化和外部类的初始化没有必然联系,仍然在第一次被使用时,调用内部类的静态块。
本文出自 “勤似春芽” 博客,请务必保留此出处http://leo01.blog.51cto.com/694903/1795384
标签:java static块
原文地址:http://leo01.blog.51cto.com/694903/1795384