【问题标题】:An object has a copy of all the class members(except the static ones), so does an object has a copy of an inner class also?一个对象具有所有类成员的副本(静态成员除外),那么对象是否也具有内部类的副本?
【发布时间】:2021-06-06 07:07:47
【问题描述】:

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

下面是我的代码:

    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 是outerClass 的成员”的一个实例,还是“innerClass 是obj1 对象的成员”的一个实例?

【问题讨论】:

  • 您的代码中有错字。声明中的三个 'n' 和尝试使用 innnerClass 时的两个
  • “对象有一个所有班级成员的副本”——你从哪里得到这样的智慧?

标签: java oop static inner-classes


【解决方案1】:

当你查看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 及更高版本,并且只打印一次“内部初始化”,而不是两次。

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 我建议再次仔细阅读引用的文本。请注意,它没有说“成员”。此外,请在链接的问题中注明this answer
    • @Holger 你是对的。我不知道为什么我认为它说“成员”......显然我没有很好地校对我的答案。无论如何,你的答案比我的要准确得多。
    猜你喜欢
    • 2011-07-17
    • 2018-07-13
    • 1970-01-01
    • 2017-01-24
    • 1970-01-01
    • 2020-09-18
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多