一个对象拥有所有类成员的副本(静态成员除外),那么对象是否也拥有内部类的副本?

时间:2021-03-08 11:40:44

标签: java oop static inner-classes

一个对象有一个所有类成员的副本(静态成员除外),那么一个对象也有一个内部类的副本吗?

下面是我的代码:

    package com.example;

        
    class outerClass{
        
         class innnerClass{
            
            }
    }


    public class A{

      public void main(String[] args){
        outerClass obj1 = new outerClass();

        outerClass.innerClass obj2 = obj1.new innerClass();
      }
    }

我怀疑 obj2 是“innerClass 的一个实例,它是 externalClass 的成员”还是“innerClass 的一个实例,它是 obj1 对象的成员”?

2 个答案:

答案 0 :(得分:3)

当您查看类似 the tutorial 的内容时,您可能会发现这样的句子

<块引用>

当从同一个类蓝图创建多个对象时,它们每个都有自己不同的实例变量副本。

注意它说的是变量,而不是成员。但即使这样也过于简单化了。它实际上意味着,对象可以具有不同的实例变量值。变量的定义(包括注解及其值)是不变的,将它们复制到对象会导致无意义的相同副本。

同样,方法的声明,包括它们的代码,都是不变的,因此,复制它们也没有意义。但是在实例上操作的方法的有效行为可能会有所不同,因为它们可能对不同的值进行操作。

这同样适用于内部类。它们的定义是不变的,不需要复制它们。但是非静态内部类实例与可能具有不同字段值的外部实例相关联,这可能导致内部类方法的不同行为。

在您的示例中,外部类中没有实例字段,内部类中没有方法,具体取决于外部对象的状态,因此使用哪个外部实例来实例化内部类对象没有区别(除了某些对垃圾收集影响较小)。

将状态添加到外部实例并将依赖行为添加到内部实例时,情况会发生变化:

public class Outer {
    boolean even;
  
    Outer(boolean even) {
      this.even = even;
    }
  
    class Inner {
        @Override
        public String toString() {
          return even? "Even": "Odd";
        }
    }
  
    public static void main(String[] args) {
        Inner i1 = new Outer(true).new Inner();
        Inner i2 = new Outer(false).new Inner();
  
        System.out.println(i1);
        System.out.println(i2);
    }
}

这里,两个 Inner 实例由于用于创建它们的外部对象而表现出不同的行为。尽管如此,这并不意味着类定义被复制。

从 JDK 16 开始,您将能够向内部类添加静态成员。在这一点上,内部类不是每个外部实例都复制变得很重要,换句话说,内部类的静态成员在运行时中只存在一次,就像顶级或嵌套静态类的静态成员一样。

例如我们将能够将示例更改为

public class Outer {
    boolean even;
  
    Outer(boolean even) {
      this.even = even;
    }
  
    class Inner {
        static {
            System.out.println("Inner initialized");
        }
        @Override
        public String toString() {
          return even? "Even": "Odd";
        }
    }
  
    public static void main(String[] args) {
        Inner i1 = new Outer(true).new Inner();
        Inner i2 = new Outer(false).new Inner();
  
        System.out.println(i1);
        System.out.println(i2);
    }
}

这将适用于 JDK 16 和更新版本,并且只打印一次“内部初始化”,而不是两次。

答案 1 :(得分:0)

是的。外部类的每个实例都将拥有自己的非静态内部类的副本。

我们知道这一点是因为给定类的每个实例都会收到该类的所有非静态成员的自己的副本,如 documentation 所述:

<块引用>

当从同一个类蓝图创建多个对象时,它们每个都有自己不同的实例变量副本。

由于非静态内部类实际上是该类的实例成员,因此每次创建其外部类的新实例时,都会创建它的唯一副本。换句话说,非静态内部类就像非静态方法或字段一样被复制。

此外,您不能在非静态内部类 (as explained in this StackOverflow answer) 中声明任何静态成员,考虑到非静态内部类在没有其外部类的实例的情况下无法存在,这是有道理的。由于非静态内部类的唯一副本是为其外部类的每个实例创建的,因此它不能包含任何静态成员。