3

我经历了这个:

子类是否继承私有字段?

但我还是一头雾水……

我说的是只继承而不是访问。我知道他们在课外看不到。

但是子类的对象是否拥有超类中这些私有成员的副本?

例如...

class Base {
    private int i;
}

class Derived extends Base {
    int j;
}

现在,

Base b = new Base();
Derived d = new Derived();

int 的大小为 4

现在,

b 的大小是否为 4,d 的大小是否为 8

或者

d 的大小也只有 4 吗?

当然,当我说 b 和 d 而不是引用时,我说的是堆上的对象。

更新:我刚刚在 SCJP 的 Kathy Sierra 和 Bert 书中读到……它说他们不是继承的……我发布了这个更新,因为仍然有很多人说是的……

4

5 回答 5

6

是的,子类的实例将具有父类的私有字段的副本。

然而,它们对子类是不可见的,因此访问它们的唯一方法是通过父类的方法。

于 2013-01-03T16:17:47.417 回答
4

由于 JVM 规范,class文件是这样构建的:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

super_class领域包括:

`对于一个类,super_class 项的值要么必须为零,要么必须是 constant_pool 表的有效索引。如果 super_class 项的值非零,则该索引处的 constant_pool 条目必须是一个 CONSTANT_Class_info (§4.4.1) 结构,表示该类文件定义的类的直接超类。直接超类及其任何超类都不能在其 ClassFile 结构的 access_flags 项中设置 ACC_FINAL 标志。

如果 super_class 项的值为零,则该类文件必须表示类 Object,即唯一没有直接超类的类或接口。

对于接口,super_class 项的值必须始终是 constant_pool 表的有效索引。该索引处的 constant_pool 条目必须是表示类 Object 的 CONSTANT_Class_info 结构。

更有趣的是fields[]部分:

Each value in the fields table must be a field_info (§4.5) structure giving a complete description of a field in this class or interface. The fields table includes only those fields that are declared by this class or interface. It does not include items representing fields that are inherited from superclasses or superinterfaces.

所以编译的类不包含继承的字段。另一方面,当创建对象时,private超级字段在内存中。为什么?让我们想象一下这个例子:

 classs A {
      int a;

      A(int a) {
          this.a = a;
      }

      void methodA() {
          System.out.println("A is: " + a);
      }
 }

 classs B extends A {
      int b;

      B(int b) {
          super(10);
          this.b = b;
      }

      void methodB() {
          super.methodA();
          System.out.println("B is: " + b);
      }
 }

输出应该是:A is: 10 \n B is ...

于 2013-01-03T16:28:59.650 回答
1

两者都不 - 对象的大小包括一些开销。但是 Derived 会比 Base 占用更多的空间。

运行一个快速测试,看起来 Base 的大小约为 20 个字节,而 Derived 的大小约为 28 个字节。请注意,这些结果在不同的 JVM 上可能会有所不同。

public static void main(String[] args) throws InterruptedException {
    int size = 500000;
    double mem;

    mem = Runtime.getRuntime().freeMemory();
    Base[] base = new Base[size];
    for (int i = 0; i < size; i++) {
        base[i] = new Base();
    }
    System.out.println((mem - Runtime.getRuntime().freeMemory()) / size);

    System.gc();
    Thread.sleep(100);
    System.gc();

    mem = Runtime.getRuntime().freeMemory();
    Derived[] derived = new Derived[size];
    for (int i = 0; i < size; i++) {
        derived[i] = new Derived();
    }
    System.out.println((mem - Runtime.getRuntime().freeMemory()) / size);
}
于 2013-01-03T16:24:03.213 回答
0

是的,派生类将具有基类的私有字段。如果 Derived 定义了公共 getter 和 setter,您将能够使用 Derived 类中的该字段。

b 的大小是 4,d 的大小是 8 还是 d 的大小也只有 4?

它是 Java,而不是 C。您不能轻易估计堆中对象的大小。它会大于 4 或 8 个字节。

于 2013-01-03T16:20:55.843 回答
0

不,私有字段被设计为可在其声明的类上访问。在类(包括派生类)之外可以访问它们的唯一方法是通过公共方法。

如果要使派生类继承基类中的成员,请使用“受保护”而不是私有。代码应该是:

class Base {
  protected int i;
}

class Derived extends Base {
  int j;
}

“受保护”访问修饰符允许继承成员并为其分配自己的访问修饰符。

于 2013-10-14T10:27:36.900 回答