黑马程序员技术交流社区

标题: 关于类加载的问题 [打印本页]

作者: 傅宇    时间: 2013-3-16 14:37
标题: 关于类加载的问题
  1. public class Demo {
  2.         public static void main(String[] args) {
  3.                 Singleton singleton = Singleton.getInstance();
  4.                 System.out.println("counter1=" + singleton.counter1);
  5.                 System.out.println("counter2=" + singleton.counter2);
  6.         }
  7. }

  8. //单例1
  9. class Singleton {
  10.         private static Singleton singleton = new Singleton();
  11.         public static int counter1;
  12.         public static int counter2 = 0;

  13.         private Singleton() {
  14.                 counter1++;
  15.                 counter2++;
  16.         }

  17.         public static Singleton getInstance() {
  18.                 return singleton;
  19.         }
  20. }

  21. //单例2
  22. class Singleton {
  23.         public static int counter1;
  24.         public static int counter2 = 0;
  25.         private static Singleton singleton = new Singleton();
  26.        
  27.         private Singleton() {
  28.                 counter1++;
  29.                 counter2++;
  30.         }

  31.         public static Singleton getInstance() {
  32.                 return singleton;
  33.         }
  34. }
复制代码
为什么单例1运行出来的结果与单例2运行出来的结果不同?求详细分析。
作者: 姓名长度不符    时间: 2013-3-16 18:09
怎么没人回答啊?我等了一下午竟然没人来?lz顶你
为什么只是调换了静态变量与构造对象的顺序,类中的方法就被调用了
问题应该是这里吧?
作者: 黑马-郑玉元    时间: 2013-3-16 18:15
怎么还没有人回答呢!个人觉得这关系到用staitic修饰的成员属性的特点,构造方法的实例化过程。
单例一:
先构造方法实例化,counter1=1,counter2=1,初始化完成后,又给赋值成了0;
单例二:
前面两句没什么好说的,前面两句执行后是counter1=0,counter2=0.然后再构造方法实例化,counter1=1,counter2=1。
我觉得事情就是这么简单!!!
作者: 刘海浩    时间: 2013-3-16 21:33
本帖最后由 刘海浩 于 2013-3-16 21:42 编辑

单例一:首先执行Singleton类中private static Singleton singleton = new Singleton();调用无参构造方法,分别是counter1和counter2自加1,然后是给静态变量counter2赋值0,所以最后输出counter1=1;counter2=0;
单例二:首先声明两个静态变量counter1、counter2,counter2变量的值为0;然后执行 private static Singleton singleton = new Singleton();调用调用无参构造方法,分别是counter1和counter2自加1;所以最终输出counter1=1;counter2=1;

作者: 袁术森    时间: 2013-3-17 09:41
我觉得应该是这样的,首先当主函数中执行到Singleton singleton = Singleton.getInstance();语句时,类class Singelton就会加载进内存中的方法区。在方法区里,依次存储类中的成员变量、成员函数。第一步就是引用型变量singleton的默认初始化为null,count1、count2默认初始化为0,构造函数和其他函数。第二步,开始进行执行可运算语句。单例1的第一句是在本类中创建本类的对象并将其首地址值赋值给引用型变量singleton。第三步,在堆内存中创建本类对象。构造函数对本类对象初始化,此时的count1、count2进堆内存时都还是默认的初始值0,在方法区中他们还没有执行到赋值语句。经过构造函数的自增后分别变为了1,该对象创建完毕后,第一条运算语句结束。第四步,方法区中继续执行可运算语句,下面的就是对count2的赋值语句,所以count2又由1变为了0,所以count1,count2 在方法区的最终值是1、0.
同理,单例2中是先对count2进行了赋值运算,由默认初始化值0变为赋值为0,然后再在本类中创建对象,通过构造函数的自增,将count1、count2在方法区静态存储区的值最终定格在了1、1.
作者: 姓名长度不符    时间: 2013-3-17 11:03
袁术森 发表于 2013-3-17 09:41
我觉得应该是这样的,首先当主函数中执行到Singleton singleton = Singleton.getInstance();语句时,类clas ...

关于第三步, public static int counter1;
                   public static int counter2 = 0;这不是静态的成员变量么?静态的变量不是优先于对象,同步于类么?一旦加载类,静态变量就初始化么?

