看了张老师写的《一个让98%的Java程序员犯难的偏门问题!》让我产生了很多个联想
所以我也来总结一下我对初始化的理解
(1)非静态的初始化
现在我把题目改了一下大家看一下:
各位先想打印的结果是多少呢?为什么呢?
public
class
Parent

...
{
public void test()

...{
}
public Parent()

...{
test();
}
public static void main(String[] args)

...{
new Child();
}
}
class
Child extends Parent

...
{
public Child()

...{
test();
}
private int instanceValue = 20;
public void test()

...{
System.out.println("the Chind instance value is: " + instanceValue);
}
}
答案是:the Chind instance value is: 0
the Chind instance value is: 20
这个相信大家很多人知道
我也说说我的理解:
new Child();
1. 开始分配分配成员变量的存储空间并进行默认的初始化.就是执行new 关键字。
2. 显示显式或隐式追溯调用父类的构造方法(一直到Object类为止);
3. 进行成员变量的显式初始化操作,也就是执行在定义成员变量时就对其进行赋值的语句.
4 调用child() 这个构造函数
所以可以认为初始化有三次。
这里还有个例子
class
TestInit
...
{
public static void main(String[] args)

...{
new TestInit();
}
public TestInit()

...{
System.out.println("the value of aFloat :"+aFloat);
System.out.println("the value of aInt :"+aInt);
}
double aFloat=MakeFloat();
public int MakeFloat()

...{
return aInt*3;
}
int aInt=3;
}
结果是:
the value of aFloat :0.0
the value of aInt :3
也不难理解初始化根顺序有关 在aFloat进行第二次初始化时 aInt只是第一次结束故 aInt=0
我在把题目改一下
class
TestInit
...
{
public static void main(String[] args)

...{
new TestInit();
}
public TestInit()

...{
System.out.println("the value of aFloat :"+aFloat);
System.out.println("the value of aInt :"+aInt);
}
double aFloat=MakeFloat(aInt);
public int MakeFloat(int var)

...{
return var*3;
}
int aInt=3;
}
出现了编译错误 :在字段被定义之前不能被引用-- 这里我就不知道是什么机制引起
希望那位大侠 给我解释一下
(2)静态块的加载
下面给个例子 ---yyfhz的代码 我cope
class
Writer_Object
...
{

public Writer_Object(String str)...{
System.out.println(str);
}
}

class
Parent
...
{

public void getData() ...{
System.out.println(" --------------------------");
System.out.println(" 进入父对象getData方法");
System.out.print(" 父对象-普通int(0表示没有赋值):");
System.out.println(parent_normal_int);
System.out.print(" 父对象-static int(0表示没有赋值):");
System.out.println(parent_static_int);
System.out.println(" 离开父对象getData方法");
System.out.println(" --------------------------");
}


public Parent() ...{
System.out.println("----------------------");
System.out.println("进入父对象构造方法");
System.out.println("调用getData方法");
getData();
System.out.println("离开父对象构造方法");
System.out.println("----------------------");
}
private Writer_Object parent_normal_obj1 = new Writer_Object("父对象-普通声明对象1创建");
private static Writer_Object parent_static_obj1 = new Writer_Object("父对象-Static声明对象1创建");
private Writer_Object parent_normal_obj2 = new Writer_Object("父对象-普通声明对象2创建");
private static Writer_Object parent_static_obj2 = new Writer_Object("父对象-Static声明对象2创建");
private int parent_normal_int = 10;
private static int parent_static_int = 11;
public static void main(String[] a)

...{
new Child();
}
}
class
Child extends Parent

...
{

public void getData() ...{
System.out.println(" --------------------------");
System.out.println(" 进入子对象getData方法");
System.out.print(" 子对象-普通int(0表示没有赋值):");
System.out.println(child_normal_int);
System.out.print(" 子对象-static int(0表示没有赋值):");
System.out.println(child_static_int);
System.out.println(" 离开子对象getData方法");
System.out.println(" --------------------------");
}


public Child() ...{
System.out.println("----------------------");
System.out.println("进入子对象构造方法");
System.out.println("调用getData方法");
getData();
System.out.println("离开子对象构造方法");
System.out.println("----------------------");
}
private Writer_Object child_normal_obj1 = new Writer_Object("子对象-普通声明对象1创建");
private static Writer_Object child_static_obj1 = new Writer_Object("子对象-Static声明对象1创建");
private Writer_Object child_normal_obj2 = new Writer_Object("子对象-普通声明对象2创建");
private static Writer_Object child_static_obj2 = new Writer_Object("子对象-Static声明对象2创建");
private int child_normal_int = 20;
private static int child_static_int = 21;
}

