在Java中,我们可以在一个类中创建多少个构造函数?

时间:2018-12-05 05:14:51

标签: java

在Java中,我们可以在一个类中创建多少个构造函数。

8 个答案:

答案 0 :(得分:19)

严格来说,JVM类文件格式将一个类的方法(包括所有构造函数)限制为少于65536。而且,根据汤姆·霍顿(Tom Hawtin)的说法,有效限制为65527。每个方法签名都在常量池中占用一个插槽。 。由于65535池条目中的某些条目(不可避免地)被其他事物消耗,因此格式良好的类文件不可能使用所有可能的方法/构造函数ID。

参考-JVMS 4.1 The ClassFile Structure

但是,如果您以正常方式编写明智的Java代码,则不会遇到该限制。

您应该有多少 ?这取决于类的用例。经常有多个“便利”构造函数重载,并使用this(...)来实现它们以链接到“主”构造函数,这是很好的选择。 (但是,您可以转到顶部。N个不同参数的N个可能组合(重载)。)

如果发现编写的构造函数过多(主观!),则应考虑使用Builder Pattern之类的替代方案。

答案 1 :(得分:12)

the maximum number of lambdasthe maximum of nested method invocations一样,由于正式指定的类文件格式或由于编译器的限制或错误,我们必须在正式的Java语言规范和技术限制之间进行区分。

通常,语言规范并未对构造函数的数量进行任何限制。因此,只有实际的限制,即类声明必须以字节码格式可以表示。

构造函数被编译为特殊方法(名为<init>),因此在类文件中,它们与普通方法共享一个表,该表限于65535个条目。我们可以通过不声明任何常规方法来最大程度地发挥作用。此外,由于每个构造函数都必须具有不同的签名,因此每个构造函数都需要在常量池中使用其自己的类型签名字符串,该字符串只能限制为65534个条目。

常量池还可以用于其他目的,例如保存此类的声明,超类和Code属性的名称(具有构造函数时需要)以及超类的链接信息。 '构造函数,我们必须调用它,因此这是类文件方面的限制因素。

因此所需的最小常量池条目为

  1. 超级类名称(修改的UTF8条目)
  2. 超级类(类型为Class,指的是1。)
  3. 此类名称(修改的UTF8条目)
  4. 此类(类型为Class,指3)。
  5. 构造函数的“方法”名称<init>(修改的UTF8条目)
  6. 引用5.的名称和类型条目以及超级构造函数签名(可以与我们的构造函数签名之一共享)
  7. 引用2和6的方法条目(用于超级构造函数调用)
  8. 属性名称Code(已修改的UTF8条目)

鉴于这些必需的条目和65534个条目的限制(大小加一个存储为无符号的两个字节),我们得到了65526个构造函数的类文件限制,实际上,我可以使用ASM库生成一个有效的类文件有那么多构造函数,而没有更多。

实际上,如果您将类命名为java.lang.Object,则可以得到更多,因为在这种特殊情况下,没有要声明的超类,也没有要调用的超构造函数。确定自己,您要调用最大限制的实际限制...

如上所述,编译器实现是第三个限制。使用Java编译器时,必须确保它不会生成调试信息(在javac的情况下,请使用-g:none),并且没有其他可能占用常量池条目的可选属性。但是,对于JDK11的javac,当您开始定义许多构造函数时,性能将大大下降。我得到了以下编译时间:

 1000 constructors:   1 second
 2000 constructors:   2 seconds
 5000 constructors:  10 seconds
10000 constructors:   1 minute
15000 constructors:   2 minutes
20000 constructors:   4 minutes
30000 constructors:  10 minutes
40000 constructors:  20 minutes
50000 constructors:  between 25 minutes and ½ hour
65526 constructors:  between 45 minutes and 1 hour

因此javac最终设法使类文件限制达到最大,但是我们甚至可以在此之前考虑实际限制。

