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

标签 java constructor jvm bytecode

据我所知,在 Java 中,隐式构造函数总是为没有构造函数的类生成 [1] , [2] .

但是在字节码中我找不到关于 JVMS 的限制。 .

所以:

  • 根据 JVMS 定义一个没有构造函数的类,仅使用其静态方法是否有效,如以下 jasmin hello world 所示?

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

Jasmine 代码:

.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 <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()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();
    }
}

正如预期的那样,它给出了一个编译错误找不到符号。如果我将构造函数添加到 jasmin,则 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() { }
  }
}

为了使外部类可以访问此私有(private)构造函数,Java 编译器向内部类添加了一个包私有(private)构造函数,该构造函数将随机创建的无构造函数类的实例作为其单个参数。此实例始终为 null,访问器仅调用无参数构造函数而不使用参数。但是因为构造函数不能命名,这是避免与其他构造函数冲突的唯一方法。为了保持类文件最小,没有添加构造函数。

旁注:总是可以在没有构造函数的情况下创建类的实例。这可以通过例如滥用反序列化来实现。如果您使用 Jasmin 定义一个没有实现 Serializable 接口(interface)的构造函数的类,您可以手动创建一个类似于序列化类的字节流。然后,您可以反序列化此类并接收它的一个实例。

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

NEW java/lang/Object
INVOKESPECIAL java/lang/Object <init> ()V

第一个是分配,第二个是构造函数的调用。 JVM 的验证程序总是在使用实例之前检查构造函数是否被调用,但理论上,JVM 完全能够分离两者,反序列化(或内部调用 VM,如果序列化不是一个选项)证明了这一点。

关于java - 没有任何构造函数的 JVM 字节码类是否有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29478139/

相关文章:

lucene - 是否可以在一个 JVM 中运行多个 map 任务?

java - Java中位置的无效内存访问

java - 策略模式中的通用方法重写

java - Swing 中的 "Atomic rendering"

java - 避免由于相同参数类型导致构造函数冲突的最佳实践?

java - 关于 Berkeley Data Structures 免费在线类(class) (CS61B) 关于 Java 中静态变量的教程问题

java - 什么是初始化 block ?

java代码执行在没有断点和正常运行的调试中产生不同的结果。 ExecutorService 坏了吗?

java - 我无法让 OnClickListeners 与包含两个不同 fragment 的 Android Activity 一起使用

java - 你怎么能得到JSON路径?