Java基礎——Java反射メカニズム及びIoC原理

21236 ワード

一、概念
主に、プログラムが自身の状態または動作にアクセスし、検出し、修正する能力を指し、自身の動作の状態と結果に基づいて、アプリケーションが記述した動作の状態と関連する意味を調整または修正することができる.Javaでは,クラスの名前が与えられる限り,反射機構によりクラスのすべての情報を得ることができる.
反射はJavaの強力なツールであり、コンポーネント間でソースコードリンクを行うことなく、柔軟なコードを容易に作成できます.しかし、反射の使用が適切でないとコストが高くなります!
クラスには何か情報があり,反射メカニズムを利用して何か情報を得ることができるが,クラスの名前を知ることが前提である.
二、作用
1)実行時に任意のオブジェクトが属するクラスを判断する.
2)実行時にクラスのオブジェクトを取得する.
3)実行時にjavaオブジェクトの属性,メソッド,構築メソッドなどにアクセスする.
三、長所と短所
まず、なぜ反射メカニズムを使うのかを明らかにしなければなりません.オブジェクトを直接作成すればいいのではないでしょうか.これは動的と静的の概念に関連しています. 
静的コンパイル:コンパイル時にタイプを決定し、オブジェクトをバインドします. 
動的コンパイル:実行時にタイプを決定し、オブジェクトをバインドします.動的コンパイルはjavaの柔軟性を最大限に発揮し、多態の応用を体現し、クラス間のレンコンの整合性を低下させる.
反射メカニズムの利点:ダイナミックなオブジェクトの作成とコンパイルを実現でき、大きな柔軟性を体現する(特にJ 2 EEの開発ではその柔軟性が非常に明らかになった).反射機構によりクラスの様々なコンテンツを得ることができ,逆コンパイルを行った.JAVAのようなコンパイルしてから実行する言語では,反射機構によりコードをより柔軟にし,オブジェクト向けの実現をより容易にすることができる.
例えば、大規模なソフトウェアでは、一度に完璧に設計することはできません.このプログラムがコンパイルされた後、発表されました.いくつかの機能を更新する必要があることに気づいたとき、ユーザーが以前のアンインストールをして、新しいバージョンを再インストールすることはできません.そうすれば、このソフトウェアはあまり使われていないに違いありません.静的であれば、プログラム全体を再コンパイルして機能の更新を実現する必要がありますが、反射メカニズムを採用すれば、アンインストールせずに、実行時に動的に作成してコンパイルするだけで、この機能を実現することができます. 
反射メカニズムの欠点:性能に影響を与える.反射を使用することは基本的に説明操作であり、JVMに何をしたいのか、そして私たちの要求を満たすことができるかを教えてあげることができます.このような操作は、同じ操作のみを直接実行するよりも常に遅い.
四、例
1、1つのオブジェクトから完全なパッケージ名とクラス名を取得する
注意:すべてのクラスのオブジェクトはクラスのインスタンスです.
package Reflect;

class Demo{
    //other codes...
}

class hello{
    public static void main(String[] args) {
        Demo demo=new Demo();
        System.out.println(demo.getClass().getName());
    }
}
実行結果:
Reflect.Demo

2、クラスオブジェクトのインスタンス化
package Reflect;

class Demo{
    //other codes...
}

class hello{
    public static void main(String[] args) {
        Class> demo1=null;
        Class> demo2=null;
        Class> demo3=null;
        try{
            //          
            demo1=Class.forName("Reflect.Demo");
        }catch(Exception e){
            e.printStackTrace();
        }
        demo2=new Demo().getClass();
        demo3=Demo.class;

        System.out.println("      "+demo1.getName());
        System.out.println("      "+demo2.getName());
        System.out.println("      "+demo3.getName());
    }
}
実行結果:
      Reflect.Demo
      Reflect.Demo
      Reflect.Demo

3、Classで他のクラスのオブジェクトをインスタンス化する
package Reflect;

class Person{
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}

class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per=null;
        try {
            per=(Person)demo.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        per.setName("Rollen");
        per.setAge(20);
        System.out.println(per);
    }
}
実行結果:
[Rollen  20]