Eclipse编译器似乎可以更好地处理此类源文件,但是,由于构造函数数量过多,IDE几乎无法使用。在关闭调试符号并稍加耐心的情况下,我设法使用65565构造函数编译了一个类。声明65528构造函数会产生有关太多常量池条目的错误消息,并且声明65527构造函数会揭示Eclipse中的错误,从而生成一个损坏的类文件,声明零个常量池条目(如前所述,数字存储为 count加一/ em>,因此编译器供应商必须记住该限制不是65535,而是65534。)

答案 2 :(得分:6)

Java支持的构造函数重载(当java类包含多个构造函数时,称为重载构造函数)。一个类可以具有多个构造函数,只要它们的签名(参数)不同即可。因此可以定义许多构造函数根据需要。没有限制。 这是一个示例:-

class Demo {
    private String name;
    private String city;
    private Double salary;

    public Demo() {
    }

    public Demo(String name) {
        this.name = name;
    }
    public Demo(Double salary) {
        this.city = city;
    }
    public Demo(String name,String city) {
        this.name = name;
        this.city = city;
    }
}

答案 3 :(得分:2)

您可以在一个类中有65535个构造函数(根据Oracle文档)。但是重要的是要牢记这一点。我们仅通过构造器重载(https://beginnersbook.com/2013/05/constructor-overloading/)实现此目的。您可以创建许多构造函数,但是具有不同的签名。

答案 4 :(得分:0)

您可以在一个类中拥有任意数量的构造函数。.JAVA对一个类可以具有的构造函数数量没有任何限制。.构造函数可以是参数化的也可以是默认的。

默认构造函数:默认构造函数没有参数,用于初始化具有相同数据的每个对象。

参数化的构造函数:参数化的构造函数具有一个或多个参数,用于用不同的数据初始化每个对象。

答案 5 :(得分:0)

tl; dr

对于具有合理功能的课程,您首先会遇到技术和非技术方面的其他问题。对于无用的类,由类文件格式的常量池施加的技术限制是:

  • 65526代表您选择的课程。
  • 65527表示名为Code的类(每个类加载器一个)。
  • 65530用于名为java.lang.Object的类(每个VM一个)。

详细信息

在我发布答案之前,问题已经结束。因此,这里除了@Holger已经介绍的内容之外。

JVM规范的相关部分是Java SE 11版本中的4.1. The ClassFile Structure

u2             constant_pool_count;
cp_info        constant_pool[constant_pool_count-1];

请注意-1。条目从1开始编号。0表示:

  • 没有超类的类(有趣!)
  • 没有名称的形式参数
  • 没有版本信息的模块
  • 没有版本信息的模块依赖性

如果将该类称为Code,则该字符串常量将使用必需的Code属性名进行重复数据删除(请参见@Holger的答案)。如果要从默认软件包之外访问javac,则需要一个非常旧的版本。

如果通过字节码写入没有作弊,则可以通过抛出null(字节码中的常量)而不是调用super()或类似方法来删除几个条目。我不记得构造函数字节码验证的确切细节-它们一定无法在不调用this()super()的情况下正常终止。

javac运行缓慢(O(n ^ 2)ish?)是正常降级的一个很好的例子。 :)

游戏时间

通过编译具有不同数量的构造函数的小类,您可以轻松地自己看到这一点。

public class Min1 {
   public Min1() {
   }
   /* Followed by (int a), (byte a), (int a, byte b), etc. */
}

在没有调试信息的情况下进行编译(人们是否还会不经意地分发带有调试信息的类文件?)。

javac -g:none Min1.java

列出具有良好旧javap的内容。

javap -verbose Min1

应该给你类似的东西

Classfile /Users/tackline/code/scratch/minimal_class/Min1.class
  Last modified Dec 5, 2018; size 119 bytes
  MD5 checksum c1a6b7c31c286165e01cc4ff240e7718
public class Min1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#7          // java/lang/Object."<init>":()V
   #2 = Class              #8             // Min1
   #3 = Class              #9             // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = NameAndType        #4:#5          // "<init>":()V
   #8 = Utf8               Min1
   #9 = Utf8               java/lang/Object
{
  public Min1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
}

答案 6 :(得分:-1)

您可以在一个类中创建65535个构造函数

更多信息:Oracle Docs

答案 7 :(得分:-2)

一个类可以拥有的构造函数数量没有限制。