字节码功能在Java语言中不可用

字节码功能在Java语言中不可用,java,jvm,bytecode,Java,Jvm,Bytecode,在Java字节码中,目前(Java 6)有哪些事情是在Java语言中做不到的 我知道两者都是图灵完备的,所以将“可以做”理解为“可以做得更快/更好,或者只是以不同的方式” 我正在考虑额外的字节码,如invokedynamic,它不能使用Java生成,除非特定的字节码是为将来的版本生成的。据我所知,Java 6支持的字节码中没有Java源代码无法访问的主要功能。其主要原因显然是Java字节码的设计考虑到了Java语言 但是,有些功能不是由现代Java编译器生成的: 委员会: 这是一个可以在类上设

在Java字节码中,目前(Java 6)有哪些事情是在Java语言中做不到的

我知道两者都是图灵完备的,所以将“可以做”理解为“可以做得更快/更好,或者只是以不同的方式”


我正在考虑额外的字节码,如
invokedynamic
,它不能使用Java生成,除非特定的字节码是为将来的版本生成的。

据我所知,Java 6支持的字节码中没有Java源代码无法访问的主要功能。其主要原因显然是Java字节码的设计考虑到了Java语言

但是,有些功能不是由现代Java编译器生成的:

  • 委员会:

    这是一个可以在类上设置的标志,用于指定如何为此类处理
    invokespecial
    字节码的特定角大小写。它是由所有现代Java编译器设置的(如果我没记错的话,“现代”是>=Java1.1),只有古代Java编译器生成的类文件没有设置。此标志仅因向后兼容原因而存在。注意,从Java7U51开始,由于安全原因,ACC_SUPER被完全忽略

  • jsr
    /
    ret
    字节码

    这些字节码用于实现子例程(主要用于实现
    最终
    块)。他们是。他们不赞成的原因是,他们使静态验证复杂化了很多,但没有获得很大的收益(即使用的代码几乎总是可以用很少的开销通过正常跳转重新实现)

  • 在一个类中有两个返回类型不同的方法

    Java语言规范不允许同一类中的两个方法仅在返回类型(即相同的名称、相同的参数列表等)上不同。然而,JVM规范没有这样的限制,因此一个类文件可以包含两个这样的方法,使用普通Java编译器无法生成这样的类文件。这里有一个很好的例子/解释


可以使用字节码而不是普通Java代码生成代码,这些代码可以在没有编译器的情况下加载和运行。许多系统都有JRE而不是JDK,如果您想动态生成代码,那么生成字节码而不是Java代码可能会更好(如果不是更容易的话),在使用之前必须先编译它。

可能对第7A节感兴趣,虽然它是关于字节码陷阱而不是字节码功能

这里有一些可以用Java字节码实现但不能用Java源代码实现的功能:

  • 从方法中抛出选中的异常,而不声明该方法抛出异常。选中和未选中的异常仅由Java编译器检查,而不是由JVM检查。因此,例如Scala可以从方法中抛出检查过的异常,而不声明它们。尽管Java泛型有一个称为的变通方法

  • 一个类中有两个返回类型不同的方法,如中所述:Java语言规范不允许在同一个类中有两个返回类型不同的方法(即相同的名称、相同的参数列表等)。然而,JVM规范没有这样的限制,因此一个类文件可以包含两个这样的方法,使用普通Java编译器无法生成这样的类文件。这里有一个很好的例子/解释


在Java语言中,构造函数中的第一条语句必须是对超类构造函数的调用。字节码没有这个限制,相反,规则是在访问成员之前,必须为对象调用超类构造函数或同一类中的另一个构造函数。这应该允许更多的自由,例如:

  • 创建另一个对象的实例,将其存储在局部变量(或堆栈)中,并将其作为参数传递给超类构造函数,同时仍保留该变量中的引用以供其他使用
  • 根据条件调用不同的其他构造函数。这应该是可能的:
我没有测试这些,如果我错了,请纠正我。

  • GOTO
    可与标签一起使用,以创建您自己的控制结构(而不是
    for
    while
    等)
  • 您可以在方法中重写
    这个
    局部变量
  • 将这两者结合起来,您可以创建尾部调用优化字节码(我在中这样做)

与此相关的一点是,如果使用debug进行编译,您可以获得方法的参数名(

),在使用Java字节码相当长一段时间并对这一问题进行了一些额外的研究之后,以下是我的发现摘要:

在调用超级构造函数或辅助构造函数之前在构造函数中执行代码

在Java编程语言(JPL)中,构造函数的第一条语句必须是对超级构造函数或同一类的另一个构造函数的调用。这对于Java字节码(JBC)来说并非如此。在字节码中,在构造函数之前执行任何代码都是绝对合法的,只要:

  • 在此代码块之后的某个时间调用另一个兼容的构造函数
  • 此调用不在条件语句中
  • 在此构造函数调用之前,不会读取构造实例的任何字段,也不会调用其任何方法。这意味着下一项
在调用超级构造函数或辅助构造函数之前设置实例字段

如前所述,在调用另一个构造函数之前设置一个实例的字段值是完全合法的。甚至存在一个遗留的黑客,使得它能够在6之前的Java版本中利用这个“功能”:

class Foo {
  public String s;
  public Foo() {
    System.out.println(s);
  }
}

class Bar extends Foo {
  public Bar() {
    this(s = "Hello World!");
  }
  private Bar(String helper) {
    super();
  }
}
class Foo {
  Foo() { }
  Foo(Void v) { }
}

class Bar() {
  if(System.currentTimeMillis() % 2 == 0) {
    super();
  } else {
    super(null);
  }
}
class Foo {
  final int bar;
  Foo() { } // bar == 0
  Foo(Void v) { // bar == 2
    bar = 1;
    bar = 2;
  }
}
record Foo(Object bar) { }
class Foo {
  void baz() { System.out.println("Foo"); }
}

class Bar extends Foo {
  @Override
  void baz() { System.out.println("Bar"); }
}

class Qux extends Bar {
  @Override
  void baz() { System.out.println("Qux"); }
}
class Foo {
  void foo() {
    bar();
  }
  void bar() { }
}

class Bar extends Foo {
  @Override void bar() {
    throw new RuntimeException();
  }
}
class Foo {
  static {
    return;
  }
}
try {
  throw new Exception();
} catch (Exception e) {
  <goto on exception>
  throw Exception();
}
class Foo {
  void m(Foo f) {
    f.super.toString(); // calls Object::toString
  }
  public String toString() {
    return "foo";
  }
}
class Foo {
  class Bar { 
    void bar(Bar bar) {
      Foo foo = bar.Foo.this;
    }
  }
}
Method method = ...
assertTrue(method.getParameterTypes() != method.getGenericParameterTypes());

Field field = ...
assertTrue(field.getFieldType() == String.class);
assertTrue(field.getGenericFieldType() == Integer.class);
class Foo {
  class Bar {
    Bar(@TypeAnnotation Foo Foo.this) { }
  }
  Foo() { } // Must not declare a receiver type
}