引子
记得上次中秋一哥们写个需求,没写完。他中秋过后还请一天假,有点错,打电话叫我帮他继续搞。
由于测试支撑,叫到我加班了。第二天过来看,打开页面直接报错,再次点击报错就不一样了。前次报错是有代码行的,第二次直接页面说类发现什么的错。
看了下代码,类似如下:
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