为什么有一个静态嵌套类导致第二个构造函数在不在源代码中时被添加?

ani*_*ban 4 java singleton design-patterns

我试图用反射打破BillPaugh Singleton解决方案,我能够做到但我可以在访问BillPaughSingleTon解决方案时看到两个构造函数.为什么这样 ?另外通过反复试验发现HelperClass中的行是造成这种情况的.为什么呢?

BillPaughClass

package creational.BillPaugh;

public class SingleTonBillPaugh
{
  private SingleTonBillPaugh instance;

  public static SingleTonBillPaugh getInstance()
  {
    return SingleTonHelper.instance;
  }

  private SingleTonBillPaugh()
  {
    System.out.println(Thread.currentThread().getName() + " instance is going to be created");
  }

  static class SingleTonHelper
  {
    private static final SingleTonBillPaugh instance = new SingleTonBillPaugh(); //if we remove this line, multiple constructor will not be there. But this line is needed for singleton. 
  }
}
Run Code Online (Sandbox Code Playgroud)

使用Reflection破坏SingleTon.

package creational.BillPaugh;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class BreakBillPaughUsingReflection
{
  public static void main(String[] args)
  {

    SingleTonBillPaugh singletonInstance1 = SingleTonBillPaugh.getInstance();
    System.out.println("singletonInstance1 " + singletonInstance1);

    SingleTonBillPaugh singletonInstance2;
    Constructor[] constructors = SingleTonBillPaugh.class.getDeclaredConstructors();

    for (Constructor construct : constructors)
    {
      construct.setAccessible(true);
      try
      {
        singletonInstance2 = (SingleTonBillPaugh) construct.newInstance();
        System.out.println("singletonInstance2 " + singletonInstance2);
      }
      catch (InstantiationException | IllegalAccessException | IllegalArgumentException
        | InvocationTargetException e)
      {
        e.printStackTrace();
      }
    }// end for

    Constructor[] constructors2 = NormalClass.class.getDeclaredConstructors();

  }
}
Run Code Online (Sandbox Code Playgroud)

两个构造函数的证明.

在此输入图像描述

T.J*_*der 5

这是我的看法:

因为SingleTonBillPaugh源代码中的构造函数是private,SingleTonBillPaugh$SingleTonHelper无法访问它,所以编译器生成的合成构造函数SingleTonBillPaugh$SingleTonHelper 可以访问.这就是合成方法和构造函数的用途:提供一种访问包含类的私有数据的方法.

我想到的更大的问题是为什么合成构造函数接受一个参数,为什么参数的类型SingleTonBillPaugh$1?(你的截图让它看起来像是一个SingleTonBillPaugh实例,但在我的测试中它实际上是一个SingleTonBillPaugh$1实例 - 也就是说,除了SingleTonBillPaugh和之外还有第三个类在这里生成SingleTonBillPaugh$SingleTonHelper).

我对这个问题的回答是:因为否则,将会有两个构造函数区别于这样一个事实,即一个是合成的,可以访问SingleTonBillPaugh$SingleTonHelper而另一个不是.Java要求签名与此更加不同,因此它生成一个类的唯一原因是区分合成构造函数和非合成构造函数.

我们可以看到我们确实有一个SingleTonBillPaugh$1类,如果我们javap -p -c SingleTonBillPaugh\$1它,我们得到:

class SingleTonBillPaugh$1 {
}
Run Code Online (Sandbox Code Playgroud)

没有比这更简单,这表明它确实纯粹作为合成构造函数的参数类型.我们可以通过查看SingleTonBillPaugh$SingleTonHelper使用的字节码进一步确认javap -p -c SingleTonBillPaugh\$SingleTonHelper:

class SingleTonBillPaugh$SingleTonHelper {
  private static final SingleTonBillPaugh instance;

  SingleTonBillPaugh$SingleTonHelper();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return

  static SingleTonBillPaugh access$000();
    Code:
       0: getstatic     #1                  // Field instance:LSingleTonBillPaugh;
       3: areturn

  static {};
    Code:
       0: new           #3                  // class SingleTonBillPaugh
       3: dup
       4: aconst_null
       5: invokespecial #4                  // Method SingleTonBillPaugh."<init>":(LSingleTonBillPaugh$1;)V
       8: putstatic     #1                  // Field instance:LSingleTonBillPaugh;
      11: return
}
Run Code Online (Sandbox Code Playgroud)

注意它是如何(接近结尾)调用构造函数的单参数版本(传入null).

实际上,似乎总是这样做 - 在构造函数的参数列表的末尾添加一个新参数.如果我将私有构造函数更改为接受String并更新SingleTonHelper以传递它"",那么合成构造函数最终会成为SingleTonBillPaugh(String, SingleTonBillPaugh$1).


你的问题如下:

我在一个没有参数构造函数的情况下保留了一个sysout,当内部类调用外部类的参数构造函数(可能是合成构造函数)时,打印相同的sysout.为什么这样?是因为内部合成构造函数调用我提供的pvt构造函数?

确切地说,合成构造函数调用私有构造函数.像这样的时代,很高兴进入字节码:

这是我的副本SingleTonBillPaugh.java:

public class SingleTonBillPaugh
{
    public static SingleTonBillPaugh getInstance()
    {
        return SingleTonHelper.instance;
    }

    private SingleTonBillPaugh()
    {
        System.out.println(Thread.currentThread().getName() + " instance is going to be created");
    }

    static class SingleTonHelper
    {
        private static final SingleTonBillPaugh instance = new SingleTonBillPaugh();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们编译它,然后使用javap -p -c SingleTonBillPaugh,我们得到:

public class SingleTonBillPaugh {
  public static SingleTonBillPaugh getInstance();
    Code:
       0: invokestatic  #2                  // Method SingleTonBillPaugh$SingleTonHelper.access$000:()LSingleTonBillPaugh;
       3: areturn

  private SingleTonBillPaugh();
    Code:
       0: aload_0
       1: invokespecial #3                  // Method java/lang/Object."<init>":()V
       4: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: new           #5                  // class java/lang/StringBuilder
      10: dup
      11: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      14: invokestatic  #7                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      17: invokevirtual #8                  // Method java/lang/Thread.getName:()Ljava/lang/String;
      20: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: ldc           #10                 // String  instance is going to be created
      25: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      34: return

  SingleTonBillPaugh(SingleTonBillPaugh$1);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method "<init>":()V
       4: return
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到,SingleTonBillPaugh(SingleTonBillPaugh$1)用源代码形式编写的构造函数woudl基本上是:

SingleTonBillPaugh(SingleTonBillPaugh$1 unused) {
    this();
}
Run Code Online (Sandbox Code Playgroud)