A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 王程 中级黑马   /  2012-8-5 22:49  /  2467 人查看  /  10 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 王程 于 2012-8-6 09:07 编辑

  1. class A
  2. {
  3.    A(){}//改一下,这里把private去掉了

  4. B b=new B();
  5. }
  6. class B
  7. {
  8.    B(){}//也去掉private
  9. A a=new A();
  10. }
  11. class Test4
  12. {
  13. public static void main(String[] args)
  14. {
  15. A a=new A();
  16. System.out.println(123);
  17. }
  18. }
复制代码
上面的代码编译无错,但是运行时会出错。
  1. class A
  2. {
  3. private static A a = new A();

  4. private B b = B.getInstance();

  5. private A(){}

  6. public static A getInstance()
  7. {
  8. System.out.println("A被调用");
  9. return a;
  10. }

  11. public void test()
  12. {
  13. System.out.println(b);
  14. }
  15. }


  16. class B
  17. {
  18. private static B b = new B();

  19. private A a = A.getInstance();

  20. private B(){}

  21. public static B getInstance()
  22. {
  23. System.out.println("B被调用");
  24. return b;
  25. }

  26. public void test()
  27. {
  28. System.out.println(a);
  29. }
  30. }


  31. class Test
  32. {
  33. public static void main(String[] args)
  34. {

  35. B b = B.getInstance();
  36. b.test();
  37. System.out.println("==========");
  38. A a = A.getInstance();
  39. a.test();
  40. }
  41. }
复制代码
这个程序中也存在相互方法调用,可以运行,有输出结果。
B被调用
A被调用
B被调用
A@35ce36
==========
A被调用
null

有两个问题,第二个为什么不会报错,而第一个报错?
第二个是主要问题,为什么会有这样的结果,本人纠结了好久,没有搞懂程序是怎么运行的,希望有高手能够详细讲解一下过程。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1 赞一个!

查看全部评分

10 个回复

倒序浏览
第一组代码里, 因为你的A 和 B 的构造方法都是私有的, 所以不能在别的类中创建对象
回复 使用道具 举报
第一个你能确定编译可以通过?应该不会编译通过,构造方法私有化,在外部类是不能直接new该类的实例的,所以编译时是不能通过的。
第二个之所以能够编译和运行,那是你加了static,你在自己类中创建了对象,虽然构造函数私有化了,但是可以通过类名来调用你的静态成员,所以第二个就可以运行成功了
回复 使用道具 举报

class A

{

private A(){}


B b=new B();

}

class B

{

private B(){}

A a=new A();

}

class Test4

{

public static void main(String[] args)

{

A a=new A();//构造函数被私有化,所以不创建对象;

System.out.println(123);

}

}
第一个,因为你的构造函数是被私有化的,所以不能创建它的对象;
-------------------------------------------------------------------------------------------------------------
class A
{
private static A a = new A(); //作为类A的成员变量而存在;

private B b = B.getInstance();

private A(){} //也被私有化了不能创建对象;

public static A getInstance()//把A类的成员变量a返回了;并没有创建新的对象;只是把A类的成员返回了;下面的B也同理;

{
System.out.println("A被调用");
return a;
}

public void test()

{
System.out.println(b);
}
}


class B

{
private static B b = new B();

private A a = A.getInstance();

private B(){}

public static B getInstance()

{
System.out.println("B被调用");
return b;
}

public void test()

{
System.out.println(a);
}
}


class Test
{
public static void main(String[] args)
{

B b = B.getInstance();
b.test();

System.out.println("==========");
A a = A.getInstance();
a.test();
}

}
-------------------------------------------------------------------------------
首先要了解对象的创建过程;创建一个类的对象必须有构造函数;当然有的时候当不我希望创建过多的对象的时候;就需要把构造函数给私有化;在类的中把本类对象作为一个成员变量;然后再对外提供了公共的方法把对象提供出去;

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1 赞一个!

查看全部评分

回复 使用道具 举报
本帖最后由 王程 于 2012-8-6 09:09 编辑

我已经把第一个程序中的private去掉了,但还是会报错啊
回复 使用道具 举报
第一个程序要是出错,可以这样理解吗,你创建A  但是A还没生成,又去创建B对象,但是也没生成,又去创建A对象,所以一直就这样循环,估计是报内存溢出,jvm说你不给你干了。你要干到什么时候是个头啊。

第二个 不看了 睡觉,看样子和第一个一样,转了转去,我说哥们你哪来题目啊,你不晕啊,
回复 使用道具 举报
没有人知道第二个程序为什么是这个结果吗?
回复 使用道具 举报
没有人知道第二个程序为什么是这个结果吗?
回复 使用道具 举报
关于第一个问题,你在类的构造函数前加了private私有化后是不能在被其他类new 对象的,而去掉private后可以编译通过,
但是运行后应该是内存出错,创建了A的对象,A又创建B的对象,都互相调用了,下面是打印的结果at B.<init>(Test.java:17)
                                                                                                                                                                                at A.<init>(Test.java:12)
                                                                                                                                                                                at B.<init>(Test.java:17)
                                                                                                                                                                                at A.<init>(Test.java:12)
                                                                                                                                                                                。。。。。。。。
至于第二个问题,可将主函数的内容一句一句的运行,看打印结果
class Test
{
        public static void main(String[] args)
        {
                B b = B.getInstance();
                //b.test();
                //System.out.println("==========");
                //A a = A.getInstance();
                //a.test();
        }
}
将后面几句话注释后结果为
B被调用
A被调用
B被调用
class Test
{
        public static void main(String[] args)
        {
                //B b = B.getInstance();
                //b.test();
                //System.out.println("==========");
                A a = A.getInstance();
                //a.test();
        }
}
将前面几句话注释掉后打印结果为
A被调用
B被调用
A被调用
由此可见运行B b = B.getInstance()与A a = A.getInstance() A类与B类都会双方调用一次分别产生a的对象与b对象,
当内存中有了a与b对象后就不会产生新的对象了,因为对象是唯一的,所以A a = A.getInstance()只调用了不会产生新的对象了,我花了不少时间还没完全搞懂,这个问题挺复杂的。

点评

虽然不是答得很好,但是尽力了,就给分。嘿嘿  发表于 2012-8-11 14:13

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1 赞一个!

查看全部评分

回复 使用道具 举报
48.B b = B.getInstance();

49.b.test();

50.System.out.println("==========");

51.A a = A.getInstance();

52.a.test();



48行首先先调用B类中的静态函数,然后打印"B被调用"这个只是表面现象,其实在调用之前要加载B类,加载B类就要执行26.private A a = A.getInstance(); 所以就要先加载A类,加载A类就要执行05.private B b = B.getInstance(); 执行这个就要继续加载B类,把B类加载完之后就开始执行05.private B b = B.getInstance();就是第一个"B被调用",然后继续加载A类,把A类加载完之后就返回去开始执行26.private A a = A.getInstance(); 就是第一个"A被调用",然后继续返回去执行48.B b = B.getInstance(); 就是第三个"B被调用"。

然后执行49.b.test();因为a,b对象都是静态,所以执行完一次后内存中已经有了这2个静态对象,以后调用26.private A a = A.getInstance(); 05.private B b = B.getInstance(); 也只是平常的赋值而已,并不加载,也并不建立新对象,随意并不存在内存溢出或死循环,所以执行49.b.test();只是简单的调用36.public void test(),根据函数的功能打印的是内存中已经存在的A对象的地址,就是"A@35ce36"


然后执行50.System.out.println("==========");

然后执行51.A a = A.getInstance(); 只是普通的调用和赋值。调用打印函数11.System.out.println("A被调用"); 就是最后一个"A被调用",然后将内存中的静态对象a的地址传给主函数中的a。

52.a.test();这个无法解释
回复 使用道具 举报
思考了一天终于想出来了这是我思考的结果,希望可以加分哦。

48.B b = B.getInstance();
49.b.test();
50.System.out.println("==========");
51.A a = A.getInstance();
52.a.test();


主函数先执行48.B b = B.getInstance(); 首先先调用B类中的静态函数,然后打印"B被调用"这个只是表面现象。
①其实在调用之前要加载B类中的静态成员
②加载B类中的静态成员就要先建立静态的B类对象;即要执行这个语句private static B b = new B();
③但是在建立B类对象(new B();)之前,就要先执行26.private A a = A.getInstance();
④所以就要先加载A类中的静态成员,
⑤加载A类中的静态成员就要先建立静态的A类对象,即要先执行这个语句private static A a = new A();
⑥但是在建立A类对象之前(new A();),就要先执行05.private B b = B.getInstance();但是这时候B类静态对象还没建立,private static B b = new B(); 这句话只执行到private static B bnew B()没有执行,只是定义了一个B类型的变量,并未给变量b赋值,但是
public static B getInstance()
{
System.out.println("B被调用");
return b;
}
已经加载,执行05.private B b = B.getInstance();没有问题,开始打印第一个"B被调用"
此时正在建立中的静态的B类对象中成员静态B类型变量b的值为null,null赋给正在建立中的静态的A类对象中的成员b后,继续加载A类直到静态的A类对象建立
然后建立完A类静态对象a之后,此时private static A a = new A(); 已经执行完毕,然后开始执行反回去开始执行第④步,然后在开始执行第③步,此时执行26.private A a = A.getInstance();这时第一个"A被调用"被打印;此时有因为 new A();已经建立,所以付给a的值不是null,而是确确实实在内存中存在的静态的A类对象a的地址。然后静态的B类对象b建立,所以在静态的B类对象ba是确确实实在内存中存在的静态的A类对象a的地址。因为是静态所以b对象无法在重新建立(只有加载才会建立,但是静态只在初始化的时候加载一次,后面不再加载,因为内存已经有了),所以b对象中的a只能是确确实实在内存中存在的静态的A类对象a的地址。此时静态的A类对象中的b已经是null了,因为是静态,所以a对象无法在重新建立(只有加载才会建立,但是静态只在初始化的时候加载一次,后面不再加载,因为内存已经有了),所以a对象中的b只能为null了。
然后继续返回去执行第②步,然后执行第①步,最后调用B b = B.getInstance();这时第二个"B被调用"被打印。至此48.B b = B.getInstance(); 执行完毕。
开始执行49.b.test();就是打印静态b对象中的成员变量a,即为静态的A类对象a的地址。这时A@35ce36开始打印。
然后执行50.System.out.println("==========");
打印==========
然后执行51.A a = A.getInstance(); 只是普通的调用和赋值。调用打印函数11.System.out.println("A被调用"); 此时最后一个"A被调用"被打印,然后将内存中的静态对象a的地址传给主函数中的a
最后执行52.a.test();就是打印静态a对象中的成员变量b,前面已经说明为null,所以此时null被打印。

至于第一个报错是因为内存不断的new对象,因为每次加载都要new对象,new对象后又要重新加载(因为是非静态成员),重新加载又要new对象,所以内存就会溢出。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1 赞一个!

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马