SE高次(11):Java反射メカニズム-クラスのすべての情報を動的に取得


反射メカニズムとは?
        Java反射メカニズムは、実行状態において、いずれのクラスに対しても、このクラスのすべての属性と方法を知ることができる.任意のオブジェクトに対して、その任意のメソッドを呼び出すことができます.このような動的に取得され、オブジェクトを動的に呼び出す方法の機能をjavaの反射メカニズムと呼ぶ.
反射機構は主に以下の機能を提供する.
  • 実行時に任意のオブジェクトが属するクラスを判定する.
  • 実行時に任意のクラスのオブジェクトを構築する.
  • 実行時に任意のクラスが有するメンバー変数と方法を判定する.
  • 実行時に任意のオブジェクトを呼び出す方法.
  • は、動的エージェントを生成する.

  • 反射の利点と欠点:
  • 反射は実行時にクラス指定情報を取得でき、プログラムの柔軟性を高めることができる.フレームワークでは、反射によってプログラムの柔軟性が大幅に増加し、ソースコードにプログラムが書き込まれないようにします.
  • 反射はいくつかのダイナミックタイプを含み、JVMはこれらのコードを最適化できないため、反射操作の効率は非反射操作よりはるかに低い.
  • は1つの機能が反射を使わずに実現できれば反射を使わないが,柔軟性を保証するために使わざるを得ないところもあるので,適宜考慮する.

  • 簡単な例:クラスのオブジェクトをインスタンス化する場合、通常はnewオペレータを使用しますが、これは作成されたオブジェクトが固定されていることを意味し、別のオブジェクトに変更するにはソースコードを変更する必要があり、反射はClassインスタンスを使用して実行時にオブジェクトをインスタンス化できます.インスタンス化されたオブジェクトを変更するには、プロファイルを変更するだけでよいため、コードを変更して再コンパイルすることを避けるために、プログラムの柔軟性が大幅に増加します. 
    反射によるクラス情報の取得
    反射を使用するには、クラスごとに対応するクラスインスタンスがあり、クラスがメモリにロードされるとJVMがクラスインスタンスを返します.
    Classインスタンスを取得する3つの方法
  • Class.forName():Class cl=Class.forName("com.reflect.Test 02");
  • 準拠対象:new Test 01().getClass();
  • クラス名:Class cla=Test 01.class;
  • Class.forName()メソッドを使用するには、パッケージ名を付ける必要があります.そうしないと、クラスが見つかりません.このメソッドはクラスを初期化します.
  • //        
    public class Test02 {
    	private int a = 0;
    	private String s = "    ";
    	protected int c = 6;
    	public String str = "    ";
    	//   
    	private Test02() {System.out.println("        ...");}
    	public Test02(int a, String s, int c, String str) {
    		this.a = a;
    		this.s= s;
    		this.c = c;
    		this.str = str;
    	}	
    	//  
    	private void priMethod() {System.out.println("    ");}
    	protected void proMethod() {System.out.println("protected    ");}
    	void method(String def) {System.out.println("      ");}
    	public void pubMethod() {System.out.println("public    ");}
    }

    フィールド変数の取得(Field)
    Class cla = Test02.class;
    Field[] f = cla.getFields();//    public      
    Field[] f2 = cla.getDeclaredFields();//           
    for(Field result : f)
    	System.out.println(result);
    System.out.println("-------------------------");
    for(Field result : f2)
    	System.out.println(result);
    //         
    Field f3 = cla.getField("c"); //c     public,       
    Field f4 = cla.getDeclaredField("c");
  • 使用説明:フィールドを取得するには、指定したフィールドを取得するか、すべてのフィールドを取得するかの2つの方法があります.Declaredを持っていない場合はpublic権限しか取得できません.Declaredがある方法ではすべての権限を取得できます.取得コンストラクタと方法の使用方法は一致しています.
  • //		Test02 tes = cla.newInstance();//error,  Test02         ,        
    		Test02 test = new Test02(5, "A", 8, "B");//    
    		Field df = cla.getDeclaredField("str");//str   public  
    		System.out.println(df.get(test));//    B

    フィールド変数を取得するとアクセスできますか?例をあげて説明する
    		Test02 test = new Test02(5, "A", 8, "B");
    		Field df = cla.getDeclaredField("a");//a       
    		System.out.println(df.isAccessible());//false	
    		System.out.println(df.get(test));//        
    の上記の例では、アクセス権の取得はfalseであるが、public権限フィールドはアクセス可能であり、他の権限フィールドにアクセスすると例外が発生するため、setAccessible()メソッドを使用する必要がある.
    アクセス権チェックsetAccessible()の有効/無効化
    setAccessiable()メソッドは、Field、Method、Constructorの親クラスであるAccessObjectクラスに属し、このメソッドの3つが共通しています.転送trueはアクセスを許可し、falseはアクセスを許可しないことを示す.
    		Class cla = Test02.class;
    		Test02 test = new Test02(5, "A", 8, "B");
    		Field df = cla.getDeclaredField("a");//a       
    		System.out.println(df.isAccessible());//false
    		//        
    		df.setAccessible(true);
    		System.out.println(df.get(test));//      	

    Fieldの簡単なケース
    		System.out.println("   :" + df.getName());
    		System.out.println("       :" + df.getModifiers());
    		System.out.println("    :" + df.getType());
    		System.out.println("         :" + df.getGenericType());
  • 使用説明:以上に加えて、getXxx()およびsetXxx()メソッドは、フィールドタイプを基本タイプとして容易に処理することができる.get()/set()を使用してもいいですが、Javaは箱詰め分解メカニズムを提供しています.

  • =====================================================================================================================
    コンストラクタの取得(Constructor)
    		//  Class  ,      
    		Class cla = Class.forName("com.reflect.Test02");
    		//       ,    
    		Constructor[] con = cla.getDeclaredConstructors();
    		for(Constructor c : con)
    			System.out.println(c);
    		System.out.println("------------------------------");
    		//     ,         
    		Constructor constr = cla.getDeclaredConstructor();
    		//          ,        
    		constr.setAccessible(true);
    		Object obj = constr.newInstance();//              
    		//  Class  ,          
    		Constructor co = cla.getDeclaredConstructor(int.class, String.class, int.class, String.class);
  • 使用説明:デフォルトコンストラクタがプライベート化されているため、ClassインスタンスのnewInstance()でオブジェクトを得ることはできません.ClassインスタンスのnewInstance()メソッドは、本質的にデフォルトのConstructorを反射してnewInstance()を呼び出します.上記のコードでは反射により私有化コンストラクタが得られ,newInstance()を呼び出すことでこのClassインスタンス対応オブジェクトが生成されるが,これはプログラムのパッケージング性を著しく損ないprivateに直接アクセスできるため注意が必要である.

  • Constructorの簡単なケース
    Constructorの主な用途はオブジェクトを生成することであり,その独自の方法もFieldとあまり差がなく,Constructorの情報を取得するために用いられる.
    		//  Class  ,        
    		Constructor co = cla.getDeclaredConstructor(int.class, String.class, int.class, String.class);
    		//        ,  Class    
    		Class[] paramt = co.getParameterTypes();
    		for(Class para : paramt)
    			System.out.print(para + ", ");
    		//    
    		System.out.println(co.getParameterCount());
    		//   Constructor     
    		Parameter[] par = co.getParameters();
    		for(Parameter p: par)
    			System.out.print(p + ", ");

    =====================================================================================================================
    取得方法(Method)
    	public static void main(String[] args) throws Exception {
    		//  Class  
    		Class cla = Test02.class;
    		Method[] me = cla.getDeclaredMethods();//  cla        
    		for(Method m : me) {
    			System.out.println(m);
    			System.out.println("   :" + m.getName());
    			System.out.println("   :" + m.getModifiers());
    			Class[] para = m.getParameterTypes();
    			for(Class p : para) {
    				System.out.println("       :" + p);
    			}
    			System.out.println("    :" + m.getReturnType());
    			System.out.println("      :" + m.getParameterCount());
    			System.out.println("=============================================");
    		}
    		//      
    		Method method = cla.getDeclaredMethod("method", String.class);
    		Test02 test = new Test02(5, "A", 8, "B");//        ,          
    		//         
    		method.invoke(test, "@#$%");
    	}

    反射の小さな知識点
  • 以上に加えて、反射はクラスの注釈情報を取得するために使用することができ、使用方法はそれほど悪くない.
  • 反射のsetAccessible()は、パッケージ性を破って慎重に使用するプライベート領域のアクセスチェックを設定するために使用されます.
  • ClassインスタンスはnewInstance()を直接呼び出し、デフォルトのコンストラクタがプライベート化されると例外が発生します.