首页 > 代码库 > java中类加载顺序

java中类加载顺序

 

引子

记得上次中秋一哥们写个需求,没写完。他中秋过后还请一天假,有点错,打电话叫我帮他继续搞。

由于测试支撑,叫到我加班了。第二天过来看,打开页面直接报错,再次点击报错就不一样了。前次报错是有代码行的,第二次直接页面说类发现什么的错。

看了下代码,类似如下:

 1 package san; 2  3 import java.io.FileNotFoundException; 4 import java.util.logging.Level; 5 import java.util.logging.Logger; 6  7 import javax.xml.bind.JAXBContext; 8 import javax.xml.bind.annotation.XmlElement; 9 import javax.xml.bind.annotation.XmlRootElement;10 11 //每个类有一个Log4j的静态日志成员12 //这里是单例13 public class ClassWithStatic {14     private static ClassWithStatic instance = new ClassWithStatic();15     private static Logger logger = Logger.getLogger(ClassWithStatic.class.getName());16 17     private ClassWithStatic() {18         JAXBContext jc;19         try {20             //do something that maybe throw IOExption;21             throw new FileNotFoundException();22         } catch (Exception e) {23             logger.log(Level.ALL, "xxx", e);24         }25     }26 27     /**28      * @return the instance29      */30     public static ClassWithStatic getInstance() {31         return instance;32     }33     34     public void doSomeThing() {35         System.out.println("doSomeThing");36     }37     38     public static void main(String[] args) {39         ClassWithStatic.getInstance().doSomeThing();40     }41 }42 43 @XmlRootElement(name = "Scenes")44 class Scenes{45     @XmlElement(name = "id", required = true)46      protected String id;47 48     /**49      * @return the id50      */51     public String getId() {52         return id;53     }54 55     /**56      * @param id the id to set57      */58     public void setId(String id) {59         this.id = id;60     }61     62 }

 

报错

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
at san.ClassWithStatic.<init>(ClassWithStatic.java:21)
at san.ClassWithStatic.<clinit>(ClassWithStatic.java:12)

这是和静态成员初始化顺序有关。

基础知识如下:

Java代码中一个类初始化顺序:static变量 --  其他成员变量  --  构造函数 三者的调用先后顺序:

初始化父类Static --> 子类的Static (如果是类实例化,接下来还会: 初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法)。

系统默认值的给予比通过等号的赋予先执行。

