java初始化之我的理解

   
看了张老师写的《一个让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 的初始化的机制是个有意思的希望我的这些理解能给大家带来帮助。
 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值