求模糊矩阵合成运算的程序
JVM上运行着多种语言,从Java到Clojure和JRuby 。 它们都有不同的语法,但是它们都编译为相同的字节码真是太棒了。 JVM将它们全部结合在一起。 当然,它偏向Java,但是即使在Java中, 字节码中也发生了一些不可思议的事情。
最著名的把戏来自以下代码:
publicclassFoo{
staticclassBar{
privateBar(){}
}
publicstaticvoidmain(String...args){
newBar();
}
}
您能猜出Bar
类有多少个构造函数?
二。 是的,你读得很好。 对于JVM, Bar
类声明2个构造函数。 如果您不相信,请运行以下代码:
Class<Foo.Bar>clazz=Foo.Bar.class;
Constructor<?>[]constructors=clazz.getDeclaredConstructors();
System.out.println(constructors.length);
Arrays.stream(constructors).forEach(constructor->{
System.out.println("Constructor: "+constructor);
});
输出如下:
Constructor: private Foo$Bar() Constructor: Foo$Bar(Foo$1)
原因是有据可查的 。 字节码了解访问修饰符,但不了解嵌套类。 为了使Foo
类能够创建新的Bar
实例,Java编译器会生成一个具有默认包可见性的附加构造函数。
可以使用javap
工具确认。
javap-v out/production/synthetic/Foo \$ Bar.class
输出以下内容:
[...] { Foo$Bar(Foo$1); descriptor: (LFoo$1;)V flags: ACC_SYNTHETIC Code: stack=1, locals=2, args_size=2 0: aload_0 1: invokespecial #1 // Method "<init>":()V 4: return LineNumberTable: line 2: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LFoo$Bar; 0 5 1 x0 LFoo$1; } [...]
注意ACC_SYNTHETIC
标志。 转到JVM规范会产生以下信息:
ACC_SYNTHETIC
标志指示此方法是由编译器生成的,并且不会出现在源代码中,除非它是第4.7.8节中指定的方法之一。
—类文件格式
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6
从理论上讲,应该可以调用此生成的构造函数-尽管不可能提供Foo $ 1的实例,但我们将其放在一边。 但是IDE似乎无法发现这第二个无参数的构造函数。 我没有在Java语言规范中找到任何参考,但是合成类和成员不能直接访问,只能通过反射访问。
在这一点上,人们可能想知道为什么所有关于合成标志的大惊小怪。 它是Java引入的,用于解决嵌套类访问的问题。 但是其他JVM语言使用它来实现其规范。 例如,Kotlin使用合成方法来访问伴随对象:
classBaz(){
companionobject{
valBAZ="baz"
}
}
在.class
文件上执行javap
返回以下输出(出于可读性目的而被删节):
{ public static final Baz$Companion Companion; descriptor: LBaz$Companion; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public Baz(); [...] public static final java.lang.String access$getBAZ$cp(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC Code: stack=1, locals=0, args_size=0 0: getstatic #22 // Field BAZ:Ljava/lang/String; 3: areturn LineNumberTable: line 1: 0 RuntimeInvisibleAnnotations: 0: #15() } [...]
注意access$getBAZ$cp()
静态方法吗? 那就是应该从Java调用的方法的名称:
public class FromJava {
public static void main(String... args) {
Baz.Companion.getBAZ();
}
}
结论
尽管在JVM开发人员的日常工作中不需要了解synthetic
标志,但了解反射API返回的一些结果可能会有所帮助。
求模糊矩阵合成运算的程序