1. 程式人生 > >Java基礎-反射例項

Java基礎-反射例項

反射最重要的用途就是開發各種通用框架,是框架設計的靈魂

很多框架(比如 Spring)都是配置化的(比如通過 XML檔案配置JavaBean,Action之類的),為了保證框架的通用性,他們可能根據配置檔案載入不同的物件或類,呼叫不同的方法,這個時候就必須用到反射——執行時動態載入需要載入的物件。

反射使用用例:

1.獲取物件的三種方式

    student類

package com.h3c.demo.reflect;

public class 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 + '\'' +
                '}';
    }

    //---------------構造方法-------------------
    //(預設的構造方法)
    Student(String str){
        System.out.println("(預設)的構造方法 s = " + str);
    }

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

    //有一個引數的構造方法
    public Student(char 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);
    }

    //**************成員方法***************//
    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 "abcd";
    }

    public void show(){
        System.out.println("is show()");
    }

    public static void main(String[] args) {
        System.out.println("main執行了");
    }

}

   獲取class物件

package com.h3c.demo.reflect;

/**
 * 獲取Class物件的三種方式
 * 1 Object ——> getClass();
 * 2 任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性
 * 3 通過Class類的靜態方法:forName(String  className)(常用)
 *
 */
public class TestReflect {

    public static void main(String[] args) {
        //第一種獲取class物件
        Student student = new Student();
        Class<? extends Student> aClass = student.getClass();
        System.out.println(aClass.getName());

        //第二種獲取class物件
        Class<Student> bClass = Student.class;
        System.out.println(aClass == bClass);

        //第三種獲取class物件
        try {
            Class<?> cClass = Class.forName("com.h3c.demo.reflect.Student");
            System.out.println(bClass == cClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

2. 通過反射獲取構造方法使用

package com.h3c.demo.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

/**
 * 通過Class物件可以獲取某個類中的:構造方法、成員變數、成員方法;並訪問成員;
 *
 * 1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、預設、公有)

 * 		2).獲取單個的方法,並呼叫:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、預設、公有;
 *
 * 			呼叫構造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class TestContructor {
    public static void main(String[] args) {
        //呼叫class物件
        try {
            Class<?> aClass = Class.forName("com.h3c.demo.reflect.Student");
        //獲取公有所有構造方法
            System.out.println("############獲取所有公有構造方法##############");
            Constructor<?>[] constructors = aClass.getConstructors();
            Arrays.asList(constructors).stream().forEach(x->System.out.println(x));
            System.out.println("************所有的構造方法(包括:私有、受保護、預設、公有)***************");
            Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
            Arrays.asList(declaredConstructors).stream().forEach(x-> System.out.println(x));
            System.out.println("*****************獲取公有、無參的構造方法*******************************");
            Constructor<?> constructor = aClass.getConstructor(null);
            System.out.println("constructor"+constructor);
            //呼叫構造方法
            Object object = constructor.newInstance();
            System.out.println("Object"+object);
            System.out.println("******************獲取私有構造方法,並呼叫*******************************");
            constructor = aClass.getDeclaredConstructor(int.class);
            System.out.println(constructor);
            //呼叫構造方法
            constructor.setAccessible(true);
            object = constructor.newInstance(23);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
/**
 * 呼叫方法:
 1.獲取構造方法:
 1).批量的方法:
 public Constructor[] getConstructors():所有"公有的"構造方法
 public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、預設、公有)

 2).獲取單個的方法,並呼叫:
 public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、預設、公有;

 呼叫構造方法:
 Constructor-->newInstance(Object... initargs)

 2、newInstance是 Constructor類的方法(管理建構函式的類)
 api的解釋為:
 newInstance(Object... initargs)
 使用此 Constructor 物件表示的構造方法來建立該構造方法的宣告類的新例項,並用指定的初始化引數初始化該例項。
 它的返回值是T型別,所以newInstance是建立了一個構造方法的宣告類的新例項物件。併為之呼叫
 **/

控制檯結果:

############獲取所有公有構造方法##############
public com.h3c.demo.reflect.Student(java.lang.String,int)
public com.h3c.demo.reflect.Student(char)
public com.h3c.demo.reflect.Student()
************所有的構造方法(包括:私有、受保護、預設、公有)***************
protected com.h3c.demo.reflect.Student(boolean)
public com.h3c.demo.reflect.Student(java.lang.String,int)
public com.h3c.demo.reflect.Student(char)
private com.h3c.demo.reflect.Student(int)
com.h3c.demo.reflect.Student(java.lang.String)
public com.h3c.demo.reflect.Student()
*****************獲取公有、無參的構造方法*******************************
constructorpublic com.h3c.demo.reflect.Student()
呼叫了公有、無參構造方法執行了。。。
ObjectStudent{name='null', age=0, sex= , phoneNum='null'}
******************獲取私有構造方法,並呼叫*******************************
private com.h3c.demo.reflect.Student(int)
私有的構造方法   年齡:23

3. 獲取成員變數並呼叫

package com.h3c.demo.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

/**
 * 獲取成員變數並呼叫:
 *
 * 1.批量的
 * 		1).Field[] getFields():獲取所有的"公有欄位"
 * 		2).Field[] getDeclaredFields():獲取所有欄位,包括:私有、受保護、預設、公有;
 * 2.獲取單個的:
 * 		1).public Field getField(String fieldName):獲取某個"公有的"欄位;
 * 		2).public Field getDeclaredField(String fieldName):獲取某個欄位(可以是私有的)
 *
 * 	 設定欄位的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					引數說明:
 * 					1.obj:要設定的欄位所在的物件;
 * 					2.value:要為欄位設定的值;
 *
 */
public class TestField {

    public static void main(String[] args) {
        //獲取class物件
        try {
            Class<?> aClass = Class.forName("com.h3c.demo.reflect.Student");
        //獲取欄位
            System.out.println("************獲取所有公有的欄位********************");
            Field[] fields = aClass.getFields();
            Arrays.asList(fields).stream().forEach(x-> System.out.println(x));
            System.out.println("************獲取所有的欄位(包括私有、受保護、預設的)********************");
            Field[] declaredFields = aClass.getDeclaredFields();
            Arrays.asList(declaredFields).stream().forEach(x-> System.out.println(x));
            System.out.println("*************獲取公有欄位**並呼叫***********************************");
            Field name = aClass.getField("name");
            System.out.println(name);
            //獲取一個物件
            Object o = aClass.getConstructor().newInstance();
            name.set(o,"李華");
            //驗證
            Student student = (Student) o;
            System.out.println("驗證姓名"+student.name);
            System.out.println("**************獲取私有欄位****並呼叫********************************");
            Field phoneNum = aClass.getDeclaredField("phoneNum");
            System.out.println(phoneNum);
            phoneNum.setAccessible(true);
            phoneNum.set(o,"15537694281");
            System.out.println("驗證聯絡方式"+student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

控制檯結果:

************獲取所有公有的欄位********************
public java.lang.String com.h3c.demo.reflect.Student.name
************獲取所有的欄位(包括私有、受保護、預設的)********************
public java.lang.String com.h3c.demo.reflect.Student.name
protected int com.h3c.demo.reflect.Student.age
char com.h3c.demo.reflect.Student.sex
private java.lang.String com.h3c.demo.reflect.Student.phoneNum
*************獲取公有欄位**並呼叫***********************************
public java.lang.String com.h3c.demo.reflect.Student.name
呼叫了公有、無參構造方法執行了。。。
驗證姓名李華
**************獲取私有欄位****並呼叫********************************
private java.lang.String com.h3c.demo.reflect.Student.phoneNum
驗證聯絡方式Student{name='李華', age=0, sex= , phoneNum='15537694281'}

4. 獲取成員方法並呼叫

package com.h3c.demo.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 獲取成員方法並呼叫:
 *
 * 1.批量的:
 * 		public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
 * 		public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
 * 2.獲取單個的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					引數:
 * 						name : 方法名;
 * 						Class ... : 形參的Class型別物件
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * 	 呼叫方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					引數說明:
 * 					obj : 要呼叫方法的物件;
 * 					args:呼叫方式時所傳遞的實參;
 */
public class TestMethod {

    public static void main(String[] args) {
        //獲取class物件
        try {
            Class<?> aClass = Class.forName("com.h3c.demo.reflect.Student");
            System.out.println("***************獲取所有的”公有“方法*******************");
            Method[] methods = aClass.getMethods();
            Arrays.asList(methods).stream().forEach(x-> System.out.println(x));
            System.out.println("***************獲取所有的方法,包括私有的*******************");
            Method[] declaredMethods = aClass.getDeclaredMethods();
            Arrays.asList(declaredMethods).stream().forEach(x-> System.out.println(x));
            System.out.println("***************獲取公有的show1()方法*******************");
            Method show1 = aClass.getMethod("show1", String.class);
            System.out.println(show1);
            Object o = aClass.getConstructor().newInstance();
            show1.invoke(o,"網紅");
            System.out.println("***************獲取私有的show4()方法******************");
            Method show4 = aClass.getDeclaredMethod("show4", int.class);
            System.out.println(show4);
            //解除私有限定
            show4.setAccessible(true);
            //需要兩個引數,一個是要呼叫的物件(獲取有反射),一個是實參
            Object result = show4.invoke(o, 20);
            System.out.println("返回值:" + result);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

}

控制檯結果:

***************獲取所有的”公有“方法*******************
public static void com.h3c.demo.reflect.Student.main(java.lang.String[])
public java.lang.String com.h3c.demo.reflect.Student.toString()
public void com.h3c.demo.reflect.Student.show1(java.lang.String)
public void com.h3c.demo.reflect.Student.show()
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 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 static void com.h3c.demo.reflect.Student.main(java.lang.String[])
public java.lang.String com.h3c.demo.reflect.Student.toString()
public void com.h3c.demo.reflect.Student.show1(java.lang.String)
private java.lang.String com.h3c.demo.reflect.Student.show4(int)
protected void com.h3c.demo.reflect.Student.show2()
void com.h3c.demo.reflect.Student.show3()
public void com.h3c.demo.reflect.Student.show()
***************獲取公有的show1()方法*******************
public void com.h3c.demo.reflect.Student.show1(java.lang.String)
呼叫了公有、無參構造方法執行了。。。
呼叫了:公有的,String引數的show1(): s = 網紅
***************獲取私有的show4()方法******************
private java.lang.String com.h3c.demo.reflect.Student.show4(int)
呼叫了,私有的,並且有返回值的,int引數的show4(): age = 20
返回值:abcd

5. 反射main方法

package com.h3c.demo.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestMain {
    public static void main(String[] args) {
        try {
            //獲取class物件
            Class<?> aClass = Class.forName("com.h3c.demo.reflect.Student");
            //2、獲取main方法
            Method main = aClass.getMethod("main", String[].class);
            //3、呼叫main方法
            //第一個引數,物件型別,因為方法是static靜態的,所以為null可以,第二個引數是String陣列
            main.invoke(null, (Object)new String[]{"a","b","c"});
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

控制檯結果:

main執行了

6. 通過反射執行配置檔案內容

package com.h3c.demo.reflect;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 我們利用反射和配置檔案,可以使:應用程式更新時,對原始碼無需進行任何修改
 * 我們只需要將新類傳送給客戶端,並修改配置檔案即可
 */
public class TestPropertyFile {

    public static void main(String[] args) {
        try {
            //通過反射獲取Class物件
            Class<?> aClass = Class.forName(getValue("className"));
            //2獲取show()方法
            Method method = aClass.getMethod(getValue("methodName"));
            //3.呼叫show()方法
            method.invoke(aClass.getConstructor().newInstance());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    //接收配置檔案內容
    public static String getValue(String key) throws IOException {
        //獲取配置檔案的物件
        Properties pro = new Properties();
        //獲取輸入流
        FileReader in = new FileReader("C:\\Users\\lYS1730\\Desktop\\property.txt");
        //將流載入到配置檔案物件中
        pro.load(in);
        in.close();
        //返回根據key獲取的value值
        return pro.getProperty(key);
    }
}

控制檯結果:

呼叫了公有、無參構造方法執行了。。。
is show()

7. 通過反射越過泛型檢查

package com.h3c.demo.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

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

        //	strList.add(100);
        //獲取ArrayList的Class物件,反向的呼叫add()方法,新增資料
        //得到 strList 物件的位元組碼 物件
        Class listClass = strList.getClass();
        //獲取add()方法
        Method m = listClass.getMethod("add", Object.class);
        //呼叫add()方法
        m.invoke(strList,true);
        m.invoke(strList, 100);

        //遍歷集合
        for(Object obj : strList){
            System.out.println(obj);
        }
    }
}

控制檯結果:

aaa
bbb
true
100