结果是:
父对象-Static声明对象1创建
父对象-Static声明对象2创建
子对象-Static声明对象1创建
子对象-Static声明对象2创建
父对象-普通声明对象1创建
父对象-普通声明对象2创建
----------------------
进入父对象构造方法
调用getData方法
--------------------------
进入子对象getData方法
子对象-普通int(0表示没有赋值):0
子对象-static int(0表示没有赋值):21
离开子对象getData方法
--------------------------
离开父对象构造方法
----------------------
子对象-普通声明对象1创建
子对象-普通声明对象2创建
----------------------
进入子对象构造方法
调用getData方法
--------------------------
进入子对象getData方法
子对象-普通int(0表示没有赋值):20
子对象-static int(0表示没有赋值):21
离开子对象getData方法
--------------------------
离开子对象构造方法
----------------------
看的出来,Java是遵循以下规则构造对象的
step 1.对有初始值的Static变量赋值 ,其次序为从祖先类到当前类依次赋值
step 2.从祖先类到当前类中的每个类型,依次执行 step 3
step 3. 对类形中所有有初始值的非static的变量,设置初始值;
执行该类型的构造方法
上面是讲静态在父类子类之间的调用的先后关系
下面讲一下静态在线程的问题
class
Init
...
{
static boolean done=false;


static ...{

new Thread() ...{

public void run() ...{
System.out.println("enter thread");
done = false;
}
}.start();


if (!done) ...{
System.out.println("test");
};
}


public static void main(String[] args) ...{
}
}

运行结果:
test
enter thread
改一下
class
Init
...
{
static boolean done=false;


static ...{

new Thread() ...{

public void run() ...{
System.out.println("enter thread");
done = false;
}
}.start();


if (!done) ...{

try ...{
Thread.sleep(1000);

} catch (InterruptedException e) ...{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("test");
};
}


public static void main(String[] args) ...{
}
}

运行结果:
enter thread
test
这里有两个线程,一个是主线程,一个是自定义的线程,
当程序初始化的时候,主线程初始化static变量和static块,同时在主线程里产生了一个自定义线程,
从运行结果可以看出,自定义线程有点比主线程延迟但是“是先初始化全部完成后才运行自定义线程”这个结论是错误的
在改一下
class
Init
...
{
static boolean done = false;
static int i=0;

static ...{

new Thread() ...{

public void run() ...{
System.out.println("enter thread");//这句会执行
System.out.println("hello");//这句会执行
System.out.println("done="+done);//这句会访问到可能没有初始化的变量,所以未初始化完成后,不会执行
}
}.start();
int all=0;

while(!done)...{

try...{
Thread.sleep(100);

if(all++>=10)...{
done=true;
}
}

catch(Exception exe)...{
exe.printStackTrace();
}
}
System.out.println("left while");//离开了一个static初始化块
}

static...{

try...{
Thread.sleep(1000);
System.out.println("done the 1S");//这句话打印出以后过一秒钟离开这个初始化块,然后线程里面的那句话才执行了
Thread.sleep(1000);
}

catch(Exception exe)...{
exe.printStackTrace();
}
}

public static void main(String[] args) throws Exception...{
}
}
结果:
enter thread
hello
left while
done the 1S
done=true
done=true;这句话之所以不能执行,是因为JAVA在静态变量初始化之前是不允许去访问静态变量的
也就是说所有的static变量或者static都执行完了以后,才可以访问static变量。
在改一下
class
Init
...
{

static volatile boolean done = false;


static void foo() ...{
System.out.println("enter foo");
done = true;
}


static ...{

new Thread() ...{

public void run() ...{
System.out.println("enter thread");
foo();
}
}.start();

while(!done);
}

public static void main(String[] args) ...{}

}

结果是:enter thread
还有一直循环作 while(!done);
为什么不能打印出 enter foo;
在JAVA类没有完全初始化之前,主线程是不允许除初始化之外的别的线程调用它的方法或者变量的
以免出初化出错
在static块里面随时可以访问static 变量,因为JVM会认为这是在初始化阶段,是在给static变量赋值
而别的线程要访问肯定不行,因为static块执行完之前,static的值都是不确定的,所以在别的线程要访问必须等到static全部初始化以后
总结:
初始化的步骤
在静态的
step 1.对有初始值的Static变量赋值 ,其次序为从祖先类到当前类依次赋值
step 2.从祖先类到当前类中的每个类型,依次执行 step 3
step 3. 对类形中所有有初始值的非static的变量,设置初始值;
非static的变量
1. 开始分配分配成员变量的存储空间并进行默认的初始化.就是执行new 关键字。
2. 显示显式或隐式追溯调用父类的构造方法(一直到Object类为止);
3. 进行成员变量的显式初始化操作,也就是执行在定义成员变量时就对其进行赋值的语句.
4 调用child() 这个构造函数
所以可以认为初始化有三次
java 的初始化的机制是个有意思的希望我的这些理解能给大家带来帮助。