Java API —— 反射


1、類加載器

    1)類的加載
        · 當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,連接,初始化三步來實現對這個類進行初始化。
        · 加載 :就是指將class文件讀入內存,並為之創建一個Class對象。任何類被使用時系統都會建立一個Class對象。
        · 連接:
            · 驗證 是否有正確的內部結構,並和其他類協調一致
            · 准備 負責為類的靜態成員分配內存,並設置默認初始化值
            · 解析 將類的二進制數據中的符號引用替換為直接引用
        · 初始化:就是我們以前講過的初始化步驟
    2)類初始化時機             

  · 創建類的實例
  · 訪問類的靜態變量,或者為靜態變量賦值
  · 調用類的靜態方法
  · 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象
  · 初始化某個類的子類
  · 直接使用java.exe命令來運行某個主類

  3)類加載器
        · 負責將.class文件加載到內在中,並為之生成對應的Class對象。雖然我們不需要關心類加載機制,但是了解這個機制我們就能更好的理解程序的運行。
        · 類加載器的組成
            · Bootstrap ClassLoader 根類加載器
            · Extension ClassLoader 擴展類加載器
            · Sysetm ClassLoader 系統類加載器
    4)獲取Class文件對象的三種方式
Person類:
package cn.itcast_01;
public class Person {
    private String name;
    int age;
    public String address;
    public Person() {
    }
    private Person(String name) {
        this.name = name;
    }
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public void show() {
        System.out.println("show");
    }
    public void method(String s) {
        System.out.println("method " + s);
    }
    public String getString(String s, int i) {
        return s + "---" + i;
    }
    private void function() {
        System.out.println("function");
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }
}

測試類:

package cn.itcast_01;
/*
 * 反射:就是通過class文件對象,去使用該文件中的成員變量,構造方法,成員方法。
 * 
 * Person p = new Person();
 * p.使用
 * 
 * 要想這樣使用,首先你必須得到class文件對象,其實也就是得到Class類的對象。
 * Class類:
 *         成員變量    Field
 *         構造方法    Constructor
 *         成員方法    Method
 * 
 * 獲取class文件對象的方式:
 * A:Object類的getClass()方法
 * B:數據類型的靜態屬性class
 * C:Class類中的靜態方法
 *         public static Class forName(String className)
 * 
 * 一般我們到底使用誰呢?
 *         A:自己玩    任選一種,第二種比較方便
 *         B:開發    第三種
 *             為什么呢?因為第三種是一個字符串,而不是一個具體的類名。這樣我們就可以把這樣的字符串配置到配置文件中。
 */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1
        Person p = new Person();
        Class c = p.getClass();
        Person p2 = new Person();
        Class c2 = p2.getClass();
        System.out.println(p == p2);// false
        System.out.println(c == c2);// true
        // 方式2
        Class c3 = Person.class;
        // int.class;
        // String.class;
        System.out.println(c == c3);
        // 方式3
        // ClassNotFoundException
        Class c4 = Class.forName("cn.itcast_01.Person");
        System.out.println(c == c4);
    }
}
2、反射
    JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
    要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法,所以先要獲取到每一個字節碼文件對應的Class類型的對象。
    1)通過反射獲取構造方法並使用
        · 獲取構造方法
    getConstructors
    getDeclaredConstructors
        · 創建對象
    newInstance()
    con.newInstance(“zhangsan", 20);
例子1:
package cn.itcast_02;
import java.lang.reflect.Constructor;
import cn.itcast_01.Person;
/*
 * 通過反射獲取構造方法並使用。
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");
        // 獲取構造方法
        // public Constructor[] getConstructors():所有公共構造方法
        // public Constructor[] getDeclaredConstructors():所有構造方法
        // Constructor[] cons = c.getDeclaredConstructors();
        // for (Constructor con : cons) {
        // System.out.println(con);
        // }
        // 獲取單個構造方法
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        // 參數表示的是:你要獲取的構造方法的構造參數個數及數據類型的class字節碼文件對象
        Constructor con = c.getConstructor();// 返回的是構造方法對象
        // Person p = new Person();
        // System.out.println(p);
        // public T newInstance(Object... initargs)
        // 使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。
        Object obj = con.newInstance();
        System.out.println(obj);
        
        // Person p = (Person)obj;
        // p.show();
    }
}

 例子2:

package cn.itcast_02;
import java.lang.reflect.Constructor;
/*
 * 需求:通過反射去獲取該構造方法並使用:
 * public Person(String name, int age, String address)
 * 
 * Person p = new Person("林青霞",27,"北京");
 * System.out.println(p);
 */
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");
        // 獲取帶參構造方法對象
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor con = c.getConstructor(String.class, int.class,
                String.class);
        // 通過帶參構造方法對象創建對象
        // public T newInstance(Object... initargs)
        Object obj = con.newInstance("林青霞", 27, "北京");
        
        System.out.println(obj);
    }
}

例子3:

