不知道这里有没有谈论过这个问题,今天我是偶然间看到的,现复录如下(稍变):
class Singleton  
{  
  private static Singleton  obj = new Singleton(); 
  public static int counter1;  
  public static int counter2 = 0;
  /////////////////////////////////////  
  public static Singleton getInstance()  
  {  return obj;  }
  private Singleton() 
  {  counter1++;   counter2++;  } 
} public class StaticTest 
{  
  public static void main(String[] args) 
  {  
    Singleton obj = Singleton.getInstance();  
    System.out.println("obj.counter1=="+obj.counter1);  
    System.out.println("obj.counter2=="+obj.counter2);  
  }  
}
结果是:
   obj.counter1==1  
  obj.counter2==0
   这个结果我一开始也想不到,作者这样解释:一个类中有两种constructor,一个是class constructor;一个是instance constructor.上面private Singleton()是属于instance constructor.作者说先进入class constructor,在进入之前class已被JVM配置好内存,所有的static field都被设为0,所以此时的counter1和counter2是0,且Singleton为null.在进入class constructor后还未来及将counter1和counter2初始化就呼叫instance constructor也就是private Singleton(),在此之中将counter1和counter2分别自加1,而出来后counter2被设为0而counter1没变.
   然后作者说解决方法是将private static Singleton  obj = new Singleton(); 这一句放在
public static int counter1;  
public static int counter2 = 0;这两句的后面.我试了一下,这样一来两个counter确实都为1.作者还说另外一种解决办法是将所有初始化动作搬到class constructor内不依赖编译器.
   如果说第一种方法我还能理解,那第二方法让我摸不着头脑.各位大虾怎么看?