ただし、Personのデフォルトの非パラメトリックコンストラクション関数をキャンセルすると、たとえばパラメータのあるコンストラクション関数を1つだけ定義すると、エラーが発生します.
たとえば、コンストラクション関数を定義します.
public Person(String name, int age) {               
    this.age=age;
    this.name=name;
}
上のプログラムを続行すると、次のように表示されます.
java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)
ですので、Classを使用して他のクラスのオブジェクトをインスタンス化する場合は、必ず自分で無参のコンストラクション関数を定義してください.
4、Classで他のクラスのコンストラクション関数を呼び出す(Classで他のクラスのオブジェクトを作成することもできます)
package Reflect;

import java.lang.reflect.Constructor;

class Person{
    public Person() {   

    }
    public Person(String name){
        this.name=name;
    }
    public Person(int age){
        this.age=age;
    }
    public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}

class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per1=null;
        Person per2=null;
        Person per3=null;
        Person per4=null;
        //         
        Constructor> cons[]=demo.getConstructors();
        try{
            per1=(Person)cons[0].newInstance();
            per2=(Person)cons[1].newInstance("Rollen");
            per3=(Person)cons[2].newInstance(20);
            per4=(Person)cons[3].newInstance("Rollen",20);
        }catch(Exception e){
            e.printStackTrace();
        }
        System.out.println(per1);
        System.out.println(per2);
        System.out.println(per3);
        System.out.println(per4);
    }
}
実行結果:
[null  0]
[Rollen  0]
[null  20]
[Rollen  20]

5、クラス実装のインタフェースを返す
package Reflect;

interface China{
    public static final String name="Rollen";
    public static  int age=20;
    public void sayChina();
    public void sayHello(String name, int age);
}

class Person implements China{
    public Person() {

    }
    public Person(String sex){
        this.sex=sex;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public void sayChina(){
        System.out.println("hello ,china");
    }
    @Override
    public void sayHello(String name, int age){
        System.out.println(name+"  "+age);
    }
    private String sex;
}

class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //       
        Class> intes[]=demo.getInterfaces();
        for (int i = 0; i < intes.length; i++) {
            System.out.println("        "+intes[i].getName());
        }
    }
}
実行結果:
        Reflect.China
(以下のいくつかの例では、この例のPersonクラスが使用されるので、紙面を節約するために、ここではPersonのコード部分を貼り付けず、メインクラスhelloのコードのみを貼り付ける)
6.他のクラスの親を取得する
class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //    
        Class> temp=demo.getSuperclass();
        System.out.println("      :   "+temp.getName());
    }
}
実行結果:
      :   java.lang.Object

7.他のクラスのすべてのコンストラクション関数を取得する
//             import java.lang.reflect.*;
class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            System.out.println("    :  "+cons[i]);
        }
    }
}
実行結果:
    :  public Reflect.Person()
    :  public Reflect.Person(java.lang.String)
class hello{
    public static void main(String[] args) {
        Class> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            Class> p[]=cons[i].getParameterTypes();
            System.out.print("    :  ");
            int mo=cons[i].getModifiers();
            System.out.print(Modifier.toString(mo)+" ");
            System.out.print(cons[i].getName());
            System.out.print("(");
            for(int j=0;j
実行結果:
    :  public Reflect.Person(){}
    :  public Reflect.Person(java.lang.String arg1){}

8、他のクラスのすべての属性を取得し、これらをまとめ、つまりclassによって一つのクラスのすべてのフレームワークを取得する
class hello {
    public static void main(String[] args) {
        Class> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============    ========================");
        //          
        Field[] field = demo.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            //      
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            //     
            Class> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + field[i].getName() + ";");
        }
        System.out.println("===============            ========================");
        //               
        Field[] filed1 = demo.getFields();
        for (int j = 0; j < filed1.length; j++) {
            //      
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            //     
            Class> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + filed1[j].getName() + ";");
        }
    }
}
実行結果:
===============    ========================
private java.lang.String sex;
===============            ========================
public static final java.lang.String name;
public static final int age;

