类是如何被初始化的


在写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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值