Java基礎:反射機制

一、什麼是反射:

      反射是Java的特徵之一,是一種間接操作目標對象的機制,核心是JVM在運行的時候才動態加載類,並且對於任意一個類,都能夠知道這個類的所有屬性和方法,調用方法/訪問屬性,不需要提前在編譯期知道運行的對象是誰,他允許運行中的Java程序獲取類的信息,並且可以操作類或對象內部屬性。程序中對象的類型一般都是在編譯期就確定下來的,而當我們的程序在運行時,可能需要動態的加載一些類,這些類因爲之前用不到,所以沒有加載到jvm,這時,使用Java反射機制可以在運行期動態的創建對象並調用其屬性,它是在運行時根據需要才加載。

二:反射的作用

  • 在運行時判斷任意一個對象所屬的類;

  • 在運行時構造任意一個類的對象;

  • 在運行時判斷任意一個類所具有的成員變量和方法;

  • 在運行時調用任意一個對象的方法;

三、反射的優缺點:

1、優點:使用反射,我們就可以在運行時獲得類的各種內容,進行反編譯,對於Java這種先編譯再運行的語言,能夠讓我們很方便的創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代碼的鏈接,更加容易實現面向對象。

2、缺點:(1)反射會消耗一定的系統資源,因此,如果不需要動態地創建一個對象,那麼就不需要用反射;

(2)反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。

四、反射機制常用的類:

Java.lang.Class;

Java.lang.reflect.Constructor;

Java.lang.reflect.Field;

Java.lang.reflect.Method;

Java.lang.reflect.Modifier;

五:反射的實現

我們知道,要使用一個類,就要先把它加載到虛擬機中,生成一個Class對象。這個class對象就保存了這個類的一切信息。
反射機制的實現,就是獲取這個Class對象,通過Class對象去訪問類、對象的元數據以及運行時的數據。
有三種方法獲得類的Class對象:Class.forName(String className)、className.class、實例對象.getClass();

六:反射涉及的API

反射首先獲取Class對象;然後獲取Method類和Field類;最後通過Method和Field類進行具體的方法調用或屬性訪問。
1:在運行時獲取對象所屬類的類名等信息

對象名.getClass().getName();

2:通過反射機制創建class對象(三種方法)

class1 = Class.forName(className);
class2 = 對象名.getClass();
class3 = 對象名.class;

3:在運行時,通過創建class對象,獲取自己的父類信息

Class<?> clazz = Class.forName(當前類);
Class<?> parentClass = clazz.getSuperclass();
parentClass.getName();//獲得父類名

4:通過反射機制創建一個類的對象

1:反射創建class對象(見上面)
2:Classname 對象=classname.newInstance(參數);

5:獲取類的全部方法,存於一個數組中

//創建class對象
Class<?> clazz = Class.forName(ClassName);
// 返回聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。 
Method[] getDeclaredMethods();
//返回可被訪問的公共方法      
Method method[] = clazz.getMethods();

6:獲取類的全部字段,存於一個數組中

Class<?> clazz = Class.forName(classname);
// 取得本類已聲明的所有字段,包括私有的、保護的
Field[] field = clazz.getDeclaredFields();
// 取得本類中可訪問的所有公共字段
Field[] filed1 = clazz.getFields();

7:操作類/對象 的某個屬性(包括私有)

Class<?> clazz = Class.forName(classname);
//返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。 包括公共、私有、保護的字段。
Field field = clazz.getDeclaredField(字段名);
//禁用Java權限修飾符的作用,無視方法權限限制進行訪問
field.setAccessible(true);
// void set(Object obj, Object value) 將指定對象變量上此 Field 對象表示的字段設置爲指定的新值。           
field.set(該類的一個對象, 字段值);

8:調用類/對象 的某個方法(包括私有)

Class<?> clazz = Class.forName(classname);

// Method getMethod(String name, Class<?>... parameterTypes) 
//返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
//獲取一個公有函數
Method method = clazz.getMethod(方法名,參數類型);
//調用具體某個實例對象的這個公有方法
method.invoke(實例對象,參數值);

// Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
//返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。 包括私有、保護、公有方法

//獲取一個私有函數
Method private_method=class.getDeclaredMethod(函數名,參數類型);

//禁用Java權限限定符的作用,使私有函數可訪問
private_method.setAccessible(true);

//調用具體實例對象的這個方法
private_method.invoke(實例對象,參數);

例子:

Student類:共六個構造方法

package fanshe;

public class Student {
    //---------------構造方法-------------------
    //(默認的構造方法)
    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);
    }
}

測試類

package fanshe;

import java.lang.reflect.Constructor;

