1. 程式人生 > >Java反射完全詳解

Java反射完全詳解

三、反射的使用

3.1獲取Class物件的三種方式。 
3.1測試類:

public class Example1 {
    public static void main(String[] args) {
        // 1.第一種方式獲取Class物件
        // new產生一個物件,一個Class物件
        Example1 example1 = new Example1();
        // 獲取Class物件
        Class class1 = example1.getClass();
        System.out.println(class1.getName());

        // 2.第二種方式獲取Class物件
        Class class2 = Example1.class;
        // 判斷是否為同一個class物件
        System.out.println(class1 == class2);

        try {
            // 3.第三種方式獲取Class物件,引數為全限定名(最常用方法)
            Class class3 = Class.forName("top.itan.reflect.Example1");
            System.out.println(class2 == class3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

//執行結果
top.itan.reflect.Example1
true
true

3.2通過反射獲取構造方法並使用。 
3.2示例類:

public class Student {

    // 無參構造方法
    public Student() {
        System.out.println("呼叫了公有、無參構造方法");
    }

    // 有一個引數的構造方法
    public Student(String name) {
        System.out.println("呼叫了公有、一個引數的構造方法,姓名:" + name);
    }

    // 有多個引數的構造方法
    public Student(String name, int age) {
        System.out.println("呼叫了公有、兩個引數的構造方法,姓名:" + name + ",年齡:" + age);
    }

    // 受保護的構造方法
    protected Student(boolean n) {
        System.out.println("呼叫了受保護的、一個引數的構造方法,n = " + n);
    }

    // 私有構造方法
    private Student(int age) {
        System.out.println("呼叫了私有的、一個引數構造方法,年齡:" + age);
    }
}

3.2測試類:

public class Contructors {
    public static void main(String[] args) throws Exception {
        // 載入Class物件
        Class clazz = Class.forName("top.itan.reflect.Student");

        // 1.獲取所有公有構造方法
        System.out.println("=============================所有公有構造方法=============================");
        Constructor[] conArray = clazz.getConstructors();
        for (Constructor constructor : conArray) {
            System.out.println(constructor);
        }

        // 2.獲取所有的構造方法(包括:私有、受保護、預設、公有)
        System.out.println("\n===================所有的構造方法(包括:私有、受保護、預設、公有)===================");
        conArray = clazz.getDeclaredConstructors();
        for (Constructor constructor : conArray) {
            System.out.println(constructor);
        }

        // 3.獲取單個公有、無參的構造方法
        System.out.println("\n=========================獲取單個公有、無參的構造方法=============================");
        // 1.無參的構造方法,所以型別是一個null,不寫也可以,這裡需要的是一個【引數的型別】
        // 2.返回的是描述這個無參建構函式的類物件。
        Constructor constructor = clazz.getConstructor(null);

        System.out.println("constructor --->>> " + constructor);
        // 呼叫構造方法
        Object object = constructor.newInstance();
        System.out.println("object --->>> " + object);
        Student student = (Student) object;
        System.out.println("student --->>> " + student);

        // 4.獲取單個公有、有參構造方法,並呼叫
        System.out.println("\n=======================獲取單個公有、有參構造方法,並呼叫==========================");
        // 這裡需要的是一個【引數的型別】
        Constructor constructor2 = clazz.getDeclaredConstructor(String.class);
        System.out.println(constructor2);
        // 呼叫構造方法
        object = constructor2.newInstance("男");

        // 5.獲取單個私有、有參構造方法,並呼叫
        System.out.println("\n=======================獲取單個私有、有參構造方法,並呼叫==========================");
        // 這裡需要的是一個【引數的型別】
        Constructor constructor3 = clazz.getDeclaredConstructor(int.class);
        System.out.println(constructor3);
        // 暴力訪問(忽略掉訪問修飾符)
        constructor3.setAccessible(true);
        // 呼叫構造方法
        object = constructor3.newInstance(18);
    }
}
// 執行結果
=============================所有公有構造方法=============================
public top.itan.reflect.Student(java.lang.String,int)
public top.itan.reflect.Student(java.lang.String)
public top.itan.reflect.Student()

=================所有的構造方法(包括:私有、受保護、預設、公有)=================
private top.itan.reflect.Student(int)
protected top.itan.reflect.Student(boolean)
public top.itan.reflect.Student(java.lang.String,int)
public top.itan.reflect.Student(java.lang.String)
public top.itan.reflect.Student()

=======================獲取單個公有、無參的構造方法===========================
constructor --->>> public top.itan.reflect.Student()
呼叫了公有、無參構造方法
object --->>> 
[email protected]
student --->>> [email protected] =====================獲取單個公有、有參構造方法,並呼叫======================== public top.itan.reflect.Student(java.lang.String) 呼叫了公有、一個引數的構造方法,姓名:男 =====================獲取單個私有、有參構造方法,並呼叫======================== private top.itan.reflect.Student(int) 呼叫了私有的、一個引數構造方法,年齡:18

3.3獲取成員變數並呼叫 
3.3示例類:

public class Student {
    public Student() {

    }

    public String name;
    protected int age;
    char sex;
    private String phoneNum;

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]";
    }

}

3.3測試類:

public class Fields {

    public static void main(String[] args) throws Exception {
        // 1.獲取Class物件
        Class clazz = Class.forName("top.itan.fields.Student");

        // 2.獲取欄位
        System.out.println("=============================獲取所有公有的欄位=============================");
        Field[] fieldArray = clazz.getFields();
        for (Field field : fieldArray) {
            System.out.println(field);
        }

        // 3.獲取所有的欄位(包括私有、受保護、預設的、公有的)
        System.out.println("\n==================獲取所有的欄位(包括私有、受保護、預設的、公有的)==================");
        fieldArray = clazz.getDeclaredFields();
        for (Field field : fieldArray) {
            System.out.println(field);
        }

        // 4.獲取公有欄位並呼叫
        System.out.println("\n==============================獲取公有欄位並呼叫==============================");
        Field field = clazz.getField("name");
        System.out.println(field);
        // 產生Student物件 --->>> Student student = new Student();無參構造方法
        Object obj = clazz.getConstructor().newInstance();
        // 為Student物件中的name屬性賦值--->>> student.name = "劉德華"
        field.set(obj, "劉德華");
        // 驗證
        Student student = (Student) obj;
        System.out.println("驗證姓名:" + student.name);

        // 5.獲取私有欄位並呼叫
        System.out.println("\n==============================獲取私有欄位並呼叫=============================");
        Field field2 = clazz.getDeclaredField("phoneNum");
        System.out.println(field2);
        // 暴力反射,解除私有限定
        field2.setAccessible(true);
        field2.set(obj, "18888889999");
        System.out.println("驗證電話:" + student);

    }

}
// 執行結果
=============================獲取所有公有的欄位=============================
public java.lang.String top.itan.fields.Student.name

=====================獲取所有的欄位(包括私有、受保護、預設的、公有的)==================
public java.lang.String top.itan.fields.Student.name
protected int top.itan.fields.Student.age
char top.itan.fields.Student.sex
private java.lang.String top.itan.fields.Student.phoneNum

==============================獲取公有欄位並呼叫==============================
public java.lang.String top.itan.fields.Student.name
驗證姓名:劉德華

==============================獲取私有欄位並呼叫=============================
private java.lang.String top.itan.fields.Student.phoneNum
驗證電話:Student [name=劉德華, age=0, sex= , phoneNum=18888889999]

3.4獲取成員方法並呼叫 
3.4示例類:

public class Student {

    public void show1(String s) {
        System.out.println("呼叫了:公有的,String引數的show1(),引數值為:s --->>> " + s);
    }

    protected void show2() {
        System.out.println("呼叫了:受保護的,無參的show2()");
    }

    void show3() {
        System.out.println("呼叫了:預設的,無參的show3()");
    }

    private String show4(int age) {
        System.out.println("呼叫了:私有的,並且有返回值的,int引數的show4(),引數值為:age --->>> " + age);
        return String.valueOf(age);
    }

    public static void main(String[] args) {

    }
}

3.4測試類:

public class Methods {

    public static void main(String[] args) throws Exception {
        // 1.獲取Class物件
        Class clazz = Class.forName("top.itan.methods.Student");

        // 2.獲取所有公有方法
        System.out.println("=========================獲取所有的公有方法=======================");
        clazz.getMethods();
        Method[] methodArray = clazz.getMethods();
        for (Method method : methodArray) {
            System.out.println(method);
        }

        System.out.println("\n==============獲取所有的方法,包括公有的、預設的、受保護的、私有的================");
        Method[] methodArray1 = clazz.getDeclaredMethods();
        for (Method method : methodArray1) {
            System.out.println(method);
        }

        System.out.println("\n=====================獲取公有的show1()方法=======================");
        // 傳入方法名和引數型別
        Method method = clazz.getMethod("show1", String.class);
        System.out.println(method);
        // 例項化一個Student物件
        Object obj = clazz.getConstructor().newInstance();
        // 呼叫,需要兩個引數,一個是要呼叫的物件(獲取有反射),一個是實參
        method.invoke(obj, "劉德華");

        System.out.println("\n======================獲取私有的show4()方法=======================");
        // 傳入方法名和引數型別
        Method method2 = clazz.getDeclaredMethod("show4", int.class);
        System.out.println(method2);
        // 解除私有限定
        method2.setAccessible(true);
        // 例項化一個Student物件
        Object student = clazz.getConstructor().newInstance();
        // 呼叫,需要兩個引數,一個是要呼叫的物件(獲取有反射),一個是實參
        Object result = method2.invoke(student, 20);
        System.out.println("返回值:" + result);

    }
}
// 執行結果
=========================獲取所有的公有方法=======================
public void top.itan.methods.Student.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

==============獲取所有的方法,包括公有的、預設的、受保護的、私有的================
public void top.itan.methods.Student.show1(java.lang.String)
private java.lang.String top.itan.methods.Student.show4(int)
protected void top.itan.methods.Student.show2()
void top.itan.methods.Student.show3()

=====================獲取公有的show1()方法=======================
public void top.itan.methods.Student.show1(java.lang.String)
呼叫了:公有的,String引數的show1(),引數值為:s --->>> 劉德華

======================獲取私有的show4()方法=======================
private java.lang.String top.itan.methods.Student.show4(int)
呼叫了:私有的,並且有返回值的,int引數的show4(),引數值為:age --->>> 20
返回值:20

3.5反射main方法 
3.5示例類:

public class Student {
    public static void main(String[] args) {
        System.out.println("main 方法執行了...");
        if (args != null) {
            for (String string : args) {
                System.out.println("值為: --->>> " + string);
            }
        }
}

3.5測試類:

public class ReflectMainMethod {

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("top.itan.main.Student");
            // 第一個引數:方法名稱,第二個引數:方法形參的型別
            Method method = clazz.getMethod("main", String[].class);

            // 例項化Student物件
            Object object = clazz.newInstance();
            // 呼叫方法
            // 方式1,因為方法是static靜態的,object也可以為null
            method.invoke(object, (Object) new String[] { "a", "b", "c" });

            // 方式2,因為方法是static靜態的,object也可以為null
            method.invoke(object, new Object[] { new String[] { "d", "e", "f" } });
            // 這其實傳了一個null值
            method.invoke(object, new String[1]);

            // 不轉成Ojbect會報錯
            method.invoke(object, new String[] { "a", "b", "c" });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
// 執行結果

main 方法執行了...
值為: --->>> a
值為: --->>> b
值為: --->>> c
main 方法執行了...
值為: --->>> d
值為: --->>> e
值為: --->>> f
main 方法執行了...
java.lang.IllegalArgumentException: wrong number of arguments
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at top.itan.main.ReflectMainMethod.main(ReflectMainMethod.java:22)

3.6反射之其他使用一:讀取配置檔案裡的類名和方法再反射 
3.6示例類:

public class Student {

    public void read() {
        System.out.println("this is read() method");
    }

}

3.6檔案file.properties內容

className = top.itan.readfile.Student
methodName = read

3.6測試類:

public class Read {

    /**
     * 我們利用反射和配置檔案,可以:應用程式更新時,對原始碼無需進行任何修改,我們只需要將寫新類,並修改配置檔案即可
     */
    public static void main(String[] args) throws Exception {
        // 1.通過反射獲取Class物件,top.itan.readfile.Student
        Class clazz = Class.forName(getValue("className"));
        // 2.獲取read()方法
        Method method = clazz.getMethod(getValue("methodName"));
        // 3.例項化物件
        Object object = clazz.getConstructor().newInstance();
        // 4.呼叫read()方法
        method.invoke(object);
    }

    /**
     * 根據key讀取配置檔案中對應的value
     * 
     * @param key
     * @return
     * @throws IOException
     */
    public static String getValue(String key) throws IOException {
        // 獲取配置檔案的物件
        Properties properties = new Properties();
        // 獲取輸入流
        InputStream inputStream = new FileInputStream("D:/eclipseworkspacefor2017/reflect/src/main/resources/file.properties");
        // 將流載入到配置檔案物件中
        properties.load(inputStream);
        inputStream.close();
        // 返回根據key獲取的value值
        return properties.getProperty(key);
    }
}
// 執行結果
this is read() method

3.7反射之其他使用二:通過反射越過泛型檢查 
3.7測試類:

public class SkipGenerics {

    /**
     * 通過反射越過泛型檢查,例如:有一個String泛型的集合,怎樣能向這個集合中新增一個int型別的值
     */
    public static void main(String[] args) throws Exception {
        ArrayList<String> strList = new ArrayList<String>();
        strList.add("aaa");
        strList.add("bbb");
        // 此處編譯期就報錯
        // strList.add(100);

        // 獲取ArrayList的Class物件,反向的呼叫add()方法,新增資料
        Class clazz = strList.getClass();
        // 獲取add()方法
        Method method = clazz.getMethod("add", Object.class);
        // 呼叫add()方法
        method.invoke(strList, 100);
        // 遍歷集合
        for (Object obj : strList) {
            System.out.println(obj);
        }
    }
}
// 執行結果
aaa
bbb
100

3.8反射之其他使用三:通過反射修改String物件的值 
3.8測試類:

public class ChangeStringValue {
	public static void main(String[] args) {
		try {
			// 建立一個String型別的物件
			String str = "abcde";
			System.out.println("str --->>> " + str);

			// 1.獲取String的Class物件
			Class<?> clazz = Class.forName("java.lang.String");
			// 2.獲取String的value欄位
			Field field = clazz.getDeclaredField("value");
			// 3.改變value屬性的訪問許可權
			field.setAccessible(true);
			// 4.獲取str物件上的value屬性的值
			char[] chars = (char[]) field.get(str);
			System.out.println("chars --->>> " + String.valueOf(chars));
			// 5.改變value所引用的char陣列中第一個字串
			chars[0] = '_';
			System.out.println("str --->>> " + str);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

// 執行結果
str --->>> abcde
chars --->>> abcde
str --->>> _bcde