当前位置: 首页 > 面试题库 >

嵌套类的构造函数出现问题

柯书
2023-03-14
问题内容

这个问题是关于Java的有趣行为的:在某些情况下,它为嵌套类生成了其他(不是默认的)构造函数。

这个问题也与Java使用该奇怪的构造函数生成的奇怪的匿名类有关。

考虑以下代码:

package a;

import java.lang.reflect.Constructor;

public class TestNested {    
    class A {    
        A() {
        }

        A(int a) {
        }
    }

    public static void main(String[] args) {
        Class<A> aClass = A.class;
        for (Constructor c : aClass.getDeclaredConstructors()) {
            System.out.println(c);
        }

    }
}

这将打印:

a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)

好。接下来,让构造函数A(int a)私有:

    private A(int a) {
    }

再次运行程序。接收:

a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)

还可以 但是现在,让我们main()以这种方式修改方法(添加类A创建的新实例):

public static void main(String[] args) {
    Class<A> aClass = A.class;
    for (Constructor c : aClass.getDeclaredConstructors()) {
        System.out.println(c);
    }

    A a = new TestNested().new A(123);  // new line of code
}

然后输入变为:

a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)
a.TestNested$A(a.TestNested,int,a.TestNested$1)

这是什么: a.TestNested $ A(a.TestNested,int,a.TestNested $ 1) <<< -– ??

好的,让我们再次将构造函数A(int a)包设为本地:

    A(int a) {
    }

再次重新运行程序(我们 不删除 带有A创建实例的行!),输出如第一次:

a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)

问题:

1) 如何解释?

2) 第三个奇怪的构造函数是什么?

更新: 调查显示如下。

1)让我们尝试使用其他类的反射来调用这个奇怪的构造函数。我们将无法执行此操作,因为没有任何方法可以创建该奇怪TestNested$1类的实例。

2)好的。让我们来解决问题。让我们将TestNested此类静态字段添加到类中:

public static Object object = new Object() {
    public void print() {
        System.out.println("sss");
    }
};

好?好的,现在我们可以从另一个类中调用第三个奇怪的构造函数:

    TestNested tn = new TestNested();
    TestNested.A a = (TestNested.A)TestNested.A.class.getDeclaredConstructors()[2].newInstance(tn, 123, TestNested.object);

抱歉,但我绝对不明白。

UPDATE-2: 进一步的问题是:

3) 为什么Java使用特殊的匿名内部类作为该第三个合成构造函数的参数类型?为什么不只Object键入具有特殊名称的构造函数?

4) 哪些Java可以将已定义的匿名内部类用于这些目的?这不是某种违反安全性的行为吗?


问题答案:

首先,感谢您提出这个有趣的问题。我很感兴趣,以至于我忍不住要看一下字节码。这是字节码TestNested

Compiled from "TestNested.java"
  public class a.TestNested {
    public a.TestNested();
      Code:
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

    public static void main(java.lang.String[]);
      Code:
         0: ldc_w         #2                  // class a/TestNested$A
         3: astore_1      
         4: aload_1       
         5: invokevirtual #3                  // Method java/lang/Class.getDeclaredConstructors:()[Ljava/lang/reflect/Constructor;
         8: astore_2      
         9: aload_2       
        10: arraylength   
        11: istore_3      
        12: iconst_0      
        13: istore        4
        15: iload         4
        17: iload_3       
        18: if_icmpge     41
        21: aload_2       
        22: iload         4
        24: aaload        
        25: astore        5
        27: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: aload         5
        32: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        35: iinc          4, 1
        38: goto          15
        41: new           #2                  // class a/TestNested$A
        44: dup           
        45: new           #6                  // class a/TestNested
        48: dup           
        49: invokespecial #7                  // Method "<init>":()V
        52: dup           
        53: invokevirtual #8                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
        56: pop           
        57: bipush        123
        59: aconst_null   
        60: invokespecial #9                  // Method a/TestNested$A."<init>":(La/TestNested;ILa/TestNested$1;)V
        63: astore_2      
        64: return        
  }

如您所见,构造函数a.TestNested$A(a.TestNested,int,a.TestNested$1)是从您的main方法中调用的。此外,null作为a.TestNested$1参数的值传递。

因此,让我们看一下神秘的匿名类a.TestNested$1

Compiled from "TestNested.java"
class a.TestNested$1 {
}

奇怪-我本来希望这堂课能真正做些事。要了解它,让我们看一下a.TestNested$A:类a.TestNested $ A {final
a.TestNested this $ 0;

  a.TestNested$A(a.TestNested);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #2                  // Field this$0:La/TestNested;
       5: aload_0       
       6: invokespecial #3                  // Method java/lang/Object."<init>":()V
       9: return

  private a.TestNested$A(a.TestNested, int);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #2                  // Field this$0:La/TestNested;
       5: aload_0       
       6: invokespecial #3                  // Method java/lang/Object."<init>":()V
       9: return

  a.TestNested$A(a.TestNested, int, a.TestNested$1);
    Code:
       0: aload_0       
       1: aload_1       
       2: iload_2       
       3: invokespecial #1                  // Method "<init>":(La/TestNested;I)V
       6: return        
}

查看包可见的构造函数a.TestNested$A(a.TestNested, int, a.TestNested$1),我们可以看到第三个参数被忽略了。

现在我们可以解释构造函数和匿名内部类。为了避免对私有构造函数的可见性限制,需要额外的构造函数。这个额外的构造函数只是委托给私有构造函数。但是,它不能具有与私有构造函数完全相同的签名。因此,添加了匿名内部类以提供唯一的签名,而不会与其他可能的重载构造函数发生冲突,例如带有署名(int,int)或的构造函数(int,Object)。由于仅需要此匿名内部类来创建唯一的签名,因此不需要实例化它,也不需要具有内容。



 类似资料:
  • 我不确定我是否过度工程化了,但我正在考虑创建一个枚举,其中包含一个枚举列表作为它的值,从中我可以得到它的值。 我无法确定枚举的类型,以便正确地将值数组筛选到正确的枚举。例如,我可以用轻松地获得US枚举。我遇到的困难是从该数组中获得正确的值。我尝试比较名称,

  • 问题内容: 这更多的是 困惑而 不是问题。我有以下代码: 即使基类构造函数为,在这里也允许对1 * 的调用。如果我们在同一包中将这些类编写为单独的类: 编译器正确地在2 *处给出了错误,因为基类构造函数不可见。 当两个类在一个类中都声明为静态时,为什么编译器没有在我的第一种情况下引发错误? 问题答案: 如果成员或构造函数被声明为私有,则仅当访问发生在包含成员或构造函数的声明的顶级类(第7.6节)的

  • 语言 go 写了一个嵌套数组 想要构造出一个能够添加一个结构体的函数 这是运行的结果 想要知道 nil为什么没变 为什么不是80

  • 我试图模拟一个有嵌套类的类。那个嵌套类有一个构造函数参数。当我试图使用mockito而不是mockito测试时,实际的方法正在执行。 我在外部类上做了@injectmock,在内部类上做了@Mock。 我像往常一样尝试使用@Mock调用类,但是实际的方法eth1()正在被访问。 需要模拟内部ClassB方法,而不是访问真实方法。 作为mockito的初学者,我试图澄清这一点。但在访问void方法等

  • C++ 类 & 对象 类的构造函数 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 下面的实例有助于更好地理解构造函数的概念:#include <iostream> using namespace std; class Line { public: v

  • 我有一个REST回调服务,它必须使用以下格式的XML: 埃德·蒂亚