作者: 袁术森    时间: 2013-3-17 11:36
本帖最后由 袁术森 于 2013-3-17 12:49 编辑

我是这样理解的 不知正确与否
首先 ,这个类的构造函数是私有的,也就是说 不能被其他类调用创建新的对象。只能在本类中创建自己的对象。
类的加载一般是在方法区中对成员变量的赋值,成员函数的存储。成员变量赋值是通过赋值运算动作完成的,在此之前,在开辟的变量空间中,变量值都是默认初始化值。
这里的对象建立完毕后,类的加载其实并没有完成,因为,类中的赋值运算并未有执行到。
我把代码修改一下 就可以看出进入对象中的类的变量赋值动作在对象建立前完成没有。
  1. class Demo {
  2.         public static void main(String[] args) {
  3.                 Singleton singleton = Singleton.getInstance();
  4.                 System.out.println("counter1=" + singleton.counter1);
  5.                 System.out.println("counter2=" + singleton.counter2);
  6.         }
  7. }

  8. //单例1
  9. class Singleton {
  10.         private static Singleton singleton = new Singleton();
  11.         public static int counter1=6;
  12.         public static int counter2 = 0;

  13.         private Singleton() {
  14.                 counter1++;
  15.                 counter2++;
  16.                                 System.out.println(counter1);
  17.         }

  18.         public static Singleton getInstance() {
  19.                 return singleton;
  20.         }
  21. }
复制代码
构造函数中加入了System.out.println(counter1);结果为1  6  0;从而说明赋值动作在类加载进内存中还没有执行到。你说的静态成员变量在对象建立前完成,是正确的,我的理解是前提是 外部调用本类,要创建本类对象时这种通常情况。
我认为楼主的这道题 是在细化类的加载进内存的过程。




作者: 姓名长度不符    时间: 2013-3-17 12:30
袁术森 发表于 2013-3-17 11:36
我是这样理解的 不知正确与否
首先 ,这个类的构造函数是私有的,也就是说 不能被其他类调用创建新的对象 ...

关于“赋值动作并没有执行到”,照这么解释,单列2的赋值动作也应该没有执行到,但是如果也在构造方法中加入打印的话,输出为7 7 1,说明他还是执行了赋值动作;
我是根据你的思路重启整理了下,先从main来入手
Singleton singleton = Singleton.getInstance();,这里并没有直接new对象,而是调用了singleton的getInstance()方法,getInstance()方法返回的是个对象,这时需要new一个对象,所以,此时开始加载类,单例1,第一行为静态的new对象,调用构造方法,所以根据你加的代码,打印counter1++;然后我与你的不同理解之处是:这里应该继续加载类中第二第三行的静态变量;进行默认初始化和显示初始化,故此时counter1和counter2分别为6和0,此时类已经加载完成;之后回到main,调用singleton.counter1与singleton.counter2;所以counter1为6,counter2为0;到此为止符合单例1的结果。
以此 再看单例2,同样是到main方法的Singleton singleton = Singleton.getInstance();依旧是因为调用Singleton.getInstance()方法,需要返回对象而开始加载类,但是不同的是,加载类的第一行不是new对象,而是加载静态成员变量,进行默认初始化和显示初始化;此时的counter1和counter2分别为null和0;之后是new对象,调用构造方法,此时counter1和counter2分别为1和1,此时类已经加载完成;回到main,调用singleton.counter1与singleton.counter2;打印1和1;
所以我认为静态成员的赋值动作还是进行了的
作者: 袁术森    时间: 2013-3-17 13:17
本帖最后由 袁术森 于 2013-3-17 13:20 编辑
姓名长度不符 发表于 2013-3-17 12:30
关于“赋值动作并没有执行到”,照这么解释,单列2的赋值动作也应该没有执行到,但是如果也在构造方法中 ...

