2015-04-06 22 views
13

AFAIK,在Java中隐式构造函数总是为没有构造函数的类生成[1],[2]是否有没有任何构造函数的JVM字节码类?

但是在字节码中我找不到JVMS上的这种限制。

所以:

  • 是有效的根据JVMS定义一个类没有构造函数仅使用它的静态方法如下面的茉莉的hello world?

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

茉莉代码:

.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(); 
    } 
} 

,并预期它提供了编译错误cannot find symbol。如果我将构造函数添加到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_INTERFACE也不ACC_SYNTHETIC),这将是有效性的很好的理由。

+1

你有没有试过编写一个调用'new Main();'的Java对象? – RealSkeptic

+0

@RealSkeptic刚刚完成,并且出现错误:无法按预期找到符号。然后,如果我将构造函数添加到Jasmin,则“新的Main()”工作。 –

+0

那么,那么。那么你的问题或多或少是哲学的。它是否有效的Java?不,这是否有效Jasmin?是。它可以从Java程序中使用吗?是。它是否像人们期望的Java类一样工作?不是完全。那么,什么是“有效”? – RealSkeptic

回答

8

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

有时候,在Java编译器甚至为了创建这样的类来创建访问构造的内部类:

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

为了使这个私有的构造函数可以访问到外clasd,Java编译器增加内部类的包 - 私有构造函数,它将随机创建的无构造函数类的实例作为其单个参数。此实例始终为空,访问者仅调用无参数构造函数而不使用参数。但是因为constrors不能被命名,所以这是避免与其他构造函数发生碰撞的唯一方法。为了保持类文件最小化,不添加构造函数。

附注:总是可以创建没有构造函数的类的实例。这可以通过例如清除反序列化来实现。如果使用Jasmin来定义一个没有实现接口的构造函数的类,则可以手动创建一个类似于该类的字节流(如果它是序列化的)。你可以反序列化这个类并接收它的一个实例。

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

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

第一个是分配代表,第二个是构造函数的调用。 JVM的验证器在使用实例之前始终检查构造函数是否被调用,但从理论上讲,JVM完全有能力分离两者,如通过反序列化所证明的(或者如果序列化不是选项,则内部调用VM)。

+0

对于像我这样的其他新手来说,这会根据http://stackoverflow.com/a/2654699/895245生成三个类。名为'Foo $ 1.class'的第三个类没有构造函数,也不是接口。但它确实有[ACC_SYNTHETIC](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1)和[EnclosingMethod](https:// docs。 oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.7)属性集不像最小的Jasmin输出,这使得该参数与设置ACC_INTERFACE的apangin一样好。 –

+0

你的问题是在课堂上。接口和注释类型没有jls的构造函数。然而,甚至在接口中使用像ASM这样的代码生成工具来定义构造函数也是可能的,因为这些只是在字节代码级别上名为的方法。上面的例子生成了三个类,Foo和Foo $ Bar是两个独立的类。 –

+0

我的意思是,“javac生成它”参数只有在可以生成与Jasmin片段相同的字节码时才有效。接口有ACC_INTERFACE,这有ACC_SYNTHETIC,而jasmin代码没有。但是的确如此,这可能会被认为更接近,因为它会产生类。 –

6

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

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

+0

接口是一个合理的参数,尽管它们设置了[ACC_INTERFACE](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1),它可以只有在这种情况下才有效。 –

相关问题