9、反射によって他のクラスを呼び出す方法
class hello {
    public static void main(String[] args) {
        Class> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try{
            //  Person   sayChina  
            Method method=demo.getMethod("sayChina");
            method.invoke(demo.newInstance());
            //  Person sayHello  
            method=demo.getMethod("sayHello", String.class,int.class);
            method.invoke(demo.newInstance(),"Rollen",20);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
実行結果:
hello ,china
Rollen  20

10、他のクラスのsetとgetメソッドを呼び出す
class hello {
    public static void main(String[] args) {
        Class> demo = null;
        Object obj=null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try{
         obj=demo.newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        setter(obj,"Sex"," ",String.class);
        getter(obj,"Sex");
    }

    /**
     * @param obj        
     * @param att        
     * */
    public static void getter(Object obj, String att) {
        try {
            Method method = obj.getClass().getMethod("get" + att);
            System.out.println(method.invoke(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param obj            
     * @param att        
     * @param value     
     * @param type       
     * */
    public static void setter(Object obj, String att, Object value,
            Class> type) {
        try {
            Method method = obj.getClass().getMethod("set" + att, type);
            method.invoke(obj, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}// end class
実行結果:
 

11、反射操作による属性
class hello {
    public static void main(String[] args) throws Exception {
        Class> demo = null;
        Object obj = null;

        demo = Class.forName("Reflect.Person");
        obj = demo.newInstance();

        Field field = demo.getDeclaredField("sex");
        field.setAccessible(true);
        field.set(obj, " ");
        System.out.println(field.get(obj));
    }
}// end class

12、反射により配列の情報を取得して修正する
import java.lang.reflect.*;

class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5};
        Class>demo=temp.getClass().getComponentType();
        System.out.println("    : "+demo.getName());
        System.out.println("      "+Array.getLength(temp));
        System.out.println("        : "+Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("            : "+Array.get(temp, 0));
    }
}
実行結果:
    : int
      5
        : 1
            : 100

13、反射による配列サイズの変更
class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5,6,7,8,9};
        int[] newTemp=(int[])arrayInc(temp,15);
        print(newTemp);
        System.out.println("=====================");
        String[] atr={"a","b","c"};
        String[] str1=(String[])arrayInc(atr,8);
        print(str1);
    }
    /**
     *       
     * */
    public static Object arrayInc(Object obj,int len){
        Class>arr=obj.getClass().getComponentType();
        Object newArr=Array.newInstance(arr, len);
        int co=Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    /**
     *   
     * */
    public static void print(Object obj){
        Class>c=obj.getClass();
        if(!c.isArray()){
            return;
        }
        System.out.println("     : "+Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i)+" ");
        }
    }
}
実行結果:
     : 15
1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================
     : 8
a b c null null null null null

14、動的エージェント
まず、クラス・ローダの入手方法を見てみましょう.
class test{

}
class hello{
    public static void main(String[] args) {
        test t=new test();
        System.out.println("      "+t.getClass().getClassLoader().getClass().getName());
    }
}
実行結果:
      sun.misc.Launcher$AppClassLoader

実はjavaには3種類のローダがあります.
1)Bootstrap ClassLoaderこのローダはc++で記述されており,一般開発では珍しい.
2)Extension ClassLoaderは拡張クラスのロードに使用され、一般的にはjrelibextディレクトリのクラスに対応する
3)AppClassLoaderはclasspathで指定されたクラスをロードし、最も一般的なローダです.javaのデフォルトのローダでもあります.
動的エージェントを完了するには、まずInvocationHandlerインタフェースのサブクラスを定義し、エージェントの具体的な操作を完了する必要があります.
package Reflect;

import java.lang.reflect.*;

//      
interface Subject {
    public String say(String name, int age);
}

//       
class RealSubject implements Subject {
    @Override
    public String say(String name, int age) {
        return name + "  " + age;
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;

    public Object bind(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object temp = method.invoke(this.obj, args);
        return temp;
    }
}

class hello {
    public static void main(String[] args) {
        MyInvocationHandler demo = new MyInvocationHandler();
        Subject sub = (Subject) demo.bind(new RealSubject());
        String info = sub.say("Rollen", 20);
        System.out.println(info);
    }
}
実行結果:
Rollen  20

クラスのライフサイクル
クラスのコンパイルが完了したら、次のステップでクラスの使用を開始する必要があります.クラスを使用する場合は、JVMから離れられないに違いありません.プログラム実行中にJVMは、マウント、リンク、初期化の3つのステップで完了します.
クラスのロードはクラスローダによって完了する、ローダは.classファイルのバイナリファイルはJVMのメソッド領域にロードされ、このクラスを記述するjavaがスタック領域に作成される.lang.Classオブジェクト.データをカプセル化するために使用します.ただし、同じクラスはクラスローダに1回しかロードされません.
リンクとは,バイナリデータを実行可能な状態に組み立てることである.
リンクはチェック、準備、解析の3段階に分けられます.
検証:このバイナリ・ファイルが現在のJVM(バージョン)に適しているかどうかを確認するために一般的に使用されます.
準備:静的メンバーにメモリ領域を割り当てることです.デフォルト設定
解析:定数プール内のコードを直接参照として変換するプロセスを指し、すべてのシンボル参照がプログラムで使用できるようになるまで(完全な対応関係を確立する).
完了すると、タイプも初期化され、初期化後にクラスのオブジェクトが正常に使用され、1つのオブジェクトが使用されなくなるまでゴミが回収されます.スペースを解放します.クラスオブジェクトへの参照がない場合はアンインストールされ、クラスのライフサイクルが終了します.
五、IoC原理
SpringにおけるIoCの実現原理は工場モードの反射機構である.
1、反射機構を使わない場合の工場モードをまず見てみましょう
/**
 *     
 */
interface fruit{
    public abstract void eat();
}

class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}

class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}
//      
//                                 
class Factory{
    public static fruit getInstance(String fruitName){
        fruit f=null;
        if("Apple".equals(fruitName)){
            f=new Apple();
        }
        if("Orange".equals(fruitName)){
            f=new Orange();
        }
        return f;
    }
}

class hello{
    public static void main(String[] a){
        fruit f=Factory.getInstance("Orange");
        f.eat();
    }
}
サブクラスを追加する場合は、ファクトリクラスを変更する必要があります.サブクラスを追加しすぎると、変更が多くなります.
2、反射機構を利用した工場モード
package Reflect;

interface fruit{
    public abstract void eat();
}

class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}