一个类中的static变量或成员变量的初始化顺序,是按照声明的顺序初始化的。

 测试类

 1 public class ClassWithStatic extends Super{ 2     private static int iTest0 = 0; 3     private static ClassWithStatic instance = new ClassWithStatic(); 4     private static int iTest1; 5     private static int iTest2 = 0; 6     static { 7         System.out.println(ClassWithStatic.class.getName() + " : static{}"); 8         iTest0++; 9         iTest1++;10         iTest2++;11     }12 13     public ClassWithStatic() {14         System.out.println(this.getClass().getName() + " : Constuctor.");15         iTest0++;16         iTest1++;17         iTest2++;18     }19 20     /**21      * @return the instance22      */23     public static ClassWithStatic getInstance() {24         return instance;25     }26 27     public void doSomeThing() {28         System.out.println("iTest0 = " + iTest0);29         System.out.println("iTest1 = " + iTest1);30         System.out.println("iTest2 = " + iTest2);31     }32     33     public static void main(String[] args) {34         //private static void main(String[] args)35         //Error: Main method not found in class san.ClassWithStatic, please define the main method as:36         //   public static void main(String[] args)37         //   or a JavaFX application class must extend javafx.application.Application38         System.out.println("public static void main(String[] args)");39         ClassWithStatic.getInstance().doSomeThing();40 41     }42 }43 44 class Super {45     private static Super instance = new Super();46     private static int iTest1;47     private int iTest2 = 0;48     static {49         System.out.println(Super.class.getName() + " : static{}");50         iTest1++;51     }52 53     public Super() {54         System.out.println(this.getClass().getName() + " : Constuctor.");55         iTest2++;56     }57 }

 

结果:

san.Super : Constuctor.
san.Super : static{}
san.ClassWithStatic : Constuctor.
san.ClassWithStatic : Constuctor.
san.ClassWithStatic : static{}
public static void main(String[] args)
iTest0 = 2
iTest1 = 2
iTest2 = 1

这里两遍子类构造,为了区分,改一下构造函数里的打印语句代码。

 1 public class ClassWithStatic extends Super{ 2     private static int iTest0 = 0; 3     private static ClassWithStatic instance = new ClassWithStatic(); 4     private static int iTest1; 5     private static int iTest2 = 0; 6     static { 7         System.out.println(ClassWithStatic.class.getName() + " : static{}"); 8         iTest0++; 9         iTest1++;10         iTest2++;11     }12 13     public ClassWithStatic() {14         System.out.println(ClassWithStatic.class.getName() + " : Constuctor with this = " + this);15         iTest0++;16         iTest1++;17         iTest2++;18     }19 20     /**21      * @return the instance22      */23     public static ClassWithStatic getInstance() {24         return instance;25     }26 27     public void doSomeThing() {28         System.out.println("iTest0 = " + iTest0);29         System.out.println("iTest1 = " + iTest1);30         System.out.println("iTest2 = " + iTest2);31     }32     33     public static void main(String[] args) {34         //private static void main(String[] args)35         //Error: Main method not found in class san.ClassWithStatic, please define the main method as:36         //   public static void main(String[] args)37         //   or a JavaFX application class must extend javafx.application.Application38         System.out.println("public static void main(String[] args)");39         ClassWithStatic.getInstance().doSomeThing();40 41     }42 }43 44 class Super {45     private static Super instance = new Super();46     private static int iTest1;47     private int iTest2 = 0;48     static {49         System.out.println(Super.class.getName() + " : static{}");50         iTest1++;51     }52 53     public Super() {54         System.out.println(Super.class.getName() + " : Constuctor with this = " + this);55         iTest2++;56     }57 }

结果:

san.Super : Constuctor with this = san.Super@15db9742
san.Super : static{}
san.Super : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : static{}
public static void main(String[] args)
iTest0 = 2
iTest1 = 2
iTest2 = 1

 

public class ClassWithStatic extends Super {    protected static int iTest0 = Super.iTest0 + 1;    private static ClassWithStatic instance = new ClassWithStatic();    protected static int iTest1;    private static int iTest2 = 0;    static {        System.out.println(ClassWithStatic.class.getName() + " : static{}");        iTest1++;        iTest2++;    }    public ClassWithStatic() {        System.out.println(ClassWithStatic.class.getName() + " : Constuctor with this = " + this);        iTest1++;        iTest2++;    }    /**     * @return the instance     */    public static ClassWithStatic getInstance() {        return instance;    }    public void doSomeThing() {        System.out.println("ClassWithStatic.iTest0 = " + iTest0);        System.out.println("ClassWithStatic.iTest1 = " + iTest1);        System.out.println("ClassWithStatic.iTest2 = " + iTest2);    }    public static void main(String[] args) {        //private static void main(String[] args)        //Error: Main method not found in class san.ClassWithStatic, please define the main method as:        //   public static void main(String[] args)        //   or a JavaFX application class must extend javafx.application.Application        System.out.println("public static void main(String[] args)");        ClassWithStatic.getInstance().doSomeThing();        System.out.println("Super.iTest0 = " + Super.iTest0);        System.out.println(Const.constanceString);//对类的静态变量进行读取、赋值操作的。static,final且值确定是常量,是编译时确定的,调用的时候直接用,不会加载对应的类        System.out.println("------------------------");        Const.doStaticSomeThing();    }}class Super {    protected static int iTest0;    private static Super instance = new Super();    protected static int iTest1 = 0;    static {        System.out.println(Super.class.getName() + " : static{}");        iTest0 = ClassWithStatic.iTest0 + 1;//1    }    public Super() {        System.out.println(Super.class.getName() + " : Constuctor with this = " + this + ", iTest0 = " + iTest0);        iTest1++;    }}class Const {    public static final String constanceString = "Const.constanceString";    static {        System.out.println(Const.class.getName() + " : static{}");    }    public static void doStaticSomeThing() {        System.out.println(Const.class.getName() + " : doStaticSomeThing();");    }}

 

san.Super : Constuctor with this = san.Super@15db9742, iTest0 = 0
san.Super : static{}
san.Super : Constuctor with this = san.ClassWithStatic@6d06d69c, iTest0 = 1
san.ClassWithStatic : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : static{}
public static void main(String[] args)
ClassWithStatic.iTest0 = 2
ClassWithStatic.iTest1 = 2
ClassWithStatic.iTest2 = 1
Super.iTest0 = 1
Const.constanceString
------------------------
san.Const : static{}
san.Const : doStaticSomeThing();

1、类的加载过程

类加载的时机就很简单了:在用到的时候就加载(和系统内存管理差不多,一个进程都是写时复制CopyOnWrite)。下来看一下类加载的过程:

加载->验证->准备->解析->初始化->使用->卸载

所有的Java虚拟机实现必须在每个类或接口被Java程序 “首次主动使用”时才初始化他们。

2、类的使用方式

Java 程序对类的使用方式可分为两种 :

•主动使用(六种)               –  创建类的实例                                                 -------Test a = new Test();                 –  访问某个类或接口的非编译期静态变量,或者对该非编译期静态变量赋值        -------读写某个类的静态变量 int b = a.staticVariable;或a.staticVariable=b;               –  调用类的静态方法                                              -------调用某个类的静态方法 Test.doSomething();               –  反射(如  Class.forName  (“  com.shengsiyuan.Test  ”)  )    -------比如Class.forName("xxxx");                 –  初始化一个类的子类(不是对父类的主动使用就初始化子类,这样的话生成一个Object类,那岂不是每个类都要初始化)  -------Child.class、Parent.class,初始化Child时,就是对Parent的主动使用,先初始化父类                –  Java虚拟机启动时被标明为启动类的类(  Java  Test  )              -------就是main方法那个所在类

•被动使用

除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化 
主要说下开始:当jvm启动时,用户需要指定一个要执行的主类(包含static void main(String[] args)的那个类),则jvm会先去初始化这个类。

3、类的加载来源

•  类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个  java.lang.Class  对象,用来封装类在方法区内的数据结构    
•  加载 .class 文件的方式   
   –  从本地系统中直接加载   
   –  通过网络下载 .class 文件(URLClassLoader)   
   –  从 zip、jar 等归档文件中加载 .class 文件   
   –  从专有数据库中提取 .class 文件   
   –  将 Java源文件动态编译为 .class 文件 
 

参考:
http://www.cnblogs.com/tianchi/archive/2012/11/11/2761631.html
http://www.cnblogs.com/o-andy-o/archive/2013/06/06/3120298.html

 

java中类加载顺序