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 instance
29      */
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 id
50      */
51     public String getId() {
52         return id;
53     }
54
55     /**
56      * @param id the id to set
57      */
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 instance
22      */
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.Application
38         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 instance
22      */
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.Application
38         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 = [email protected]
san.Super : static{}
san.Super : Constuctor with this = [email protected]
san.ClassWithStatic : Constuctor with this = [email protected]
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 = [email protected], iTest0 = 0
san.Super : static{}
san.Super : Constuctor with this = [email protected], iTest0 = 1
san.ClassWithStatic : Constuctor with this = [email protected]
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.htmlhttp://www.cnblogs.com/o-andy-o/archive/2013/06/06/3120298.html
时间: 2024-11-05 11:24:16

java中类加载顺序的相关文章

ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗

1.ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗? :当然是运行期间啊,我自己有个理解误区,改正后如下:编译期间编译器是不去加载类的,只负责编译而已,去rt.jar拿数据干嘛,不依然是class文件,jvm是只要是class文件就能运行. 2.类加载ClassLoader,各个类加载器执行顺序是什么? :永远是自己写的加载器先去加载,记住并不是真正的加载,而是双亲委派机制,每个加载器都不真正去加载,而是去让父加载器去加载,想一下,自然界亦是如

Java 的类加载顺序

Java 的类加载顺序 一.加载顺序 1.父类的静态成员变量初始化 1.1.静态代码块 1.2.普通代码块 1.3.无参构造器 2.父类的静态代码块 3.子类的静态成员变量初始化 3.1.静态代码块 3.2.普通代码块 3.3.无参构造器 4.子类的静态代码块 5.父类的普通成员变量初始化 5.1.静态代码块 5.2.普通代码块 5.3.无参构造器 6.父类的普通代码块 7.父类的无参构造器 8.子类的普通成员变量 8.1.静态代码块 8.2.普通代码块 8.3.无参构造器 9.子类的普通代码块

Java虚拟机类加载顺序

Java虚拟机在加载类的时候,先初始化父类静态变量,再初始化子类静态变量,然后加载父类,最后加载子类 public class Parent { static{ System.out.println("static parent"); } public Parent(){ System.out.println("parent loaded"); } public void getaaa(){ System.out.println("parent aaa&q

Java中线程顺序执行

现有线程threadone.threadtwo和threadthree,想要的运行顺序为threadone->threadtwo->threadthree,应该如何处理?这里需要用到一个简单的线程方法join(). join()方法的说明:join方法挂起当前调用线程,直到被调用线程完成后在继续执行(join() method suspends the execution of the calling thread until the object called finishes its ex

java 中类加载器

jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式. java 中类加载器分为三个: BootstrapClassLoader 负责加载 ${JAVA_HOME}/jre/lib 部分 jar 包 ExtClassLoader 加载 ${JAVA_HOME}/jre/lib/ext 下面的 jar 包 AppClassLoader 加载用户自定义 -classpath 或者 Jar 包的 Class-Path 定义的第三方包 类的生命周期

java中类加载机制

在java中的每一个类都会对应一个Class对象,我们通常把这个Class对象称之为字节码对象,那么这个字节码对象是由谁来产生的呢?java中的类是由谁来加载进内存的呢?接下来我介绍的就是负责将java中的字节码文件加载到内存,创建Class对象的类ClassLoader,也就是java中的类加载器. 类加载器一般由系统来提供,不需要我们自己实现,但是通过我们自定义的类加载器可以更加灵活的加载class文件.在java中有三个默认的类加载器分别是Bootstrap ClassLoader(启动类

java中类加载时机

java虚拟机规范虽然没有强制性约束在什么时候开始类加载过程,但是对于类的初始化,虚拟机规范则严格规定了有且只有四种情况必须立即对类进行初始化,遇到new.getStatic.putStatic或invokeStatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化.生成这4条指令最常见的java代码场景是: 1)使用new关键字实例化对象 2)读取一个类的静态字段(被final修饰.已在编译期把结果放在常量池的静态字段除外) 3)设置一个类的静态字段(被final修饰.已在编

Java中类加载机制和反射技术

我们知道一个对象在运行时有两种类型,一个是编译类型,一个是运行时类型.在程序运行时,往往是需要发现类和对象的真实的信息的.那么如何获的这种信息呢? 其一,如果我们在编译和运行时都知道类型的具体信息,这时是可以手动将一个对象转换为运行时的类型. 其二,如果我们在编译时无法预知对象和类到底是属于哪些类,那么程序只有依靠运行时的信息来发现对象和类的真实的信息了,这时就必须要用到反射技术. 在谈具体的发射技术之前,我想先回顾下,有关类的加载的一些基本的性质和原理,以方便我们更好地理解,反射的作用和特点.

java中类加载的全过程及内存图分析

类加载机制: jvm把class文件加载到内存,并对数据进行校验.解析和初始化,最终形成jvm可以直接使用的java类型的过程. (1)加载 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口. (2)链接 将java类的二进制代码合并到jvm的运行状态之中的过程 2.1 验证 确保加载的类信息符合jvm规范,没有安全方面的问题. 2.2 准备 正式为类变量(static