1、关于<init>与<clinit>
At the level of the Java Virtual Machine, every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name <init>. This name is supplied by a compiler. Because the name <init> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Instance initialization methods may be invoked only within the Java Virtual Machine by the invokespecial instruction (§invokespecial), and they may be invoked only on uninitialized class instances. An instance initialization method takes on the access permissions (JLS §6.6) of the constructor from which it was derived.
A class or interface has at most one class or interface initialization method and is initialized (§5.5) by invoking that method. The initialization method of a class or interface has the special name <clinit>, takes no arguments, and is void (§4.3.3).
<clinit> 在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行
Other methods named <clinit> in a class file are of no consequence. They are not class or interface initialization methods. They cannot be invoked by any Java Virtual Machine instruction and are never invoked by the Java Virtual Machine itself.
In a class file whose version number is 51.0 or above, the method must additionally have its ACC_STATIC flag (§4.6) set in order to be the class or interface initialization method.
This requirement is new in Java SE 7. In a class file whose version number is 50.0 or below, a method named <clinit> that is void and takes no arguments is considered the class or interface initialization method regardless of the setting of its ACC_STATIC flag.
The name <clinit> is supplied by a compiler. Because the name <clinit> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java Virtual Machine; they are never invoked directly from any Java Virtual Machine instruction, but are invoked only indirectly as part of the class initialization process.
接口中有<clinit>方法,其owner,也就是所属的symbol为java.lang.Object
2、关于invoke与invokeExact
A method is signature polymorphic if and only if all of the following conditions hold:
- It is declared in the java.lang.invoke.MethodHandle class.
- It has a single formal parameter of type Object[].
- It has a return type of Object.
- It has the ACC_VARARGS and ACC_NATIVE flags set.
In Java SE 7, the only signature polymorphic methods are the invoke and invokeExact methods of the class java.lang.invoke.MethodHandle.
invoke()与invokeExact()方法的定义在MethodHandle类中,如下:
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable; public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
The Java Virtual Machine gives special treatment to signature polymorphic methods in the invokevirtual instruction (§invokevirtual), in order to effect invocation of a method handle. A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation (§5.4.3.5), with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. See the java.lang.invoke package in the Java SE platform API for more information.
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MethodHandleTest { public static void main(String[] args) throws Throwable { /* * Lookup相当于MethodHandle工厂类,通过findxxx方法可以得到相应的 * MethodHandle,还可以配合反射API创建MethodHandle,对应的方法 * 有unreflect、unreflectSpecial等 */ MethodHandles.Lookup lookup = MethodHandles.lookup(); /* * 调用MethodType的静态方法创建MethodType实例,有三种创建方式: * (1)methodType及其重载方法:需要指定返回值类型以及0到多个参数 * (2)genericMethodType:需要指定参数的个数,类型都为Object * (3)fromMethodDescriptorString:通过方法描述来创建 * 创建好MethodType对象后,还可以对其进行修改,MethodType类中提 * 供了一系列的修改方法,比如: * changeParameterType、changeReturnType等 */ MethodType methodType = MethodType.methodType(int.class, int.class); MethodHandle handle = lookup.findStatic(MethodHandleTest.class, "doubleVal", methodType); /* * (1)invokeExact:调用此方法与直接调用底层方法一样,需要做到参数类型精确匹配 * * (2)invoke:nvoke方法允许更加松散的调用方式。它会尝试在调用的时候进行返回值 * 和参数类型的转换工作。这是通过MethodHandle类的asType方法来完成的, * asType方法的作用是把当前方法句柄适配到新的MethodType上面,并产生一个新的方法句柄。 * 当方法句柄在调用时的类型与其声明的类型完全一致的时候,调用invoke方法等于调用 * invokeExact方法;否则,invoke方法会先调用asType方法来尝试适配到调用时 * 的类型。如果适配成功,则可以继续调用。否则会抛出相关的异常。这种灵活的适配机制, * 使invoke方法成为在绝大多数情况下都应该使用的方法句柄调用方式。 * * (3)invokeWithArguments:直接通过方法参数来调用。 * 其实现是先通过genericMethodType方法得到MethodType,再通过MethodHandle的 * asType转换后得到一个新的MethodHandle,最后通过新MethodHandle的 * invokeExact方法来完成调用。 */ Integer temp = (Integer) handle.invoke(2); System.out.println(temp); // 4 } public static int doubleVal(int val) { return val * 2; } }
关于invoke()与invokeExact()方法:
The types of its parameters are the static types of the actual argument expressions.
An argument expression which is the null literal
null
(§3.10.7) is treated as having the static typeVoid
.The result type is determined as follows:
If the method invocation expression is an expression statement, the method is
void
.Otherwise, if the method invocation expression is the operand of a cast expression (§15.16), the return type is the erasure (§4.6) of the type of the cast expression.
Otherwise, the return type is the method's declared return type,
Object
.
再看一个例子,如下:
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MethodHandleTest { public MethodHandle getHandler_print() { MethodHandle mh = null; MethodType mt = MethodType.methodType(void.class); MethodHandles.Lookup lk = MethodHandles.lookup(); try { mh = lk.findVirtual(MethodHandleTest.class, "print", mt); } catch (Throwable e) { e.printStackTrace(); } return mh; } public MethodHandle getHandler_substring() { MethodHandle mh = null; MethodType mt = MethodType.methodType(String.class, int.class, int.class); MethodHandles.Lookup lk = MethodHandles.lookup(); try { mh = lk.findVirtual(String.class, "substring", mt); } catch (Throwable e) { e.printStackTrace(); } return mh; } public void test_substring() throws Throwable { MethodHandle mh = new MethodHandleTest().getHandler_substring(); String str = "hello world"; Object result1 = mh.invoke(str, 1, 3); Object result2 = (String) mh.invokeExact(str, 1, 3); // 方法执行时报错,因为方法返回的类型为Object,与声明中为String不符合 // Object result3 = mh.invokeExact(str, 1, 3); // 第二个参数类型为Integer,与声明中为int不符合,则类型适配不符合,系统报错 // Object result4 = (String)mh.invokeExact(str, new Integer(1), 3); System.out.println("result 1:" + result1); System.out.println("result 1:" + result2); } public void test_print() throws Throwable { MethodHandleTest mht = new MethodHandleTest(); MethodHandle mh = mht.getHandler_print(); int result5 = (int) mh.invoke(mht); Object result6 = mh.invoke(mht); System.out.println("result 5:" + result5); System.out.println("result 6:" + result6); } public static void main(String[] args) throws Throwable { MethodHandleTest m = new MethodHandleTest(); m.test_substring(); m.test_print(); } public void print() { System.out.println("invoke print"); } }
class Test{ // e.g 1 // a variable arity parameter,Otherwise,it is a fixed arity method // b 的英文叫法为 a variable arity parameter public int m1(final int a[][],int ...b)[][]{ return a; } // e.g 2 如下m2方法不能共存 public static final void m2(List list){ } public void m2(List<String> list) { } }
3、Miranda Method
In early VM's there was a bug -- the VM didn't walk the interfaces
of a class looking for a method, they only walked the superclass
chain. This meant that abstract methods defined only in interfaces
were not being found. To fix this bug, a counter-bug was introduced
in the compiler -- the so-called Miranda methods. If a class
does not provide a definition for an abstract method in one of
its interfaces then the compiler inserts one in the class artificially.
That way the VM didn't have to bother looking at the interfaces.
This is a problem. Miranda methods are not part of the specification.
But they continue to be inserted so that old VM's can run new code.
Someday, when the old VM's are gone, perhaps classes can be compiled
without Miranda methods. Towards this end, the compiler has a
flag, -nomiranda, which can turn off the creation of these methods.
Eventually that behavior should become the default.
Why are they called Miranda methods? Well the sentence "If the
class is not able to provide a method, then one will be provided
by the compiler" is very similar to the sentence "If you cannot
afford an attorney, one will be provided by the court," -- one
of the so-called "Miranda" rights in the United States.
参考文章:
(1)https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandle.html