对于下面这个问题你是怎么理解的呢?单例一当类加载过程中执行到第一条语句 即创建本类对象的语句时,此时,类完成加载没有,如果完成了那么类中的赋值动作完成了没有,那么类中的成员变量(在方法区中)此时的值是多少呢 是0和0 呢 还是6 和0 呢?
另外 ,单例二,
  1. class Demo {
  2.         public static void main(String[] args) {
  3.                 Singleton singleton = Singleton.getInstance();
  4.                 System.out.println("counter1=" + singleton.counter1);
  5.                 System.out.println("counter2=" + singleton.counter2);
  6.         }
  7. }


  8. //单例2
  9. class Singleton {
  10.         public static int counter1=6;
  11.         public static int counter2 =0;
  12.         private static Singleton singleton = new Singleton();
  13.         
  14.         private Singleton() {
  15.                 counter1++;
  16.                 counter2++;
  17.                                 System.out.println(counter1);
  18.         }

  19.         public static Singleton getInstance() {
  20.                 return singleton;
  21.         }
  22. }
复制代码
是引用型变量 count1、count2、singleton默认初始化分别为0、0、null,然后依次执行可运算语句。而类在加载中被执行到的第一句 是count1的赋值语句,所以此时count1、count2在方法区的值是6、0,然后才执行到创建对象的语句,即对引用型变量赋值的语句。我认为在本类中创建对象的这条语句本身就是类加载过程中的一部分。而不是类加载完毕后才进行创建的对象。因为这里的构造函数是私有的,而主函数的那条语句只是在调用类中的静态方法。
我还是认为单例一中 在类加载过程中执行到创建对象语句时,由于赋值语句在后,所以在进入堆时 count1、count2的值在方法区中的值仍为默认初始化值,还未执行到赋值动作。
单例二 是在类加载中先执行了赋值语句,所以在执行到创建对象语句时,进入堆时count1、count2在方法区中的值已被赋值动作变为了6、0
作者: 陈丽莉    时间: 2013-3-17 13:28
若仍有问题,请继续追问或完善问题;没问题的话,请将帖子分类改成【已解决】~
作者: 袁术森    时间: 2013-3-17 13:30
陈丽莉 发表于 2013-3-17 13:28
若仍有问题,请继续追问或完善问题;没问题的话,请将帖子分类改成【已解决】~ ...

可是斑竹
我和长度姓名不符同学的争论谁对谁非呢
我俩都挺纠结的啊
求指点啊
作者: 陈丽莉    时间: 2013-3-17 13:33
袁术森 发表于 2013-3-17 13:30
可是斑竹
我和长度姓名不符同学的争论谁对谁非呢
我俩都挺纠结的啊

互相促进共同进步很好啊,那你们继续讨论吧~~~
作者: 袁术森    时间: 2013-3-17 13:35
陈丽莉 发表于 2013-3-17 13:33
互相促进共同进步很好啊,那你们继续讨论吧~~~

我要吃饭 头大了
作者: 姓名长度不符    时间: 2013-3-17 13:42
本帖最后由 姓名长度不符 于 2013-3-17 13:49 编辑
袁术森 发表于 2013-3-17 13:17
对于下面这个问题你是怎么理解的呢?单例一当类加载过程中执行到第一条语句 即创建本类对象的语句时,此时 ...

单例一当类加载过程中执行到第一条语句时,构建完对象,此时类应该没有加载完成,只是此时优先执行了构造方法,即
                   counter1++;
                   counter2++;
                   System.out.println(counter1);故counter1打印1,
但是还有静态成员变量没加载,继续在方法区(为什么我喜欢叫代码区- -)加载
                   public static int counter1=6;
                   public static int counter2 = 0;此时类完成加载,返回main主方法
主方法继续 System.out.println("counter1=" + singleton.counter1);
                 System.out.println("counter2=" + singleton.counter2);
故counter1打印6,counter2打印0
关于单例2,我的意思和你没什么出入啊?。。。。突出以下我的重点,只要加载类,静态成员变量与静态代码块一定会被加载,至于静态变量赋没赋值,看实际语句,就以这单例1和2,        
                                                  public static int counter1=6;
                                                  public static int counter2 =0;这里明显赋值了,所以此时counter1,2会有值
而我们两的分歧是这里面有先后顺序?我还是跟着代码走,第一行是什么,就执行什么,执行完,就接着第二行
作者: 姓名长度不符    时间: 2013-3-17 13:45
版主明显不想告诉我们{:soso_e127:}




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2