解决方案 »

  1.   

    我是不明白class constructor和instance construtor
    为什么会先进入class constructor呢
      

  2.   

    1、进入之前class已被JVM配置好内存,所有的static field都被设为0,所以此时的counter1和counter2是0,且Singleton为null.     //这个可以看懂2、在进入class constructor后还未来及将counter1和counter2初始化   //这个可以看懂3、就呼叫instance constructor也就是private Singleton(),  //不懂,为什么要呼叫instance constructor????
      

  3.   

    请那位大哥,把这个程序的开始到结束步聚给讲一下,1、从mian函数,开始,
    2、产生obj的实例对象Singleton obj = Singleton.getInstance();  
    3、调用class Singleton的类构造函数,//是private Singleton() 吗????
    4、接着调用Singleton.getInstance()方法,……………………乱了!!
      

  4.   

    我们用java来解决实际问题。java技术交流,讨论java的技术细节和最新技术。欢迎中高级程序员以及渴望学习java技术的有一定经验的程序爱好者加入讨论。QQ群:3001581
      

  5.   

    他说的第二种方法是指这个吧class Singleton  
    {  
      private static Singleton  obj; 
      public static int counter1;  
      public static int counter2;
      ///////////////////////////////////// 
      public static Singleton getInstance()  
      {  return obj;  }
      private Singleton() 
      {
        counter1=0;
        counter2=0;
        counter1++;
        counter2++;
        obj = new Singleton();
      } 
    }
      

  6.   

    楼上的是错的,如果那样写没有意义啊,编译时提示NullPointerException。现在我把我的理解贴上,同志们继续谈论啊。
       如果我把源码写成这样:
       class Singleton  
       {  
          public static int counter1;  
          public static int counter2 = 0;
          ///////////////////////////////////// 
          public Singleton() 
         {  counter1++;  counter2++;  } 
       }    public class StaticTest 
       {  
         public static void main(String[] args) 
         {  
            Singleton obj = new Singleton();  
            System.out.println("obj.counter1=="+obj.counter1);  
            System.out.println("obj.counter2=="+obj.counter2);  
          }  
        }
        编译运行后:
            obj.counter1==1
            obj.counter2==1;
        这个结果我相信大家都不会感到奇怪,这符合我们的思想,但我恰恰要说的就是里。我想一个类在被new后即实例化后,会做下面五件事情(先不管继承,那太麻烦)。
        一、设置本类变量(实例变量和类变量)的缺省值,如:0、false、null。
        二、初始化本类静态变量和静态块(静态方法是不会自动执行的,除非被谁调用了)。这里就有个先后顺序了,由于静态变量和静态块的地位是平等的,所以采用先来后到制,即谁在前面就先初始化谁。
        三、初始化本类非静态变量和非静态块(非静态方法是不会自动执行的,除非被谁调用了)。这里还是个个先后顺序,非静态变量和非静态块的地位也是平等的,所以仍然采用先来后到制,谁在前面就先初始化谁。
        四、调用本类的构造方法。
        这里的一、二我相就是原作者所称的class constructor,三、四是instance constructor,区别就是class constructor只被执行一次,而instance constructor每new一次就被执行一次。
    现在我解释上面的新代码。
        当new Singleton()时先把counter1和counter2设置为0,然后要初始化静态变量和静态块,这里没有对counter1和counter2初始化,即没有出现类似counter1=...和counter2=...的代码,所以两者仍为0,然后要初始化非静态变量和非静态块(省略),最后调用构造方法将counter1和ccounter2自加1,于是结果是显而易见的。现在来看看最上面的那些代码。
        对于Singleton obj = Singleton.getInstance()这句,有人问干嘛要这么生成一个实例呢?原因是Singleton中唯一的构造方法被private独占,我们不能用new Singleton()(如果改成public然后用new Singleton(),其结果定让人不迷惑)。现在我想大家最想知道发生在Singleton.getInstance()的故事。在声明此句后,类Singleton原则上只应做上面步骤的一、二,因为我们并没显示的调用new Singleton(),但Singleton把上面的几个步骤做完了而且以一种很奇妙的方式。我说下我的想法:
        一、JVM设置obj为null,counter1和counter2为0。
        二、初始化obj、counter1和counter2。在初始化obj时却使用了new Singleton(),这正是奇妙之处。然后就会步骤四,直接调用那个private Singleton()(忽略了步骤一二三。一二不需要再做,因为没有非静态变量和非静态块所以也不做三),调用过后counter1和counter2变成1。然后还得初始化counter1和counter2,因为刚才只初始化了obj。可以看到counter1并没有变而counter2=0,表明counter又被写回0。
        至此结束,counter1=1,counter2=0。
        原作者提供的解决办法一是将obj挪到counter1和counter2后面,这当然可行因为obj出来后不做任何事直接返回主程序。办法二是说把所有的初始化动作搬到class constructor中我仍不知怎么回事。如果把public static Singleton obj = new Singleton()去掉,直接在getInstance()中写return new Singleton(),结果就是counter1=counter2=0。这会不会是第二种方法呢?
        上面只谈论了静态的一些东西,现在如果在public static counter2 = 0;下面加个非静态块
    static { counter1 = 1; counter2 = 1;},那么上述结果又变了,变成counter1=2 counter2=0。有意思吧。
      

  7.   

    楼主;在第一种解释中,不是进入程序后构造函数就分配内存吗?怎么能没来得及用private singleton()初始化就去执行 
    instance constructor呢?
      

  8.   

    说实话,我不知道JVM是怎么分配内存的,我只是根据一步步调试来判断的.我不敢保证我所说的全部正确,但绝大部分应该差不多.
      

  9.   

    我来解释吧 
    看了Tinking in java 的人都知道,有几个关于初始化问题的相关经典例子!!
    这个问题也和初始化问题差不多!!
     
    如果全局变量 带有static 关键字 ,则 这个变量在类加载中被付值!!
     我来说初始化的顺序 
      注意: --  代表下一步操作
      (1)当Singleton类 被加载的时候,private static Singleton  obj = new Singleton(); 这句化被首先执行,然后需要调用够照函数—-引起counter1;counter2这2句话的调用(此时counter1和counter2都为0)--然后counter1和counter2自加都为1--然后singeleton对象被够造,保存在getInstance() 方法中
       (2)在执行  public static int counter1;  public static int counter2 = 0;
    这2句话 分别付值(在这里是第2次被调用了),第2次调用的时候 counter1只是被声明并没有改变counter1的值仍然为1 ,而调用counter2的时候,counter2又被复值为0--最后看到我们的结果
     
    ps:你们把public static int counter2 = 0;这句话放到最前面在执行,在看运行效果
      

  10.   

    这里的关键是 够造 对象的时候 counter1 和counter2 的付值被执行了2遍
      

  11.   

    以下是LZ程序的执行顺序
    class Singleton  
    {  
      private static Singleton  obj = new Singleton();    //1   counter1=0,counter2=0
      public static int counter1;                         //3   counter1=1,counter2=1
      public static int counter2 = 0;                     //4   counter1=1,counter2=0  public static Singleton getInstance()    
      {  return obj;  }                                   //6   counter1=1,counter2=0
      private Singleton()      
      {  counter1++;  counter2++;  }                      //2   counter1=1,counter2=1
    } public class StaticTest 
    {  
      public static void main(String[] args) 
      {  
        Singleton obj = Singleton.getInstance();           //5  counter1=1,counter2=0
        System.out.println("obj.counter1=="+obj.counter1); //7  counter1=1,counter2=0
        System.out.println("obj.counter2=="+obj.counter2); //8  counter1=1,counter2=0
      }  
    }
      

  12.   

    java程序是虚拟机解释的,其运行过程是简单、确定的。准确的java运行机制可以参考Inside JVM一书,没有必要自己琢磨。
    在介绍java程序运行过程前,首先说明以下规则:
    规则1、 jvm在第一次用到某个类时,才会装载这个类。
    规则2、在装载类时,jvm为这个类的所有静态变量分配空间,并全部初始化为0。然后按照静态变量在类定义中出现的顺序依次调用每个静态变量的初始化语句。
    规则3、在创建类的实例时,jvm为这个类的所有实例(非静态)变量分配空间,并全部初始化为0。然后按照实例变量在类定义中出现的顺序依次调用每个实例变量的初始化语句。最后调用这个类的构造函数。根据上述规则,java程序的运行过程应该是这样的:
    1、jvm装载用户指定的类,为这个类的所有静态变量分配空间,并全部初始化为0。然后按照静态变量在类定义中出现的顺序依次调用每个静态变量的初始化语句。然后才开始找main函数,如果找不到,就提示"java.lang.NoSuchMethodError: main",找到就执行main。
    2、依次执行main函数的各个语句。
    3、遇到没有装载过的类,就按照“规则2”装载这个类。
    4、遇到new语句,就按照“规则3”创建类的实例。按照前面介绍的规则。主贴的例子应该很容易理解。其实原作者说得已经很清楚了。但tcpipokokok的解释有误。 
    原作者的说的“另外一种解决办法”应该是说将静态变量的初始化全部放在构造函数中,不要用静态变量的初始化语句。这样就不用依赖初始化语句的调用顺序了。
      

  13.   

    下面这个例子可以看作以上说明的demo:
    // Singleton.java
    class Singleton {
    private static JustTest s1 = new JustTest("调用Singleton类静态变量s1的初始化语句");
    private JustTest i1 = new JustTest("调用Singleton类实例变量i1的初始化语句");
    public static Singleton getInstance() {
    return obj;
    }
    private Singleton(){
    System.out.println("调用 Singleton 的构造函数");
    }
    private JustTest i2 = new JustTest("调用Singleton类实例变量i2的初始化语句");
    private static JustTest s2 = new JustTest("调用Singleton类静态变量s2的初始化语句");
    private static Singleton  obj = new Singleton();
    }
    // StaticTest.java
    class StaticTest {
    private static JustTest s1 = new JustTest("调用StaticTest类静态变量s1的初始化语句");
    private static JustTest s2 = new JustTest("调用StaticTest类静态变量s2的初始化语句");
    private JustTest i1 = new JustTest("调用StaticTest类实例变量i1的初始化语句");
    private JustTest i2 = new JustTest("调用StaticTest类实例变量i2的初始化语句");
      public static void main(String[] args) {
    System.out.println("进入main函数");
    Singleton obj = Singleton.getInstance();
    }
    }
    // JustTest.java
    class JustTest {
    public JustTest(String str) {
    System.out.println(str);
    }

      

  14.   

    建议各位有兴趣对这个问题做深入研究的兄弟们,先用 Eclipse 之类的调试工具,对程序单步执行一遍,基本就明白得差不多了。另外,不知道下面的写法是不是楼主所说的“第二种方法”?class Singleton
    {
        private static Singleton obj;
        public static int counter1;
        public static int counter2;
        /////////////////////////////////////
        public static Singleton getInstance()
        {  return obj;  }
        private Singleton()
        {  counter1++;  counter2++;  }
        static {
            obj = new Singleton();
            counter1 = 0;
            counter2 = 0;
        }
    }
      

  15.   

    fmddlmyy(寒潭)说得很好,专业
    请 fmddlmyy(寒潭) 告知我的错误,谢谢
      

  16.   

    我对fmddlmyy(寒潭)的规则2有疑问。你指的Inside Java是王森写的吧中文名字叫java深度历险。小弟也有幸拜读过,王森先生提到了类的载入(load)和实例化(instance)是不同的,在类只是load时王森先生提到了:并不执行static静态初始化块中的语句,王森先生没有提到是否执行静态变量的初始化。做个实验:
    class Singleton  
    {  
       private static Singleton obj = new Singleton(); 
       public static int counter1;  
       public static int counter2 = 0;   public static Singleton getInstance()  
       {  return obj;  }    private Singleton() 
       {  counter1++;  counter2++;  System.out.println("******"); } 
    } public class StaticTest 
    {  
       public static void main(String[] args) 
       {  
          try
          {
             Class objClass = new StaticTest().getClass();
             Class.forName("Singleton", false, objClass.getClassLoader());//此句是将类Singleton载入并不实例化(王森先生提到)
          }
          catch (Throwable e)
          {  System.err.println(e); }
        }  
    }
    如果按fmddlmyy(寒潭)说的,在类在装载类时,jvm为这个类的所有静态变量分配空间,并全部初始化为0。然后按照静态变量在类定义中出现的顺序依次调用每个静态变量的初始化语句。那么obj应该被初始化,则要调用构造方法,必然将打印出******,可是我的结果什么也没有。这是怎么回事?
      

  17.   

    不好意思,我自己失误了,fmddlmyy(寒潭)的规则2是对的
      Class.forName("Singleton", false, objClass.getClassLoader());第二参数为true时静态变量和静态块就初始化了,此时仍是load,没有instance,抱歉。
      

  18.   

    对于这个问题我的理解是这样的:不知大家知不知道Java有一个Partially Initialization(部分初始化)机制,就是在对象还未被初始化时,如果需要调用对象的某些成员,则会打破常规初始化顺序,先对该对象执行部分初始化,即只初始化所需的部分,等到按常规顺序执行到相应步骤时,再补充余下的初始化工作。关于“部分初始化”,一个常见的例子就是,在父类变量初始化中,或构造函数中调用多态方法。因为多态方法要用到子类的实例,所以这时需要对先对子类做“部分初始化”,以保证这个方法被正常调用。楼主的这个例子关键语句是:
    private static Singleton  obj = new Singleton(); 
    public static int counter1;  
    public static int counter2 = 0;
    private Singleton() { counter1++;  counter2++;  } 
    这四句虽然不属于我说的这种情况,但也可以做类似这样的解释:我们知道,static变量的初始化是先于对象的初始化和构造函数的执行的,但在第一句的static变量的初始化中调用了构造函数,这就需要对对象做部分初始化,从而使得counter1和counter2先被初始化为0,再依次递增为1。当执行到第二句时,由于counter1已经被初始化了,因而该句被编译器忽略。当执行到第三句时,编译器检测到一个赋值操作,于是counter2被重新赋值为0。于是就出现了程序所显示出的结果:counter1 == 1, counter2 == 0
      

  19.   

    JAVA这种不严密的语言才会出这种问题
      

  20.   

    我说的"Inside JVM"中文版的名字是《深入Java虚拟机》。我在两三年前看过,比较适合希望了解底层机制或者想做JVM的朋友。
      

  21.   

    所谓第二种方法不就是叫你把counter2=0绊倒构造函数里来么
    class Singleton 

    private static Singleton obj = new Singleton(); 
    public static int counter1; 
    public static int counter2;
    ///////////////////////////////////// 
    public static Singleton getInstance() 
    { return obj; }
    private Singleton() 
    { counter2 = 0; counter1++; counter2++; } 
    }
      

  22.   


    -----
    fmddlmyy(寒潭) ( ) 信誉:100 规则1、 jvm在第一次用到某个类时,才会装载这个类。
    规则2、在装载类时,jvm为这个类的所有静态变量分配空间,并全部初始化为0。然后按照静态变量在类定义中出现的顺序依次调用每个静态变量的初始化语句。
    规则3、在创建类的实例时,jvm为这个类的所有实例(非静态)变量分配空间,并全部初始化为0。然后按照实例变量在类定义中出现的顺序依次调用每个实例变量的初始化语句。最后调用这个类的构造函数。
    ------
    一目了然
      

  23.   

    我觉得这是个很简单的问题,只要了解JAVA的初始化步骤就应该能理解为什么会这现那样的结果这两条语句:
    public static int counter1;
    public static int counter2 = 0;
    是在counter1和counter2被赋值为默认的0并且递增后执行的不需要研究JVM,看看TIJ也一样能搞懂这些事情
      

  24.   

    看看字节码就知道发生什么事了:static {};
      Code:
       Stack=2, Locals=0, Args_size=0
       0:   new     #2; //class Singleton
       3:   dup
       4:   invokespecial   #15; //Method "<init>":()V
       7:   putstatic       #17; //Field obj:LSingleton;
       10:  iconst_0
       11:  putstatic       #19; //Field counter2:I
       14:  return
      

  25.   

    将所有初始化动作搬到class constructor内不依赖编译器.
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------我想意思应是这样的:
    class constructor 初始化时会依次执行下面的语句:
     private static Singleton  obj = new Singleton(); 
      public static int counter1;  
      public static int counter2 = 0;
    当第一句执行时却调用了new Singleton();这是个instance constructor
    所以该程序的初始化动作并不完全是在class constructor 中完成的
    解决办法:将这句:
    private static Singleton  obj = new Singleton(); 中的new Singleton();方法修改为应该在class constructor中完成的( 静态)方法;比如
    private static Singlecton  getNew(){return obj};
      

  26.   

    整个程序改了下成了这样:
    class Singleton  
    {  
      private static Singleton  obj = getNew(); 
      public static int counter1;  
      public static int counter2 = 0;
      ///////////////////////////////////// 
      public static Singleton getInstance()  
      { obj=new  Singleton();
       return obj;  }
      private Singleton() 
      {  counter1++;  counter2++;  } 
      private static Singleton getNew()
      {return obj;}
    } public class StaticTest 
    {  
      public static void main(String[] args) 
      {  
        Singleton obj = Singleton.getInstance();  
        System.out.println("obj.counter1=="+Singleton.counter1);  
        System.out.println("obj.counter2=="+Singleton.counter2);  
      }  
    }
      

  27.   

    class Singleton
    {
    private static Singleton obj = new Singleton(); // step 1
    public static int counter1; // step 4
    public static int counter2 = 0; // step 5:counter2 == 0
    /////////////////////////////////////
    public static Singleton getInstance()
    { return obj; }
    private Singleton()
    { counter1++; //step 2: counter1 == 1
    counter2++;  //step 3: counter2 == 1
    System.out.println("counter 2: " + counter2); // 这里会打印出来,counter2 == 1的。
    }
    }Step 1 ~ 5 是程序的执行顺序,你看step 3 之后 counter2 == 1,
    然后step 5后 counter2 == 0了。
      

  28.   

    关于class constructor与instance constructor,这个是虚拟机内部对类的实现机制,讨论的太详细没有什么实际意义。这个问题我想可不可以这样回答。
    这是一个类中静态成员变量,与静态方法初始化顺序的问题,以楼主的程序为例子,
    class Singleton  
    {  
      private static Singleton  obj = new Singleton(); (1)
      public static int counter1;                      (2)
      public static int counter2 = 0;                  (3)  
      ///////////////////////////////////// 
      public static Singleton getInstance()            (4)  
      {  return obj;  }
      private Singleton()                              (5)
      {  counter1++;  counter2++;  } 
    } public class StaticTest 
    {  
      public static void main(String[] args) 
      {  
        Singleton obj = Singleton.getInstance();  
        System.out.println("obj.counter1=="+obj.counter1);  
        System.out.println("obj.counter2=="+obj.counter2);  
      }  
    }当Singleton类的静态方法,或者变量被调用时,虚拟机就会对所有的静态变量,方法进行默认初始化,初始化的顺序是先变量,后方法。此时(1)为null(2)为0,(3)为(0),(4)是一个方法,对方法的初始化,虚拟机只要求知道Singleton类,有这个名字叫Singleton getInstance()的静态方法,这个方法是无参的返回值是Singleton的,其他的他一概不管。
    然后进行显示初始化,此时在(1)运行时,这个Singleton 变量被联到一个对象的引用上,通过构造方法创建了一个这个类的实例,构造方法把变量(2),(3)进行了运算。使得(2),(3)为1;此时(1)的初始化完毕。(2)开始初始化,因为(2)并没有指定显示初始化的值,所以保持=1不便。可是(3)指定了显示初始化的值所以(3)为0。然后轮到方法他仍然不管你的方法的具体实现。到调用的时候他会去管。这个例子并没有在静态方法里调用构造器。如果调用一次构造器:
      public static Singleton getInstance()  
     {  return new Singleton();  }
    然后在测试类里面调用一下这个方法,此时:变量的值将分别为:2,1。
    如果将(1)移动到(3)下面,按照初始化顺序自然为:1,1。
    如果将此例改为:
    class Singleton  
    {  
      private static Singleton  obj ;
      public static int counter1;  
      public static int counter2 ;
       
      ///////////////////////////////////// 
      public static Singleton getInstance()  
     {  return new Singleton();  }
      private Singleton() 
      {  counter1++;  counter2++;  } 
    } public class StaticTest 
    {  
      public static void main(String[] args) 
      {  
        Singleton obj = Singleton.getInstance();  
        System.out.println("obj.counter1=="+Singleton.counter1);  
        System.out.println("obj.counter2=="+Singleton.counter2);  
      }  
    }
    这个改法就应该是将初始化全部交给class constructor的意思了。如果你一定要有显示初始化那就这样:
    class Singleton  
    {  
      private static Singleton  obj ;
      public static int counter1; 
    // counter2可以等于任何值
      //public static int counter2=5 ;
      public static int counter2=0; 
      ///////////////////////////////////// 
      public static Singleton getInstance()  
     {  return new Singleton();  }
      private Singleton() 
      {  counter1++;  counter2++;  } 
    } public class StaticTest 
    {  
      public static void main(String[] args) 
      {  
        Singleton obj = Singleton.getInstance();  
        System.out.println("obj.counter1=="+Singleton.counter1);  
        System.out.println("obj.counter2=="+Singleton.counter2);  
      }  
    }
    这样不会出现任何问题。
    我认为如果笔者出这道题,他的目的应该是使读者理解初始化顺序,而不是什么编译器的内部机制。毕竟编译器的内部机制对于绝大多数的程序员来讲不用理解的太深刻。
      

  29.   

    楼主这个程序本来就不严谨。
    在声明一个变量后一定要对它初始化,何况你的两个静态变量是在系统调用构造函数之前就被执行了的我刚建的技术交流QQ群:27856203,我是个喜欢交流的人,并坚信交流是提高的最重要因素。我每天都会更新群里面一些心得体会,希望你也能上去留言。本论坛刚刚建立,请不要在里面讨论非IT技术问题。C,C++,JAVA,VB,C#,SQL,欢迎这方面的高手
      

  30.   

    刚才仔细看了这个例子,该例子的作者不过是为了证明一个类中所有语句的执行顺序。
    当外部得到一个类的实例后,类的内部就会创建这个实例,在创建这个实例的时候,就会调用内部的私有的构造函数,将两个静态变量+1(原来默认不变),加完后,在初始化静态变量,由于一个有初始化值,所以变回了0,而另一个没有初始化,维持1不变。------------------------------------------------------------------------------
    我刚建的技术交流QQ群:27856203,我是个喜欢交流的人,并坚信交流是提高的最重要因素。我每天都会更新群里面一些心得体会,希望你也能上去留言。本论坛刚刚建立,请不要在里面讨论非IT技术问题。C,C++,JAVA,VB,C#,SQL,欢迎这方面的高手
      

  31.   

    fmddlmyy(寒潭)说得很好,专业
    ---------------------------------------
    fmddlmyy(寒潭)你能说说,楼主的这个程序里foxxiao111()的这句话:“这里的关键是 够造 对象的时候 counter1 和counter2 的付值被执行了2遍” 对还是错
      

  32.   

    先说明,我自己的观点是counter1被赋值了一次,counter2 被赋值了2次
    我觉得conunter1被赋值为1,而counter2先被赋值为1,后有被赋值为0;
      

  33.   

    很久没看java了
    先留个记号。
      

  34.   

    class Singleton  
    {  
      private static Singleton  obj ;
      public static int counter1;  
      public static int counter2 ;
       
      ///////////////////////////////////// 
      public static Singleton getInstance()  
     {  
       if(obj!=null)
         return obj;
         return new Singleton(); 
      }
      private Singleton() 
      {  counter1++;  counter2++;  } 

      

  35.   

    public static int counter1; (2)
    private static Singleton obj = new Singleton(); (1)
    public static int counter2 = 0; (3)
    如果是这样呢?大家说说有是个什么结果呢? 
      

  36.   

    counter1 = 1;
    counter2 = 0;
      

  37.   

    语法不严谨,既然counter1是static,就不可以通过obj.counter1来调用
      

  38.   

    private static Singleton  obj = new Singleton(); 
      public static int counter1;  
      public static int counter2 = 0;依次执行就行了
      相当于执行语句:
      counter1=0; //默认初始化
      counter2=0; //默认初始化
      counter1++;  counter2++;  //第一句执行
      //第二句没有做初始化,所以没有语句可执行
      count2=0;//第三句有初始化语句,执行。如果你把构造函数换成其他的方法就更容易理解了。
      

  39.   

    同意楼上的解释;
    如果把顺序交换的话,结果就会不一样:
    public static int counter1;
    public static int counter2 = 0;
    private static Singleton obj = new Singleton();
    这样的话,结果就是1,1;
      

  40.   

    奇怪的Java运行顺序
    ---------------------------------------------------------------------
    <pre>
    public class Singleton {
    private static Singleton obj = new Singleton(); //1 public static int counter1; //2 public static int counter2 = 0; //3 public static Singleton getInstance() { //4
    return obj; //5
    } private Singleton() { //6
    counter1++; //7
    counter2++; //8
    } public static void main(String[] args) { //9
    Singleton obj = Singleton.getInstance(); //10
    System.out.println("obj.counter1==" + obj.counter1); //11
    System.out.println("obj.counter2==" + obj.counter2); //12
    }
    }
    </pre>
    运行结果的确让人大跌眼镜,竟然是:
    obj.counter1==1
    obj.counter2==0
    ---------------------------------------------------------------------
    之后使用eclipse代码跟踪了一下,发现竟然是程序竟然是这么执行的!
    步骤以及说明如下:
    1.根据Java的类加载机制,加载类时首先分配static变量默认值以及执行static块,具体顺序根据代码中的顺序来执行.即:程序执行到1步骤,先分配obj=null,然后调用Singleton()方法;
    2.调用Singleton()方法,进入Singleton()方法体,执行7和8步骤,将counter1和counter2分别+1;
    3.从Singleton()方法中返回,由于没有执行完所有的static的变量或者代码块的初始化,则继续从2步骤开始执行;
    4.由于2步骤不进行初始化赋值,则counter1的值没有发生改变,还是==1;
    5.执行3步骤,由于counter2需要进行初始化,则执行counter2=0的代码,这个是问题的根源;
    5.整个static执行完毕后,在进入10步骤,将结果打印出来为"counter1==1,counter2==0"
    ---------------------------------------------------------------------
    总结:
    1.由于单例模式比较特别,static的执行步骤跟代码的顺序有关.如果把<pre>private static Singleton obj = new Singleton();</pre>放到<pre>public static int counter1; public static int counter2 = 0;</pre>之后,则最终结果是"counter1==1,counter2==1".即:static的初始化跟代码顺序有关.
    2.类的实例构造函数其实是经过new操作后,JVM在分配实例的空间后,进行自动调用的方法.一般我们用来初始化实例的成员数据(这也是最常见的用法).
    ---------------------------------------------------------------------
    说明自己对类的加载的认识:
    1.第一次加载类时,JVM会给类的static变量分配空间,并且使用默认值初始化分配的空间(基本数据为0,对象为null).笼统的说,就是把一些为0的空间分配给类的static变量.
    2.根据类中的代码顺序,以此执行static变量的自定义初始化或者static代码块.其中,我们可以在代码中给static变量赋值,也可以在static代码块中执行更加复杂的操作.
    3.通过调用new操作符,向JVM申请类的实例变量的空间,即为类的实例分配非static变量的空间,同static变量分配空间一样,JVM也是使用默认值来初始化分配的非static变量的空间.这时,实例拥有一些为0的空间.
    4.然后JVM调用类的构造函数,进行一系列的操作.一般我们都是做一些对非static变量的初始化工作.从另外一个角度说,类的构造函数的作用跟类的static代码块差不多:构造函数会在非static变量获得空间后调用,而static代码块会在static变量获得空间后调用.
    5.需要注意的是,static代码块或者构造函数都是在类或者类实例拥有自己的内存空间后的前提下才被调用的,一般都用来自定义的初始化操作.
    ---------------------------------------------------------------------
    对于上面的Singleton单例模式,比较特殊.主要是这几点:
    1.在执行static变量的自定义初始化或者static代码块的时候,突然new一个本身类的实例.
    2.在构造函数中,对static变量进行赋值操作.
    3.在返回时,继续执行未完成的static变量的自定义初始化或者static代码块工作,导致又重复对static变量重复赋值的操作.
    ---------------------------------------------------------------------
    理解了上面几点,我想应该对JVM的类加载应该有一个比较明确的认识了:-)