拥有没有任何构造函数的JVM字节码类是否有效?

AFAIK,Java中的隐式构造函数总是为没有构造函数的类生成[1] , [2] 。

但在字节码中,我无法在JVMS上找到这样的限制。

所以:

  • 根据JVMS来定义一个没有构造函数的类只有在下面的jasmin hello世界中使用它的静态方法是有效的吗?

  • 除了无法创建它的实例之外还有其他任何后果吗? 根据https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.2.4 ,我将无法使用invokespecial来初始化实例,这会使new无用。 (不能使用未初始化的对象)。

Jasmin代码:

 .class public Main .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 getstatic java/lang/System/out Ljava/io/PrintStream; ldc "Hello World!" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V return .end method 

也就是说, 没有构造函数:

 .method public ()V aload_0 invokenonvirtual java/lang/Object/()V return .end method 

使用java Main运行会给出预期的输出Hello World!

我检查了javap -v输出,与Java不同, jasmin没有生成默认构造函数。

我也尝试过调用new Main(); 无论如何看看会发生什么:

 public class TestMain { public static void main(String[] args) { Main m = new Main(); } } 

并且正如预期的那样,它给出了编译错误, cannot find symbol 。 如果我将构造函数添加到TestMain那么TestMain可以工作。

为完整性输出javap -v

 public class Main minor version: 0 major version: 46 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Utf8 Main.j #2 = Class #17 // Main #3 = NameAndType #21:#23 // out:Ljava/io/PrintStream; #4 = Utf8 ([Ljava/lang/String;)V #5 = Utf8 java/lang/Object #6 = Class #5 // java/lang/Object #7 = Utf8 Hello World! #8 = Class #16 // java/io/PrintStream #9 = String #7 // Hello World! #10 = Class #19 // java/lang/System #11 = Utf8 Code #12 = Utf8 main #13 = Fieldref #10.#3 // java/lang/System.out:Ljava/io/PrintStream; #14 = Utf8 SourceFile #15 = NameAndType #18:#22 // println:(Ljava/lang/String;)V #16 = Utf8 java/io/PrintStream #17 = Utf8 Main #18 = Utf8 println #19 = Utf8 java/lang/System #20 = Methodref #8.#15 // java/io/PrintStream.println:(Ljava/lang/String;)V #21 = Utf8 out #22 = Utf8 (Ljava/lang/String;)V #23 = Utf8 Ljava/io/PrintStream; { public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #9 // String Hello World! 5: invokevirtual #20 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } SourceFile: "Main.j" 

如果任何人都可以使用javac(特别是没有ACC_INTERFACEACC_SYNTHETIC )生成那个有效的参数。

这是合法的。 JVMS没有另外说明。

有时,Java编译器甚至会创建这样的类,以便为内部类创建访问器构造函数

 class Foo { { new Bar(); } class Bar() { private Bar() { } } } 

为了使外部clasd可以访问此私有构造函数,Java编译器将一个包私有构造函数添加到内部类,该构造函数将随机创建的无构造函数的类的实例作为其单个参数。 此实例始终为null,访问者仅在不使用参数的情况下调用无参数构造函数。 但由于无法命名constrors,这是避免与其他构造函数进行冲突的唯一方法。 为了使类文件保持最小,不添加构造函数。

旁注:始终可以创建没有构造函数的类的实例。 这可以通过例如禁止反序列化来实现。 如果使用Jasmin来定义没有实现Serializable接口的构造函数的类,则可以手动创建类似于类的字节流(如果已序列化)。 您可以反序列化此类并接收它的实例。

在Java中,构造函数调用对象分配是两个单独的步骤。 这甚至通过创建实例的字节代码暴露。 new Object()类的东西由两个指令代表

 NEW java/lang/Object INVOKESPECIAL java/lang/Object  ()V 

第一个是分配,第二个是构造函数的调用。 JVM的validation程序始终检查在使用实例之前是否调用了构造函数,但理论上,JVM完全能够分离两者,正如反序列化(或内部调用VM,如果序列化不是一个选项)所certificate的那样。

您已经自己回答了这个问题:根据JVMS,没有构造函数的类绝对有效。 您不能在纯Java中编写这样的类,但可以使用字节码生成来构造它。

想想接口:从JVM的角度来看,它们也是没有构造函数的类。 它们也可以有静态成员(甚至可以从命令行调用接口的main方法)。