Java SEの基盤の統合(VII):リフレクション

私は最初の反射の概念は、「Javaのプログラミングのアイデア」に見られるの話を聞いた、私はこれについて少し青い話の本、そして自己Javaは、石を感じることで川を渡って自分自身を導くために何の前任者は、簡単にオンラインエントリーの本を発見しました、でもすべての推奨「Javaプログラミングのアイデアは」、それは今で表示され、この本は、開発者の特定の開発経験のためのより適切な初心者のために本当に適していません(理由は、おそらく2016年の始まりでした、私は比較的低い知識を識別するだけの能力が多分ありました)。少しプルは離れて、引き戻す、引き戻します。

1 RTTIと反射

時間内の「Javaプログラミングのアイデア」に反映述べたように、筆者は、Java RTTI、すなわちRTTI実行時型インフォ(実行時型情報)が、実際にはRTTIと考えるが、それはC ++ RTTI、Javaの固有のものですそれは多分ちょうどアカウントにそれのためのC ++の読者の理由の比較的多数を取って、この概念ではありません。

1.1 RTTI

RTTIはならば、実行時に動的に各オブジェクトのタイプを決定する、例えば、しばしばdynamic_castをするために使用されるプログラムは、構文の任意の他のタイプに動的に実行時に対象とすることができることを可能にするC ++言語のコアメカニズムであるが、エラーは、それがチューブによって所有されていない、発生します。

1.2を反映

Java言語は、ウィキペディアにこの説明を提供するユニークなコンセプトの反射が、コンピュータサイエンスの一般的な概念ではありません。

コンピュータサイエンス反映手段のコンピュータプログラム実行時間(実行時間)が、自身の状態や動作を検出し、修正する能力をアクセスすることができます。プログラムが実行中の「観察」することができ、その動作を変更するには反映されている場合比喩的に言えば、。

用語「反射」との「内省」タイプのイントロスペクション)との関係に注意してください。(または「自己検査」)内省のみ(と呼ばれる実行時情報に機構自体を指すメタデータ検出)のみならず、プログラム自体の反射は、実行時情報を検出することができるように、さらに別の必要な手順に従ってプログラムステータスや設定情報の変更。

ビューの上記の説明から、反射およびRTTIは本当に好き、私はRTTIは、実行時に動的に変換タイプでなく、オブジェクトにだけでなく、を含むべきである反射の広い範囲のサブセットを反映したものであると考えることを好みます振る舞い(メソッド)、アクセス、変更などの操作を行うための状態(フィールド)。

反射のみ「Javaプログラミングのアイデア」の理解を通じて、著者の深遠な意味を理解していない可能性がある場合ので、RTTIについて話した理由は、反射がC ++ RTTIと同等だと思います。(私はそのようなものですしていました)

2 Javaリフレクション

Javaで反射に特有の、説明することができます:Javaリフレクションメカニズム我々がそうでそのインターフェイス、親クラス、フィールド、メソッド、および含めて、実行時に任意のメタ情報にクラスにアクセスできるようにします。我々は反射の使用を容易にすることができるように、JDKもAPIを提供し、これらのAPIはjava.lang.reflectのパッケージであり、これらのAPIは、その上のメソッド、フィールド、アレイとが含まれます。など春、MyBatiesなどと言っても過言で、Javaの世界では、「彼らが望むものは何でも」本当におなじみの反射、多くのフレームワークは、反射技術に大きく依存し、いいえ、春は、その後、ランタイム、メソッド、フィールド上の注釈付きの情報でクラスを取得しません対応する治療を行います。

2.1クラスオブジェクト

そのクラスのオブジェクト・クラス・オブジェクト、すべてのクラスは、仮想マシンの仕様を参照クラスに対応する方式エリアに向けた情報の種類が提供され、そのクラスのオブジェクト参照を持っていないので、どんな種類の仮想マシン実装の意志そのようなクラスのオブジェクト参照がある、でも基本的な種類があります。例えば:

public class Main {

