首页 > 代码库 > 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中类加载顺序