反射

反射

  1. 是 JAVA API, 是Java提供的現成的類!!
    • 接受API提供的功能!
  2. 是Java提供的動態執行機制, 動態加載類, 動態建立對象, 動態訪問屬性, 動態調用方法.

靜態與動態

靜態: 事先約定的規則, 執行期間按照固定規則執行.java

動態: 事先沒有約定, 在執行期間動態肯定執行規則.spring

JAVA 中的靜態執行: 編譯已經就肯定執行規則(執行次序), 在運行期間按照編譯結果順序執行.緩存

Foo foo = new Foo();
foo.hello();

JAVA 中的動態執行: 運行期間才能肯定加載哪些類, 建立哪些對象, 執行哪些方法...eclipse

動態加載類

Java 提供了動態加載類的APIide

Class cls=Class.forName(類名);

如:

Class cls=Class.forName("demo.Foo");

  

案例:測試

Scanner in= new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
//動態加載類
Class cls=Class.forName(className);
System.out.println(cls);

動態建立對象

語法 spa

Object obj = cls.newInstance();

執行cls引用的類信息中的無參數構造器, 動態建立實例. 若是類沒有無參數構造器, 則拋出異常!3d

提示: 反射能夠調用有參數構造器.code

案例:xml

Scanner in= new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
//動態加載類
Class cls=Class.forName(className);
System.out.println(cls);
//動態建立對象
Object obj = cls.newInstance();
System.out.println(obj);

動態獲取類的方法信息

發射API提供了動態獲取類中方法信息的API:

Method[] ary= cls.getDeclaredMethods();

案例:

Scanner in= new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
//動態加載類
Class cls=Class.forName(className);
System.out.println(cls);

//動態獲取類的方法信息
//從cls表明的類信息中獲取所有的方法信息
Method[] ary=
    cls.getDeclaredMethods();
//天天一個Method表明一個方法信息
//方法的全部要素都在這個對象中
for (Method method : ary) {
    System.out.println(method);
}

Method 對象表明方法信息

Method提供了獲取方法詳細信息的方法:

//獲取方法名
String name = method.getName();
//獲取返回值類型
Class type = method.getReturnType();

案例:

Scanner in= new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
//動態加載類
Class cls=Class.forName(className);
System.out.println(cls);

//動態獲取類的方法信息
//從cls表明的類信息中獲取所有的方法信息
Method[] ary=
    cls.getDeclaredMethods();
//天天一個Method表明一個方法信息
//方法的全部要素都在這個對象中
for (Method method : ary) {
    System.out.println(method);
    //獲取方法的詳細信息:
    System.out.println(
            method.getName());
    System.out.println(
            method.getReturnType());
    String name=method.getName();
    //檢查一個字符串是否以test爲開頭
    if(name.startsWith("test")){
        System.out.println("找到了");
    }
}

動態執行方法

invoke: 調用 method: 方法

語法:

method.invoke(執行方法的對象, 傳遞的參數)

//正確的狀況
購買飲料.invoke(小賣鋪, 錢)

//錯誤的狀況
購買飲料.invoke(達內前臺, 錢) 

必須在對象上執行一個非靜態方法, 調用方法時候必須有對象.

在invoke方法執行時候, 必須傳遞包含當前方法的對象!!!

案例:

業務需求: 執行某個類中所有的以test爲開頭的無參數無返回值的非靜態方法.

//動態加載類
Scanner in= new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
Class cls=Class.forName(className);
//動態獲取所有方法信息
Method[] ary=
    cls.getDeclaredMethods();
//迭代所有方法查找以test爲開頭的方法
Object obj = cls.newInstance();//"";
for (Method method : ary) {
    if(method.getName()
            .startsWith("test")){
        System.out.println(method);
        //動態執行方法
        method.invoke(obj);
    }
}

使用invoke

Object obj=
    method.invoke(對象, 參數1, 參數2...)

invoke 方法有返回值, 返回被調用方法執行的結果, 對象後面參數是執行方法時候傳遞的參數.

invoke 能夠調用私有方法.

案例:

//動態加載類
Scanner in = new Scanner(System.in);
System.out.print("輸入類名:");
String className=in.nextLine();
Class cls = Class.forName(className);

//1. 找到demo方法
// Class 提供了根據方法簽名找到指定
// 方法信息的API
String name="demo";//方法名
//類型列表 Class[]
// String.class 表示字符串的類型
// int.class 表示int類型
// 任何.class 表示任何的類型
Class[] types={String.class,int.class};
//根據方法簽名在cls查找方法信息
Method method=
    cls.getDeclaredMethod(name, types);