class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}

class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}

class hello{
    public static void main(String[] a){
        fruit f=Factory.getInstance("Reflect.Apple");
        if(f!=null){
            f.eat();
        }
    }
}

これで、任意の複数のサブクラスを追加しても、ファクトリクラスは変更する必要はありません.
反射メカニズムを用いたファクトリモードは,反射によりインタフェースのインスタンスを取得できるが,完全なパケットとクラス名を伝達する必要がある.また、ユーザーは、1つのインタフェースに使用可能なサブクラスがいくつあるかを知ることができません.そのため、プロパティファイルの形式で必要なサブクラスを構成します.
3、反射機構を使用して、属性ファイルの工場モード(即ちIoC)を結合する
まずfruitを作成します.propertiesのリソースファイル:
apple=Reflect.Apple
orange=Reflect.Orange
次に、プライマリ・クラス・コードを記述します.
package Reflect;

import java.io.*;
import java.util.*;

interface fruit{
    public abstract void eat();
}

class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}

class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}
//       
class init{
    public static Properties getPro() throws FileNotFoundException, IOException{
        Properties pro=new Properties();
        File f=new File("fruit.properties");
        if(f.exists()){
            pro.load(new FileInputStream(f));
        }else{
            pro.setProperty("apple", "Reflect.Apple");
            pro.setProperty("orange", "Reflect.Orange");
            pro.store(new FileOutputStream(f), "FRUIT CLASS");
        }
        return pro;
    }
}

class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}

class hello{
    public static void main(String[] a) throws FileNotFoundException, IOException{
        Properties pro=init.getPro();
        fruit f=Factory.getInstance(pro.getProperty("apple"));
        if(f!=null){
            f.eat();
        }
    }
}
実行結果:
Apple