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


这个问题是关于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(int a)包设为本地:

    A(int a) {

}

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

a.TestNested$A(a.TestNested)

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


如何解释?

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


调查显示如下。

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);

抱歉,但我绝对不明白。


进一步的问题是:

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

哪些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)。由于仅需要此匿名内部类来创建唯一的签名,因此不需要实例化它,也不需要具有内容。

以上是 嵌套类的构造函数出现问题 的全部内容, 来源链接: utcz.com/qa/397873.html

回到顶部