/*
 * 通過Class對象可以獲取某個類中的:構造方法、成員變量、成員方法;並訪問成員;
 *
 * 1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)
 * 		2).獲取單個的方法,並調用:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
 * 		3).調用構造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class Constructors {

    public static void main(String[] args) throws Exception {
        //1.加載Class對象
        Class clazz = Class.forName("fanshe.Student");

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

        System.out.println("************所有的構造方法(包括:私有、受保護、默認、公有)***************");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("*****************獲取公有、無參的構造方法*******************************");
        Constructor con = clazz.getConstructor(null);
        //1>、因爲是無參的構造方法所以類型是一個null,不寫也可以:這裏需要的是一個參數的類型,切記是類型
        //2>、返回的是描述這個無參構造函數的類對象。
        System.out.println("con = " + con);

        //調用構造方法
        Object obj = con.newInstance();
        //	System.out.println("obj = " + obj);
        //	Student stu = (Student)obj;

        System.out.println("******************獲取私有構造方法,並調用*******************************");
        con = clazz.getDeclaredConstructor(char.class);
        System.out.println(con);
        //調用構造方法
        con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
        obj = con.newInstance('男');
    }
}

輸出

**********************所有公有構造方法*********************************
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
************所有的構造方法(包括:私有、受保護、默認、公有)***************
private fanshe.Student(int)
protected fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
fanshe.Student(java.lang.String)
*****************獲取公有、無參的構造方法*******************************
con = public fanshe.Student()
調用了公有、無參構造方法執行了。。。
******************獲取私有構造方法,並調用*******************************
public fanshe.Student(char)
姓名:男

Process finished with exit code 0

StudentNew類 獲取成員變量並調用

package fanshe;

public class StudentNew {
    public StudentNew(){

    }
    //**********字段*************//
    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 + "]";
    }

}

測試類

package fanshe;


import java.lang.reflect.Field;

/*
 * 獲取成員變量並調用:
 *
 * 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 Fields {

    public static void main(String[] args) throws Exception {
        //1.獲取Class對象
        Class stuClass = Class.forName("fanshe.StudentNew");
        //2.獲取字段
        System.out.println("************獲取所有公有的字段********************");
        Field[] fieldArray = stuClass.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");
        fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println("*************獲取公有字段**並調用***********************************");
        Field f = stuClass.getField("name");
        System.out.println(f);
        //獲取一個對象
        Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
        //爲字段設置值
        f.set(obj, "劉德華");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
        //驗證
        StudentNew stu = (StudentNew)obj;
        System.out.println("驗證姓名:" + stu.name);


        System.out.println("**************獲取私有字段****並調用********************************");
        f = stuClass.getDeclaredField("phoneNum");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "18888889999");
        System.out.println("驗證電話:" + stu);

    }
}

輸出

************獲取所有公有的字段********************
public java.lang.String fanshe.StudentNew.name
************獲取所有的字段(包括私有、受保護、默認的)********************
public java.lang.String fanshe.StudentNew.name
protected int fanshe.StudentNew.age
char fanshe.StudentNew.sex
private java.lang.String fanshe.StudentNew.phoneNum
*************獲取公有字段**並調用***********************************
public java.lang.String fanshe.StudentNew.name
驗證姓名:劉德華
**************獲取私有字段****並調用********************************
private java.lang.String fanshe.StudentNew.phoneNum
驗證電話:Student [name=劉德華, age=0, sex= , phoneNum=18888889999]

StudentTwo 獲取成員方法並調用

package fanshe;

public class StudentTwo {
    //**************成員方法***************//
    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";
    }
}

測試類

package fanshe;

import java.lang.reflect.Method;

/*
 * 獲取成員方法並調用:
 *
 * 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 MethodClass {

    public static void main(String[] args) throws Exception {
        //1.獲取Class對象
        Class stuClass = Class.forName("fanshe.StudentTwo");
        //2.獲取所有公有方法
        System.out.println("***************獲取所有的”公有“方法*******************");
        stuClass.getMethods();
        Method[] methodArray = stuClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************獲取所有的方法,包括私有的*******************");
        methodArray = stuClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************獲取公有的show1()方法*******************");
        Method m = stuClass.getMethod("show1", String.class);
        System.out.println(m);
        //實例化一個Student對象
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "劉德華");

        System.out.println("***************獲取私有的show4()方法******************");
        m = stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
        System.out.println("返回值:" + result);
    }
}

輸出

***************獲取所有的”公有“方法*******************
public void fanshe.StudentTwo.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 fanshe.StudentTwo.show1(java.lang.String)
void fanshe.StudentTwo.show3()
private java.lang.String fanshe.StudentTwo.show4(int)
protected void fanshe.StudentTwo.show2()
***************獲取公有的show1()方法*******************
public void fanshe.StudentTwo.show1(java.lang.String)
調用了:公有的,String參數的show1(): s = 劉德華
***************獲取私有的show4()方法******************
private java.lang.String fanshe.StudentTwo.show4(int)
調用了,私有的,並且有返回值的,int參數的show4(): age = 20
返回值:abcd

Process finished with exit code 0

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章