package cn.itcast_02;
import java.lang.reflect.Constructor;
/*
 * 需求:通過反射獲取私有構造方法並使用
 * private Person(String name){}
 * 
 * Person p = new Person("風清揚");
 * System.out.println(p);
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");
        // 獲取私有構造方法對象
        // NoSuchMethodException:每個這個方法異常
        // 原因是一開始我們使用的方法只能獲取公共的,下面這種方式就可以了。
        Constructor con = c.getDeclaredConstructor(String.class);
        // 用該私有構造方法創建對象
        // IllegalAccessException:非法的訪問異常。
        // 暴力訪問
        con.setAccessible(true);// 值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。
        Object obj = con.newInstance("風清揚");
        System.out.println(obj);
    }
}

 2)通過反射獲取成員變量並使用

        · 獲取所有成員
    getFields
           getDeclaredFields
        · 獲取單個成員
    getField
    getDeclaredField
        · 修改成員的值
    set(Object obj,Object value) :將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
例子:
package cn.itcast_03;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/*
 * 通過發生獲取成員變量並使用
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");
        // 獲取所有的成員變量
        // Field[] fields = c.getFields();
        // Field[] fields = c.getDeclaredFields();
        // for (Field field : fields) {
        // System.out.println(field);
        // }
        /*
         * Person p = new Person(); p.address = "北京"; System.out.println(p);
         */
        // 通過無參構造方法創建對象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);
        // 獲取單個的成員變量
        // 獲取address並對其賦值
        Field addressField = c.getField("address");
        // public void set(Object obj,Object value)
        // 將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
        addressField.set(obj, "北京"); // 給obj對象的addressField字段設置值為"北京"
        System.out.println(obj);
        // 獲取name並對其賦值
        // NoSuchFieldException
        Field nameField = c.getDeclaredField("name");
        // IllegalAccessException
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");
        System.out.println(obj);
        // 獲取age並對其賦值
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 27);
        System.out.println(obj);
    }
}

  3)通過反射獲取成員方法並使用

        · 獲取所有方法
    getMethods
    getDeclaredMethods
        · 獲取單個方法
    getMethod
    getDeclaredMethod
        · 暴力訪問
    method.setAccessible(true);
例子1:
package cn.itcast_04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");
        // 獲取所有的方法
        // Method[] methods = c.getMethods(); // 獲取自己的包括父親的公共方法
        // Method[] methods = c.getDeclaredMethods(); // 獲取自己的所有的方法
        // for (Method method : methods) {
        // System.out.println(method);
        // }
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        /*
         * Person p = new Person(); p.show();
         */
        // 獲取單個方法並使用
        // public void show()
        // public Method getMethod(String name,Class<?>... parameterTypes)
        // 第一個參數表示的方法名,第二個參數表示的是方法的參數的class類型
        Method m1 = c.getMethod("show");
        // obj.m1(); // 錯誤
        // public Object invoke(Object obj,Object... args)
        // 返回值是Object接收,第一個參數表示對象是誰,第二參數表示調用該方法的實際參數
        m1.invoke(obj); // 調用obj對象的m1方法
        System.out.println("----------");
        // public void method(String s)
        Method m2 = c.getMethod("method", String.class);
        m2.invoke(obj, "hello");
        System.out.println("----------");
        // public String getString(String s, int i)
        Method m3 = c.getMethod("getString", String.class, int.class);
        Object objString = m3.invoke(obj, "hello", 100);
        System.out.println(objString);
        // String s = (String)m3.invoke(obj, "hello",100);
        // System.out.println(s);
        System.out.println("----------");
        // private void function()
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}

例子2:我給你ArrayList<Integer>的一個對象,我想在這個集合中添加一個字符串數據,如何實現呢?

package cn.itcast.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
 * 我給你ArrayList<Integer>的一個對象,我想在這個集合中添加一個字符串數據,如何實現呢?
 */
public class ArrayListDemo {
    public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        // 創建集合對象
        ArrayList<Integer> array = new ArrayList<Integer>();
        // array.add("hello");
        // array.add(10);
        Class c = array.getClass(); // 集合ArrayList的class文件對象
        Method m = c.getMethod("add", Object.class);
        m.invoke(array, "hello"); // 調用array的add方法,傳入的值是hello
        m.invoke(array, "world");
        m.invoke(array, "java");
        System.out.println(array);
    }
}
例子3:寫一個方法,public void setProperty(Object obj, String propertyName, Object value){},此方法可將obj對象中名為propertyName的屬性的值設置為value。
Tool類:
package cn.itcast.test;
import java.lang.reflect.Field;
public class Tool {
    public void setProperty(Object obj, String propertyName, Object value)
            throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        // 根據對象獲取字節碼文件對象
        Class c = obj.getClass();
        // 獲取該對象的propertyName成員變量
        Field field = c.getDeclaredField(propertyName);
        // 取消訪問檢查
        field.setAccessible(true);
        // 給對象的成員變量賦值為指定的值
        field.set(obj, value);
    }
}

 測試類:

package cn.itcast.test;
public class ToolDemo {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException {
        Person p = new Person();
        Tool t = new Tool();
        t.setProperty(p, "name", "林青霞");
        t.setProperty(p, "age", 27);
        System.out.println(p);
        System.out.println("-----------");
        Dog d = new Dog();
        t.setProperty(d, "sex", '男');
        t.setProperty(d, "price", 12.34f);
        System.out.println(d);
    }
}
class Dog {
    char sex;
    float price;
    @Override
    public String toString() {
        return sex + "---" + price;
    }
}
class Person {
    private String name;
    public int age;
    @Override
    public String toString() {
        return name + "---" + age;
    }
}

 

 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM