在以下代码中创建了多少个对象?

时间:2009-11-10 11:44:55

标签: java

class ClassA {
    void h() { }
}

class ClassB extends ClassA{

    public static void main(String[] args){
       ClassA a = new ClassA();
       ClassB b = new ClassB();
  }
}

是的,每个人都认为它很简单,必须且肯定应该只创建两个对象。

但是在我读到“Think in Java,2nd Edition”之后,我认为它可能包含一些更有趣的内容。

在“Think in Java”中,有一句话:“当你创建一个派生类的对象时,它包含一个基类的子对象。这个子对象就像你创建了一个对象一样基类本身。“

第2版第278页。你也可以通过这个链接“http://www.codeguru.com/java/tij/tij0065.shtml”(标题为“初始化基类”的部分)看到它。

7 个答案:

答案 0 :(得分:13)

应为2. main函数是静态的,因此它不需要对象。

ClassA a是一个对象,ClassB b是一个对象

编辑:ClassB不包含两个对象,因为extends是is-a关系,而不是has-a关系。 (ta mik)

编辑:还有运行时系统创建的String[]对象,并且可能在该数组中放置任意数量的字符串对象。我有意无视这些,但承认它们可能存在。 (ta diveshpremdeep和Adam Goode)

最终编辑:为了确定创建了多少对象(通过程序,而不是运行时系统),您可以像命令一样在命令行上使用javap程序(如果测试) .java包含你的例子)

$ javac test.java
$ javap -c ClassB

输出:

Compiled from "test.java"
class ClassB extends ClassA{
ClassB();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method ClassA."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #2; //class ClassA
   3:   dup
   4:   invokespecial   #1; //Method ClassA."<init>":()V
   7:   astore_1
   8:   new #3; //class ClassB
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_2
   16:  return

}

正如您所看到的,只有两位字节码是new(它创建了我假设的对象)。一个用于课程ClassA,另一个用于课程ClassB。您可以注意到之后调用invokespecial命令来调用构造函数,以及如何从类ClassA的构造函数中调用类ClassB的构造函数,但是没有创建新对象在构造函数内部(它是默认的空构造函数)。

调用javap -c ClassA显示了一个同样无聊的构造函数,它调用Object的构造函数。

总结:它是new字节码,它在堆上创建对象,而不是仅仅填充invokespecial字节码分配的内存细节的new

答案 1 :(得分:3)

调用3个构造函数来创建2个对象。

答案 2 :(得分:3)

如果您指的是main方法块中的对象,那么2.这可以通过Java堆转储和以下代码扩展来验证:

class ClassA {
    void h() { }
}

class ClassB extends ClassA{
    public static void main(String[] args) throws Exception {
        System.in.read();
        main1(args);
        System.in.read();
    }

    public static void main1(String[] args){
       ClassA a = new ClassA();
       ClassB b = new ClassB();
  }
}

所以:

  1. 编译该类并运行它。程序执行在System.in.read()停止。
  2. 找出进程ID。
  3. 运行jmap -dump:format=b,file=snapshot1.jmap PROCESS_ID
  4. 点击控制台中的任意键继续执行。
  5. 运行jmap -dump:format=b,file=snapshot2.jmap PROCESS_ID
  6. 再次按任意键完成程序。
  7. 之后,您将获得两个可以比较的Java堆转储快照。

    我得到了以下值(使用Java 1.6.0_15,OSX):

    • snapshot1.jmap:17584个对象
    • snapshot1.jmap:17586个对象

    差异是2,也是创建对象的数量。

答案 3 :(得分:2)

创建了两个对象 - ClassA的实例和ClassB的实例。

调用新的ClassB()不会调用main()方法。默认构造函数,格式为

public ClassB() {}

将被使用。 ClassA也是如此。

答案 4 :(得分:1)

2

虽然B在创建A时会延伸AB中的构造函数会被执行,但最后只会创建A中的一个对象。

1 B + 1 ClassA a = new ClassA(); a = new ClassB(); = 2个对象

至于:

  

“当您创建派生类的对象时,它在其中包含基类的子对象。此子对象与您自己创建基类的对象相同。”

这意味着,以下是可能的:

B

也就是说,您可以将B的实例分配给A类型的引用,因为:

  

“...此子对象与您自己创建基类的对象相同”

这就是"is-a"关系的全部意义所在。你可以说“每个B都是一个A”,这是真的。

不能做的是调用A中定义的{{1}}

中未定义的方法

我希望有所帮助。

答案 5 :(得分:0)

默认构造函数,自动调用super()(父默认构造函数)构造函数。如果您创建一个Class对象,也会创建一个ClassA对象。

答案 6 :(得分:0)

这似乎是您的教授要求您在抽象层面上思考的问题之一。

话虽如此,我愿意打赌其创建的4个对象:

1) Class A

2) Class B

3) String[]

4) Since there is no such thing as a string array it is a char array.

当然这只是一个纯理论上的想法