シーン
今日、jvm仮想マシンの初期化フェーズを調べたところ、初期化をトリガーする次の5つの状況があることがわかりました。
初期化フェーズでは、仮想マシンの仕様では、クラスをすぐに「初期化」する必要があるのは5つのケースのみであると厳密に規定されています(そして、ロード、検証、および準備は、当然、この前に開始する必要があります):
1)new、getstatic、putstaticに遭遇しますまたはこれらの4バイトコード命令をinvokestaticするときに、クラスが初期化されていない場合は、最初にその初期化をトリガーする必要があります。これらの4つの命令を生成するための最も一般的なJavaコードシナリオは次のとおりです。newキーワードを使用してオブジェクトをインスタンス化する場合、クラスの静的フィールドを読み取るか設定します(finalによって変更され、結果はコンパイル時に静的プールに入れられます。フィールドを除く)、およびクラスの静的メソッドを呼び出す場合。
2)java.lang.reflectパッケージメソッドを使用してクラスへのリフレクション呼び出しを行う場合、クラスが初期化されていない場合は、最初にその初期化をトリガーする必要があります。
3)クラスを初期化するときに、その親クラスが初期化されていないことがわかった場合は、最初にその親クラスの初期化をトリガーする必要があります。
4)仮想マシンの起動時に、実行するメインクラス(main()メソッドを含むクラス)を指定する必要があり、仮想マシンはこのメインクラスを最初に初期化します。
5)当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
なかでも第5条には触れておらずMethodHandle
、使用シーンを補っています。
MethodHandle
JDK6より前は、Javaリフレクションを使用して動的メソッド呼び出しを実装していました。mybatis、springなど、ほとんどのフレームワークはリフレクションをより多く使用します。JDK7では、java.lang.invoke.MethodHandle(メソッドハンドル)が追加されました。これは
“现代化反射”
。と呼ばれます。実際、リフレクションとjava.lang.invoke.MethodHandleは、どちらも間接的にメソッドを呼び出す方法但java.lang.invoke.MethodHandle比反射更简洁,用反射功能会写一大堆冗余代码
です。
公式APIによる説明:
メソッドハンドルは、基になるメソッド、コンストラクター、フィールド、または同様の低レベル操作への型指定された直接実行可能な参照であり、引数または戻り値のオプションの変換があります。実際、メソッドハンドル、へのポインターを取得することは可能です。同様の方法。
下面看一个例子,使用方法句柄(MethodHandle)调用toString()方法:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
/**
* Created by hfl on 2020/12/11
*/
public class MHTestDemo {
public String toString(String s) {
return "hello," + s + "MethodHandle";
}
public static void main(String[] args) {
MHTestDemo mhTest = new MHTestDemo();
MethodHandle mh = getToStringMH(); //获取方法句柄
try {
// 1.调用方法:
String result = (String) mh.invokeExact(mhTest, "ssssss"); //根据方法句柄调用方法----注意返回值必须强转
System.out.println(result);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 2.or like this:
try {
MethodHandle methodHandle2 = mh.bindTo(mhTest);
String toString2 = (String) methodHandle2.invokeWithArguments("sssss");
System.out.println(toString2);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 得到当前Class的不同表示方法,最后一个最好。一般我们在静态上下文用SLF4J得到logger用。
System.out.println(MHTestDemo.class);
System.out.println(mhTest.getClass());
System.out.println(MethodHandles.lookup().lookupClass()); // like getClass()
}
/**
* 获取方法句柄
* @return
*/
public static MethodHandle getToStringMH() {
//获取方法类型 参数为:1.返回值类型,2方法中参数类型
MethodType mt = MethodType.methodType(String.class, String.class);
MethodHandle mh = null;
try {
//查找方法句柄
mh = MethodHandles.lookup().findVirtual(MHTestDemo.class, "toString", mt);
} catch (NoSuchMethodException | IllegalAccessException e) {
e.printStackTrace();
}
return mh;
}
}
演算結果:
hello、ssssssMethodHandle
hello、sssssMethodHandle
クラスcom.gupao.gpjvm.ch01.MHTestDemo
クラスcom.gupao.gpjvm.ch01.MHTestDemo
クラスcom.gupao.gpjvm.ch01.MHTestDemo
この記事の終わり。