    public static void main(String[] args) {
        //直接使用类型名.class的方式获取
        Class<?> intClass = int.class;
        Class<?> userClass = User.class;
        User user = new User();
        //使用对象引用.getClass()的方式获取
        Class<?> userClass2 = user.getClass();
        //对于基本类型,名字就是类型名,例如int类型的name就是int
        System.out.println(intClass.getName());
        //对于类来说,名字是全限定类名
        System.out.println(userClass.getName());
        System.out.println(userClass2.getName());

        //simpleName是将包的信息略掉,只有类名
        System.out.println(userClass.getSimpleName());
    }
}
复制代码

上記のコードは、2つの方法でクラスオブジェクトを取得し、一方は直接の.classタイプ名、オブジェクト参照は、呼のgetClass()メソッドを使用することで、基本的なタイプは、最初の方法を使用することができ、参照方法の2種類が使用されてもよいです、参照した後、クラスオブジェクトは、「彼らは、やりたい」ことができます取得します!それはいくつかの方法は、すなわち、どのような方法があるのクラスに取得することができ、どのような方法署名などは、次のコードは、リフレクションの簡単な使用を示しています。

2.2ペアは、フィールド上で動作します

APIクラスのフィールドは、動作の様々なフィールドを有するオブジェクトがフィールドのクラスオブジェクトを取得するために必要な取得、フィールド、請求getDeclaredFields()メソッドの配列を得るためにgetDeclaredFields()またはれるGetFields()を呼び出すと、プライベートフィールドを含むことができ、そしてれるGetFields ()が含まれていない、あなたはまた、あなたがないNoSuchFieldExceptionが例外をスロー見つけることができない場合は、のgetField(文字列)またはgetDeclaredField(String)メソッドを呼び出すことにより、フィールドの名前を指定するために取得することができます。次のコードは、フィールドを操作する方法を示しています。

public class Main {

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
        User user = new User();
        Class<?> userClass = user.getClass();

        //获取字段
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("field Type is " + field.getType().getName() + " ---- field name is " + field.getName());
        }
        System.out.println("before set field value : " + user.getId());
        Field field = userClass.getDeclaredField("id");
        field.setAccessible(true);
        field.set(user, 314L);
        System.out.println("after set filed value : " + user.getId());
    }
}
复制代码

その主張の種類すべてのフィールドを含むフィールドの配列を取得するためのコードは、Fieldオブジェクトによって、我々は、オブジェクト名、タイプ、およびオブジェクト内の偶数フィールドの値を取得することができます。getDeclaredFieldによる買収(「ID」)フィールドと呼ばれるIDとそのアクセスは、使用に続いて、フィールドにアクセスすることはできません、フィールドがプライベートである場合は、アクセスがtrueに設定されていない、真で設定した後、フィールド設定方法の設定値の設定方法は、最初のパラメータは、オブジェクトのインスタンスに関数であり、2つのパラメータを有し、2番目のパラメータは、フィールドの値は、次のプログラムを実行した結果です。

field Type is java.lang.Long ---- field name is id
field Type is java.lang.String ---- field name is username
field Type is java.lang.String ---- field name is password
before set field value : null
after set filed value : 314
复制代码

また、注釈は、この機能の原理を実現するために、自動的にフィールドに割り当て、フィールドは、親クラス情報、IOCコンテナSpringフレームワーク機能自動組立を得ることができる反射に依存して、フィールドランタイムを得ます情報、(自動組立を行うか否かを決定するために使用される)コメント情報を入力し、その後、例外をスロー見つけ、それを直接求めた値を割り当て、クラスのコンテナインスタンスに見えます。

2.3ペア操作する方法

メソッドのAPIクラスは、動作の多くの方法があるが、方法のほとんどは、このようなメソッドの戻り値、パラメータリスト、パラメータの数、メソッド名など、APIのほとんど修正方法などの情報を取得します。非常によく似た名前とそのAPIを提出し、同じ命名方式が使用されていると言えます。次のコードは、メソッドを操作する方法を示しています。

