在写Dao工厂的时候遇到了这么一个问题:每次访问数据的时候总是出错,
提示下面的代码 daoconfig.load(int) 出错!
public class DaoFactory {
private static DaoFactory factory = new DaoFactory();
private static Properties daoconfig = new Properties();
private DaoFactory(){
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
try {
daoconfig.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError();
}
}
public static DaoFactory getInstance(){
return factory;
}
public <T> T getDao(Class<T> klass){
try {
String key = klass.getSimpleName();
T dao = (T) Class.forName(daoconfig.getProperty(key)).newInstance();
return dao;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
开始真想不出这一行代码哪里出了问题,于是设置了一个端点调试一下,
发现daoconfig的值是null,这时才恍然大悟,于是将private static DaoFactory factory = new DaoFactory();
这段代码移到private static Properties daoconfig = new Properties();下面,这样问题就解决了!
那为什么这样改变一下代码的位置就能解决这个问题呢?说到底还是类成员的初始化时机问题!
下面总结一下类成员的初始化过程,方便自己以后的复习!
在第一次遇上某类时,JVM就会加载该类的class文件,如果该类有父类,则先加载其父类,并依此类推。
然后检查class文件的正确性,接着创建该类的class对象。随着类的加载,进行类的静态初始化,并且总是先执行父类的静态初始化。
静态初始化的顺序是
(1)为静态变量分配内存空间
(2)将静态变量默认初始化为0,false,null(其实是将分配了的内存的二进制位置为0)
(3)按源码的排列顺序进行初始化
例:
class P{
static P p = new P(2);
static int a = 20;
static int b ;
public P(int i){
this.b = this.a - i;
}
}
class Test1{
public static void main(String[] args) {
int m = P.p.b;
System.out.println(m);
P n = new P(2);
System.out.println(n.b);
}
}
上面程序的输出结果是-2和18
我们来梳理一遍就清楚为什么是-2和18了
首先执行int m = P.p.b; 语句,JVM会加载P这个类,然后为p,a,b分配内存空间,执行默认初始化,此时
p的值为null,a和b的值为0,然后按顺序为他们进行初始化,首先是new P(2); 调用P的构造函数,因为此时a为0
所以b的值是-2,接着为a赋值为20后结束。所以输出m的值为-2。
输出18的原因是类似的。
知道了这些,上面的DaoFactory为什么出错也是一清二楚了!
类的实例变量的初始化时机:
当第一次new 一个对象时,先执行上面的静态初始化,然后
(1)为成员变量分配内存空间
(2)将成员变量默认初始化为0,false,null(其实是将分配了的内存的二进制位置为0)
(3)按源码的排列顺序进行初始化
(4)执行构造函数
例
class Test{
int a = 20;
public Test(){
a = 100;
b = 10;
}
{
a = 10;
b = 1;
}
int b = 5;
public String toString(){
return "a = " + a + "\n" + "b = " + b;
}
public static void main(String[] args) {
System.out.println(new Test().toString());
}
}
这段程序将输出a = 100和 b = 10
过程也就是上面所说的4步
阿里巴巴的一道笔试题
public class Test {
public static int k=0;
public static Test t1 = new Test("t1");
public static Test t2 = new Test("t2");
public static int i = print("i");
public static int n=99;
public int j= print("j");
{
print("构造块");
}
static{
print("静态块");
}
public Test(String str){
System.out.println((++k)+":"+str+" i="+i+" n="+n);
++i;
++n;
}
public static int print(String str){
System.out.println((++k)+":"+str+" i="+i+" n="+n);
++n;
return ++i;
}
/**
* @param args
*/
public static void main(String[] args) {
Test t = new Test("init");
}
}
第一步:加载类,为静态变量分配内存空间,并对内存进行置0,此时k=0 , t1=null, t2=null ,i=0, n=0
第二步:根据静态变量的声明顺序对其进行第一次初始化 首先 k=0
1:对t1进行初始化时,执行 new Test("t1"),执行这条语句时,将会为j分配内存,并初始化 ,执行 print("j"),打印出1:j i=0 n=0,此时k=1 i=1 n=1
然后继续往下执行初始化块中的语句 print("构造块"); 打印出2:构造块 i=1 n=1 ,此时 k=2 i=2 n=2 ,接着,执行构造函数,打印出 3:t1 i=2 n=2,此时,k=3, i=3,n=3,至此 t1 初始化完成
2:对t2进行初始化,与t1的初始化类似执行 new Test("t2"),执行这条语句时,将会为j分配内存(对于实例变量,每个实例都有一份),并初始化 ,执行 print("j"), 打印出4:j i=3 n=3,此时k=4 i=4 n=4,然后继续往下执行初始化块中的语句 print("构造块"); 打印出5:构造块 i=4 n=4 ,此时 k=5 i=5 n=5 ,接着,执行构造函数, 打印出 6:t2 i=5 n=5, 至此 t2 初始化完成
3:初始化 i ,执行print("i") ,打印出7:i i=6 n=6 , 此时 k=7 , i=7,n=7
4:初始化 n , n=99
第三步:执行 静态代码块中的语句 print("静态块"); 打印出8:静态块 i=7 n=99 此时 k=8 , i=8,n=100
第四步:类变量的初始化完成后,执行 new Test("init"),该实例有其自己的实例变量 j ,并为其分配内存,初始化,执行print("j") 打印出9:j i=8 n=100 此时 k=9 i=9 n=100
接着执行初始化块中的语句,print("构造块") 打印出10:构造块 i=9 n=101 此时 k=10 i=10 n=102
最后执行 构造函数 打印出 11:init i=10 n=102 此时 k=11 i=11 n=103
至此 ,程序执行完毕。
最后的结果为:
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102