Javaベースシリーズ-Java反射

15543 ワード

クラス能力を解析できるプログラムを反射(reflective)と呼ぶ.反射メカニズムの機能は非常に強力で、主に以下の機能を提供しています.
  • 任意のクラスについて、このクラスのすべての属性と方法を知ることができる.
  • は、任意のオブジェクトについて、その任意の方法および属性を呼び出すことができる.

  • クラス
    プログラム実行中、Javaランタイムシステムは常にすべてのオブジェクトに対してランタイムと呼ばれるタイプ識別を維持します.この情報は、各オブジェクトが属するクラスを追跡します.仮想マシンは、ランタイムタイプ情報を使用して、対応するメソッドを選択して実行します.しかしながら、javaである専用のJavaクラスを介してこれらの情報にアクセスすることができる.lang.Class.
    クラスクラスクラスのインスタンスは、実行中のJavaアプリケーションのクラスとインタフェースを表します.列挙は一種であり、注釈はインタフェースである.各配列もクラスに属し、同じ要素タイプと次元数を持つすべての配列が共有するClassオブジェクトとして反映されます.元のJavaタイプ(boolean,byte,char,short,int,long,float,double)とキーワードvoidもClassオブジェクトとして表されます.
    Classクラスには共通構造関数はありません.Classオブジェクトは、クラスがロードされ、クラスローダのdefineClassメソッドが呼び出されるため、Java仮想マシンによって自動的に構築されます.
    クラスオブジェクトをnewすることはできませんが、既存のクラスからクラスオブジェクトを得るには、次の3つの方法があります.
    1、ClassクラスのforNameメソッド
     Class> clazz = Class.forName("com.codersm.study.jdk.reflect.Person");
    

    2、一つのクラスを通過するオブジェクトのgetClass()方法
     Class> clazz = new Person().getClass();
    

    3、Class字面定数
     Class clazz = Person.Class;
    

    まとめ:
  • forNameメソッドを呼び出すときにClassNotFoundExceptionという名前の例外をキャプチャする必要があることに注意してください.forNameメソッドはコンパイラで伝達された文字列に対応するクラスが存在するかどうかを検出できないため、プログラムの実行時にのみチェックでき、存在しない場合はClassNotFoundException例外を放出します.
  • Class字面定数この方法はより簡単で、より安全です.コンパイラはコンパイラによってチェックされるため、forNameメソッドを呼び出す必要がないため効率が高くなります.クラスは、文字量の方法でClassオブジェクトの参照を取得しても自動的に初期化されないためです.さらに興味深いことに,字面定数の取得Classオブジェクト参照方式は通常のクラスだけでなく,インタフェース,配列,基本データ型も適用できる.

  • Classクラスは、そのClassオブジェクトに対応する詳細を取得するために多くのインスタンスメソッドを提供しています.Classクラスには、ほぼ次のメソッドが含まれています.各メソッドには複数のリロードバージョンが含まれています.したがって、簡単な説明をするだけです.詳細はJDKドキュメントを参照してください.
  • クラス情報の取得
    コンテンツの取得
    メソッド署名
    コンストラクタConstructor getConstructor(Class>... parameterTypes)
    方法Method getMethod(String name, Class>... parameterTypes)
    ツールバーのField getField(String name)
    Annotation A getAnnotation(ClassannotationClass)
    内部クラスClass>[] getDeclaredClasses()
    外部クラスClass> getDeclaringClass()
    実装されたインタフェースClass>[] getInterfaces()
    修飾子int getModifiers()
    パッケージPackage getPackage()
    クラス名String getName()
    略称String getSimpleName()
    注意:getDeclaredXxxメソッドは、private/publicに関係なく、すべてのXxxを取得できます.
  • クラス自体の情報を判断する方法
    判断内容
    メソッド署名
    注記の種類boolean isAnnotation()
    このAnnotation修飾を使用しましたか?boolean isAnnotationPresent(Class extends Annotation> annotationClass)
    匿名クラス?boolean isAnonymousClass()
    配列?boolean isArray()
    列挙?boolean isEnum()
    元のタイプ?boolean isPrimitive()
    インターフェース?boolean isInterface()
    objがClassのインスタンスかどうかboolean isInstance(Object obj)
  • 反射を使用してペアを生成し、操作する
  • プログラムは、Methodオブジェクトによって対応する方法を実行することができる.
  • は、Constructorオブジェクトを介して対応するコンストラクタ作成インスタンスを呼び出す.
  • は、Filedオブジェクトを介してオブジェクトのメンバー変数値に直接アクセスし、変更します.


  • オブジェクトの作成
    反射によってオブジェクトを生成する方法は2つあります.
  • ClassオブジェクトのnewInstance()メソッドを使用して、Classオブジェクト対応クラスのインスタンスを作成します(この方法では、Classオブジェクトの対応クラスにデフォルトコンストラクタが必要です).
  • Classオブジェクトを使用して指定したConstructorオブジェクトを取得し、ConstructorオブジェクトのnewInstance()メソッドを呼び出して、指定したコンストラクタを選択してインスタンスを作成します.
  • class Person {
    
        private String name;
    
        private Integer age;
    
        public Person() {
            this.name = "system";
            this.age = 99;
        }
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public String getName() {
            return name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            Class pClass = Person.class;
            //    1       
            Person p = pClass.newInstance();
            System.out.println(p);
            //    2       
            Constructor constructor = pClass.getDeclaredConstructor(
                                                        String.class, Integer.class);
            Person person2 = constructor.newInstance("zhangsan",20);
            System.out.println(person2);
        }
    }
    

    呼び出し方法
    クラスに対応するクラスオブジェクトを取得すると、そのクラスオブジェクトのgetMethodによってMethod配列またはMethodオブジェクトを取得することができる.各Methodオブジェクトは1つのメソッドに対応し、Methodオブジェクトを取得した後、invokeメソッドを呼び出すことでそのMethodオブジェクトに対応するメソッドを呼び出すことができます.
        Person person = new Person();
        //   getAge  
        Method getAgeMethod = person.getClass().getMethod("getAge",null);
        //   invoke     getAge  
        Integer age = (Integer) getAgeMethod.invoke(person,null);
        System.out.println(age);
    

    メンバー変数へのアクセス
    クラスに含むメンバー変数の全てまたは指定されたメンバー変数Fieldは、クラスのgetField()メソッドによって取得することができ、次の2つの方法でメンバー変数の値を読み出して設定ことができる.
  • getXxx(Object obj):objオブジェクトのメンバー変数の値を取得します.ここでのXxxは8の基本タイプに対応し、メンバー変数のタイプが参照タイプであれば、getの後ろのXxxをキャンセルします.
  • setXxx(Object obj,Xxx val):objオブジェクトのメンバー変数値をval値に設定.ここでのXxxは8つの基本タイプに対応し、メンバータイプが参照タイプである場合、setの後ろのXxxをキャンセルします.
  •  Person person = new Person();
     //   name    Field
     Field nameField = person.getClass().getDeclaredField("name");
     //         
     nameField.setAccessible(true);
     //   person       name  
     String name = (String) nameField.get(person);
     System.out.println("name = " + name);
     //   person       name  
     nameField.set(person, "lisi");
     System.out.println(person);
    

    オペレーション配列
    Javaでのjava.lang.reflectパッケージには、Java配列を動的に作成およびアクセスする方法を提供する配列を動的に操作できるクラスが存在します.Arrayでは、getまたはset操作を実行して値を取り、値を割り当てることができます.Classクラスで配列に関連付ける方法は、次のとおりです.
    方法
    説明
    public native Class> getComponentType()
    配列要素のタイプを表すClass、すなわち配列のタイプを返します.
    public native boolean isArray()
    このクラスオブジェクトが配列クラスを表すかどうかを判定します.
    java.lang.reflect.Arrayでよく使われる静的方法は次のとおりです.
  • newInstance(Class> componentType, int length)
  • newInstance(Class> componentType, int... dimensions)
  • int getLength(Object array)
  • Object get(Object array, int index)
  • void set(Object array, int index, Object value)

  • 汎用配列コピー機能を実現し、そのコードは以下の通りである.
    public class GenericityArray {
    
        public static  T[] copy(T[] clazz) {
            return (T[]) Array.newInstance(
                            clazz.getClass().getComponentType(), 
                            clazz.length);
    }
    
        public static void main(String[] args) {
            Integer[] array = {1, 2, 3};
            Integer[] copyArray = GenericityArray.copy(array);
            System.out.println(copyArray.length);
        }
    }
    

    反射を使用した汎用情報の取得
    実際の開発のニーズに応えるために、Javaは、反射操作によって汎用化されるために、java.lang.reflect.ParameterizedTypejava.lang.reflect.GenericArrayTypejava.lang.reflect.TypeVariablejava.lang.reflect.WildcardTypeを追加した.
    を選択します.
    意味
    ParameterizedType
    Collectionなどのパラメトリックタイプ
    GenericArrayType
    1つの要素タイプは、パラメトリックタイプまたはタイプ変数の配列タイプです.
    TypeVariable
    さまざまなタイプの変数の共通インタフェース
    WildcardType
    ワイルドカードタイプの式.extends Number
    ここではParameterizedTypeを用いる汎用情報を取得することができる.
    public class Client {
     
        private Map objectMap;
     
        public void test(Map map, String string) {
        }
     
        public Map test() {
            return null;
        }
     
        /**
         *       
         *
         * @throws NoSuchFieldException
         */
        @Test
        public void testFieldType() throws NoSuchFieldException {
            Field field = Client.class.getDeclaredField("objectMap");
            Type gType = field.getGenericType();
            //   type generic type   
            System.out.println(field.getType());
            System.out.println(gType);
            System.out.println("**************");
            if (gType instanceof ParameterizedType) {
                ParameterizedType pType = (ParameterizedType) gType;
                Type[] types = pType.getActualTypeArguments();
                for (Type type : types) {
                    System.out.println(type.toString());
                }
            }
        }
     
        /**
         *       
         *
         * @throws NoSuchMethodException
         */
        @Test
        public void testParamType() throws NoSuchMethodException {
            Method testMethod = Client.class.getMethod("test", Map.class, String.class);
            Type[] parameterTypes = testMethod.getGenericParameterTypes();
            for (Type type : parameterTypes) {
                System.out.println("type -> " + type);
                if (type instanceof ParameterizedType) {
                    Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
                    for (Type actualType : actualTypes) {
                        System.out.println("\tactual type -> " + actualType);
                    }
                }
            }
        }
     
        /**
         *        
         *
         * @throws NoSuchMethodException
         */
        @Test
        public void testReturnType() throws NoSuchMethodException {
            Method testMethod = Client.class.getMethod("test");
            Type returnType = testMethod.getGenericReturnType();
            System.out.println("return type -> " + returnType);
     
            if (returnType instanceof ParameterizedType) {
                Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
                for (Type actualType : actualTypes) {
                    System.out.println("\tactual type -> " + actualType);
                }
            }
        }
    }
    

    反射を使用した注記の取得
    反射を使用して注釈情報を取得する方法については、Java注釈の実践を参照してください.
    参考資料
  • Javaタイプ情報(Classオブジェクト)と反射機構
  • を深く理解する.
  • Java反射