//找到了私有方法
System.out.println(method);
//執行私有方法
//打開方法的執行權限!!!違反封裝!
method.setAccessible(true); 
Object obj = cls.newInstance();
Object value=
    method.invoke(obj, "Tom", 12);
System.out.println(value); 

反射用途

  1. eclipse 中解析類的結構使用了反射
  2. JUnit識別被測試方法使用了反射
    • JUnit3利用反射查找test開頭的方法
    • JUnit4利用反射解析@Test查找測試方法
  3. Spring管理Bean對象, 注入Bean屬性使用了反射
    • 利用反射建立Bean對象實例
    • 利用反射注入Bean的屬性
  4. 註解的解析使用了反射
    • 利用反射API支持註解
  5. 強行執行私有方法(訪問私有屬性)

JUnit4 原型:

@Retention(RetentionPolicy.RUNTIME)
public @interface Demo {

}

public class TestCase {
    public void test(){
        System.out.println("test");
    }
    @Demo
    public void hello(){
        System.out.println("Hello");
    }
    @Demo
    public void helloKitty(){
        System.out.println("Hello Kitty");
    }
}

/*
 * 動態執行一個類中所有以@Demo註解標註的方法
 */
public class Demo05 {
    public static void main(String[] args) 
        throws Exception{
        //動態加載類
        //動態獲取所有方法
        //動態檢查方法的註解信息
        Scanner in=new Scanner(System.in);
        System.out.print("類名:");
        String className=in.nextLine();
        Class cls = Class.forName(className);
        Method[] ary=
            cls.getDeclaredMethods();
        Object obj = cls.newInstance();
        for (Method method : ary) {
            //檢查一個方法的註解信息
            //method.getAnnotation(被檢查的註解類型)
            //返回註解類型對象, 若是爲空表示沒有註解
            //不爲空表示找到了被檢查的註解Annotation
            Demo ann=method.getAnnotation(
                    Demo.class);
            System.out.println(method);
            System.out.println(ann); 
            if(ann!=null){
                method.invoke(obj);
            }
        }
    }
}
Demo.java

Spring 原型

代碼:

public class ApplicationContext {
    //是緩存Spring容器的Bean對象
    private Map<String, Object> beans=
        new HashMap<String, Object>();
    /**
     * 利用配置文件初始化當前容器
     * 利用xml配置文件, 初始化所有的Bean對象
     */
    public ApplicationContext(String xml) 
        throws Exception{
        //利用DOM4j, 讀取XML文件
        //解析XML文件內容, 獲得Bean的類名
        //和Bean的ID:
        // 根據類名動態加載類而且建立對象
        // 將對象和對應的ID添加到map中

        //從 Resource(classpath) 中讀取流
        InputStream in=getClass()
            .getClassLoader()
            .getResourceAsStream(xml); 
        SAXReader reader=new SAXReader();
        Document doc=reader.read(in);
        in.close();
        //解析xml :<beans><bean><bean>....
        Element root=doc.getRootElement();
        //讀取根元素中所有的bean子元素
        List<Element> list=
                root.elements("bean");
        for (Element e : list) {
            //e 就是 bean 元素 id屬性和class屬性
            String id=e.attributeValue("id");
            String className=
                    e.attributeValue("class");
            //動態加載類, 動態建立對象
            Class cls=Class.forName(className);
            Object bean=cls.newInstance();
            beans.put(id, bean);
        }
    }

    public Object getBean(String id){
        //根據id在map查找對象, 並返回對象
        return beans.get(id); 
    }

    //泛型方法: 優勢是能夠減小一次類型轉換
    public<T> T getBean(
            String id, Class<T> cls){
        return (T)beans.get(id);
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="foo"
        class="demo.Foo"></bean>
    <bean id="date"
        class="java.util.Date"></bean>
    <bean id="testCase"
        class="demo.TestCase"></bean>
</beans>

public class Demo06 {

    public static void main(String[] args) 
        throws Exception {
        ApplicationContext ctx=
            new ApplicationContext(
            "spring-context.xml");
        Foo foo = (Foo)ctx.getBean("foo");
        Foo f2 = ctx.getBean(
                "foo", Foo.class);
        System.out.println(foo); 
        System.out.println(f2); 
    }
}
ApplicationContext.java

做業

  1. 實現 JUnit3 原型
  2. 實現 JUnit4 原型
  3. 實現 Spring 原型
  4. 調用一個對象的私有方法
相關文章
相關標籤/搜索