【问题标题】:In Java how many constructor can we create in one class?在 Java 中,我们可以在一个类中创建多少个构造函数?
【发布时间】:2019-05-15 20:38:55
【问题描述】:

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

【问题讨论】:

  • 您可以根据需要创建任意数量
  • 一个类中方法的数量限制为65535。所以我猜构造函数的数量也限制为65535。
  • 呸,这些答案是错误的,现在这个问题,在没有上下文的情况下可能毫无意义,但完全合理,问题已经结束。
  • @AndyTurner 65535 位。我声称它可能是 65527。另一方面,这个非常相似的问题有 28 票stackoverflow.com/q/4342072/4725

标签: java


【解决方案1】:

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

参考 - JVMS 4.1 The ClassFile Structure

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

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

如果您发现自己编写的构造函数数量过多(主观的!),您或许应该考虑替代方法,例如 Builder Pattern

【讨论】:

    【解决方案2】:

    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 几乎无法使用。关闭调试符号并有点耐心,我设法用 Eclipse 编译了一个具有 65526 个构造函数的类。声明 65528 构造函数会产生关于常量池条目过多的错误消息,并且声明 65527 构造函数会揭示 Eclipse 中的一个错误,生成一个损坏的类文件,声明零常量池条目(如前所述,该数字存储为 count 加一,因此编译器供应商必须记住限制不是 65535 而是 65534)。

    【讨论】:

    【解决方案3】:

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

    【讨论】:

    • "Here is an example" 您的示例无法编译,因为您有两个签名相同的构造函数。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      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】:

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

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

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

        【讨论】:

          【解决方案7】:

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

          更多信息:Oracle Docs

          【讨论】:

          • 以前的答案已经确定这不是很准确,并且链接的 JVM 规范没有明确说明构造函数。
          【解决方案8】:

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

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-10
          • 1970-01-01
          • 2020-01-09
          • 2018-07-14
          • 2015-06-21
          • 1970-01-01
          • 2018-07-18
          相关资源
          最近更新 更多