有没有任何构造函数的JVM字节码类是有效的吗? [英] Is it valid to have a JVM bytecode class without any constructor?

查看:113
本文介绍了有没有任何构造函数的JVM字节码类是有效的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

AFAIK,在Java隐式构造函数中,始终为没有构造函数的类生成 [1] [2]



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



因此:





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;
ldcHello World!
invokevirtual java / io / PrintStream / println(Ljava / lang / String;)V
return
.end方法

没有构造函数:

  .method public< init>()V 
aload_0
invokenonvirtual java / lang / Object /< init>()V
return
.end方法



使用 c> $



我已经检查了 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
主版本:46
标志:ACC_PUBLIC,ACC_SUPER
常量池:
#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。 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
标志:ACC_PUBLIC,ACC_STATIC
代码:
stack = 2,locals = 1,args_size = 1
0:getstatic#13 //字段java / lang / System.out:Ljava / io / PrintStream;
3:ldc#9 //字符串Hello World!
5:invokevirtual#20 //方法java / io / PrintStream.println:(Ljava / lang / String;)V
8:return
}
SourceFile:Main。 j



如果任何人都可以使用javac生成(特别是 ACC_INTERFACE ACC_SYNTHETIC )。

解决方案

这是合法的。



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

/ p>

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

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



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



在Java中,构造函数调用和对象分配是两个独立的步骤。这甚至暴露在创建实例的字节代码。 新的Object()代表两个例子

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

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

AFAIK, in Java implicit constructors are always generated for a class without constructors [1], [2].

But in bytecode I could not find such restriction on the JVMS.

So:

  • is it valid according to the JVMS to define a class without constructor only to use its static methods as in the following jasmin hello world?

  • does it have any further consequences besides not being able to create instances of it? I won't be able to use invokespecial to initialize instances, which renders new useless according to https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.2.4 (can't use uninitialized object).

Jasmin code:

.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

that is, without a constructor:

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

?

Running with java Main gives the expected output Hello World!.

I have checked the javap -v output and unlike Java, jasmin did not generate the default constructor.

I have also tried to call new Main(); anyway to see what happens with:

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

and as expected it gives a compilation error cannot find symbol. If I add the constructor to the jasmin then TestMain works.

Output of javap -v for completeness:

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"

If anyone can generate that with javac (in particular no ACC_INTERFACE nor ACC_SYNTHETIC) that would be a good argument for validity.

解决方案

It is legal. The JVMS does not say otherwise.

Sometimes, the Java compiler does even create such classes in order to create accessor constructors for inner classes:

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

In order to make this private constructor accessible to the outer clasd, the Java compiler adds a package-private constructor to the inner class that takes an instance of the randomly created constructor-less class as its single argument. This instance is always null and the accessor only invokes the parameterless constructor without using the argument. But because constrors cannot be named, this is the only way to avoid collissions with other constructors. In order to keep the class file minimal, no constructor is added.

On a side note: It is always possible to create instances of classes without constructors. This can be achieved by, for example, absusing deserialization. If you use Jasmin to define a class without a constructor that implements the Serializable interface, you can create a byte stream manually that resembles the class if it was serialized. You can than deserialize this class and receive an instance of it.

In Java, a constructor call an an object allocation are two seperate steps. This is even exposed by the byte code of creating an instance. Something like new Object() is represented by two instuctions

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

the first being the allocation, the second being the constructor's invocation. The JVM's verifier always checks that a constructor is called before the instance is used but in theory, the JVM is perfectly capable of detaching both, as proven by deserialization (or internal calls into the VM, if serialization is not an option).

这篇关于有没有任何构造函数的JVM字节码类是有效的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