public class Main {

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        User user = new User();
        Class<?> userClass = user.getClass();

        //获取方法
        Method[] methods = userClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("method return type is " + method.getReturnType());
            System.out.println("method name is " + method.getName());
            System.out.println("method params count " + method.getParameterCount());
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                System.out.println("param type is " + parameter.getType());
                System.out.println("param name is " + parameter.getName());
            }
            System.out.println("----------------------------------------");
        }
        Method testMethod1 = userClass.getMethod("testMethod1", int.class, int.class);
        testMethod1.setAccessible(true);
        testMethod1.invoke(user, 1,1);  //调用该方法

    }
}
复制代码

そして、したいと提出)getDeclaredMethodsによって、すべてのメソッドを取得します(各メソッドは、Methodオブジェクトのインスタンスである、これが唯一の彼らのJDKのAPIの抽象化され、実際には、仮想マシンではそれほど単純ではありません、そして、すべてのAPIによるとそのパラメータ・リストを横断しながら、情報、コード、名前、様々なパラメータおよびそのパラメータリストの戻り値を取得する方法を取得します。最後getMethod()メソッドのパラメータ、最初のパラメータgetMethod()メソッドで指定された指定されたオブジェクトインスタンスのメソッドがメソッド名で、2番目のパラメータは可変パラメータの数で取得し、クラス・オブジェクト・パラメータを表し、Iは、testMethod1定義しました対応する方法が見つからない場合は2つのint型のパラメータは、そうここに渡された2つのオブジェクトint.classを、例外がスローされないNoSuchMethodException。

この方法を使用してコールにアクセスして起動するように構成され、その後、最初のパラメータは、オブジェクト・インスタンスのアクションを実行することで、第二のパラメータは可変パラメータであり、パラメータ値が渡される必要があります。最後に、プログラムを実行すると、次の出力は、一般的に見ることができます。

....
method type is void
method name is setPassword
method params count 1
param type is class java.lang.String
param name is arg0
----------------------------------------
method type is void
method name is testMethod1
method params count 2
param type is int
param name is arg0
param type is int
param name is arg1
----------------------------------------
....
复制代码

実際には、より多くありますが、私は一部を傍受してきました。我々の期待に沿った出力の多くが、出力パラメータ名は何地獄は何ですか?arg0は、arg1が何ですか?

私たちは、java8後にコンパイルされ-parametersを提供し、コンパイルがjava8前に同様のarg0に、代わりにARG1された後の反射は、オペレータがバイトコードにコンパイルされ、ランタイムオブジェクト、パラメータ名のためのメカニズムであると言ってきましたオプションでは、選択はデフォルトでオフになって、指定されたが、開く場合が開きます後、コンパイル済みのバイトコードは、ソースパラメータ名を使用します。その前に、それを実行しているフィールド名を取得する方法はありますか?答えは技術の使用には、ASMバイトコードやその他の技術を使用することで、我々は興味を持って友人が関連する情報を検索するために、オンラインで行くことができ、関与することはありません。

3まとめ

また、深遠な反射技術は、これはフィールドのより多くの使用及び操作は、方法は、ほとんど百が通過するという事実についての反射の簡単な使用だけの簡単な紹介で、それはそれは確かにJDKのドキュメントを見てみましょうではありませんA。読者は、仮想マシンのいくつかの知識を持っている場合、原理は、実際には、クラスローダが完了した後にどのようなこれらのフィールド、メソッド、注釈、インターフェースやその他の情報は、メソッド領域に格納されることを推測することは難しいことではありませんので、原則では、これは、関与していませんでした年は、また、その情報にアクセスするためのクラスオブジェクト参照を残しました。

それは「舞台裏」物事のこの種のために非常に適し反映されているので、多くのフレームワークは、リフレクションに多かれ少なかれを使用しています。最後に、実際にはJavaの世界では、「彼らが望むものは何でも」できる反射を学びます。

おすすめ

転載: juejin.im/post/5d6e808c518825168d37d2b8