文档章节

java static块使用

m
 mfcliu
发布于 2016/07/03 20:03
字数 1438
阅读 4
收藏 0

 

1. 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. 内部类

内部类的初始化和外部类的初始化没有必然联系,仍然在第一次被使用时,调用内部类的静态块。

© 著作权归作者所有

共有 人打赏支持
m
粉丝 0
博文 1
码字总数 1438
作品 0
浦东
私信 提问
java 中静态代码块执行的时机

1、简单认为JAVA静态代码块在类被加载时就会自动执行 是错误的 2、正解:static块真正的执行时机 一个类的运行分为以下步骤: 装载 连接 初始化 装载阶段 通过类型的完全限定名,产生一个代表...

职业搬砖20年
2018/07/06
0
0
java 同步块(Java Synchronized Blocks)

Java 同步块包括方法同步和代码块同步。java 同步可以避免资源竞争,避免死锁。 主题包括的主要内容: 关键字 Synchronized 实例方法 Synchronized static methods Synchronized blocks in i...

丁佳辉
2015/08/18
0
0
简单的入门Android开发和Java语言基础[图]

简单的入门Android开发和Java语言基础[图] 前言: 去年年底到今年年初,我做过简单的智能家居,也实现过一些简单的直连和远程的智能家居。于是就将最简单的通信发布出来:智能家居简单实现—...

原创小博客
2018/08/04
0
0
两道面试题,带你透彻解析Java类加载机制

  在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:   class Grandpa { static { System.out.println("爷爷在静态代码块"); } } class Father extends Gran...

java进阶架构师
2018/12/06
0
0
Java多线程学习(二)synchronized关键字(2)

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

分布式事务解决方案框架(LCN)

什么是XA接口 XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口...

群星纪元
11分钟前
0
0
linux 操作系统 常用命令和软件安装

1.系统时间更新 ntpdate time.windows.com 2.传送文件 rsync -av /home/data/a.dat -e ssh root@192.168.0.100:/home 3.传送文件夹 scp -r /home/data root@192.168.0.100:/home 4.JDK安装 ......

WJtiny
33分钟前
0
0
pg_lightool基于basebackup的单表恢复和块恢复

开源软件pg_lightool,实现了基于wal日志的块恢复。详情参见博客:https://my.oschina.net/lcc1990/blog/1931485。由于wal日志中FPW的不确定性,它不能作为一个数据库恢复的解决方案。目前对...

movead
40分钟前
2
0
对比剖析Swarm Kubernetes Marathon编排引擎

Docker Native Orchestration 基本结构 Docker Engine 1.12 集成了原生的编排引擎,用以替换了之前独立的Docker Swarm项目。Docker原生集群(Swarm)同时包括了(Docker Engine \/ Daemons)...

Linux就该这么学
42分钟前
2
0
Mybatis的结果集处理

此时我们已经可以把整段的SQL语句取出,但还并没有在数据库中去执行,我们可以先来分析一下配置文件中SQL语句执行后的结果集是如何处理的。 Mybatis会将结果集按照映射配置文件中定义的映射规...

算法之名
54分钟前
25
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部