java反射機制

  那麼首先,就從本身栽的跟斗上開始吧,本身在剛學java的時候,沒怎麼用過java的框架,因此看到書上對java反射的描述,也只是把這個跟其餘的知識點同樣做爲一個新的知識點,在腦子裏有了一個印象。那個時候並不知道java反射機制有什麼做用,只知道java反射機制能夠破壞單例模式。到了後來接觸的多了,才發現java反射機制真的是一種特別有用的東西,咱們所知的spingMVC、struts2等框架的底層實現就運用了反射機制。java

      先來看一下java反射機制的概念,百度百科裏面給出的是:JAVA反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。從概念上咱們就能夠知道,爲何java反射機制是衆多java框架的基石之一了。最多見的咱們在配置servlet的時候,要給定servlet實現類的全名,而後再配置這個servlet處理哪些url請求。這裏當咱們的web應用接受到的請求與url-pattern相匹配時,就知道要將這個請求交與咱們配置的servlet來進行處理,可是咱們在web.xml中的配置只是配置了一個處理該請求的類的名字,web應用卻能經過咱們的配置來實現用該類處理相應的請求。正是經過java反射機制,咱們的應用纔可以根據咱們的配置對於各類不一樣的請求生成相應的servlet對象並調用正確的處理方法。各類監聽器、過濾器的配置也是如此。還有咱們常用的jdbc,由於java給各數據庫廠商提供了統一接口,並無具體實現,咱們在使用jdbc時,java程序並不知道咱們所用的數據庫是哪一種類型,因此咱們在使用jdbc時首先就要加載數據庫的驅動(雖然JDBC4以後能夠經過classpath下的jar包自動加載驅動了),Class.forName("DriverName"),這句話就是告訴java程序咱們所使用的驅動是哪種,是oracle仍是mysql。那麼爲何執行了這句代碼後,虛擬機就知道咱們使用的是哪種驅動程序呢。Class.forName("DriverName")執行後jvm會查找並加載名爲DriverName的類並返回該類的一個類對象,也就是說jvm會執行該類的靜態代碼段,而無論oracle仍是mysql全部Driver類的靜態代碼塊中都有java.sql.DriverManager.registerDriver(new Driver()); 這樣一句代碼,就是在類加載時將本身的一個驅動類對象註冊到驅動管理器中。因此Class.forName("DriverName")這句代碼能夠實現驅動類的加載。
mysql

  接下來看一看java反射機制是如何實現的呢。要想實現java反射,咱們須要掌握Class、Constructor、Field、Method這四個類的用法。顧名思義,class表明的是類對象,Constructor表明的是構造器對象,Field是類的屬性對象,Method是類的方法對象。看到這四個類咱們就能知道爲何說java反射能夠在運行時知道任意一個類(對象)的全部屬性和方法。web

  •   Class

   Clas類是反射中最核心的類,也是反射實現的起點。Class自己表示一個類的自己,經過Class能夠完整地獲得一個類中的完整結構,包括此類中的方法定義、屬性定義等。獲取Class對象的方式有三種:sql

    •   經過Object類的getClass()方法獲取。
    •        經過Class類的靜態方法forName(String className)獲取。
    •        經過「類.class」來獲取。

 

package reflect;

public class ClassTest {
    public static void getClassMethod1(){
        ClassTest t = new ClassTest();
        Class clazz = t.getClass();
        System.out.println("方法1獲取的類對象對應的類名是"+clazz.getName());
    }
    public static void getClassMethod2(){
        Class clazz = ClassTest.class;
        System.out.println("方法2獲取的類對象對應的類名是"+clazz.getName());
    }
    public static void getClassMethod3() throws ClassNotFoundException{
        Class clazz = Class.forName("reflect.ClassTest");
        System.out.println("方法3獲取的類對象對應的類名是"+clazz.getName());
    }
    public static void main(String[] args) throws ClassNotFoundException {
        getClassMethod1();
        getClassMethod2();
        getClassMethod3();
    }
}

輸出結果爲:數據庫

經過Class對象能獲取Constructor、Field、Method這三個其餘類的對象。數組

  •  獲取Constructor
    •  Constructor getConstructor(Class[] params) -- 得到使用特殊的參數類型的公共構造函數
    •  Constructor[] getConstructors() -- 得到類的全部公共構造函數
    •  Constructor getDeclaredConstructor(Class[] params) -- 得到使用特定參數類型的構造函數(與接入級別無關)
    •  Constructor[] getDeclaredConstructors() -- 得到類的全部構造函數(與接入級別無關)
package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

@SuppressWarnings({"all"})
public class ClassGetConstructor {
    public int i;
    private static final Class clazz = ClassGetConstructor.class;
    public ClassGetConstructor(int i){
        this.i = i;
    }
    private ClassGetConstructor(){}
    public static void getConstructorTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("getConstructorTest測試結果:");
        //獲取使用指定參數類型的公共構造參數,由於是獲取公共方法這裏會拋出權限異常SecurityException
        Constructor c1 = clazz.getConstructor(Integer.TYPE);
        //這裏使用Integer.class會報異常,Integer.TYPE表示int類的Class對象,Integer.class表示Integer類的Class對象
        ClassGetConstructor t1 = (ClassGetConstructor) c1.newInstance(1);
        System.out.println(t1.i);
        Constructor c2 = clazz.getConstructor(null);
        //這裏會報異常,由於無參的構造方法是private的,但報出的異常是 java.lang.NoSuchMethodException,卻不是SecurityException
        ClassGetConstructor t2 = (ClassGetConstructor) c2.newInstance();
        System.out.println(t2.i);
    }
    
    public static void getConstructorsTest(){
        System.out.println("getConstructorsTest測試結果:");
        Constructor[] cs = clazz.getConstructors();
        //這裏只能獲取到有參的構造方法
        for(Constructor c : cs)
            System.out.println(c);
    }
    public static void getDeclaredConstructorTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("getDeclaredConstructorTest測試結果:");
        Constructor c1 = clazz.getDeclaredConstructor(Integer.TYPE);
        Constructor c2 = clazz.getDeclaredConstructor();
        System.out.println(c1);
        ClassGetConstructor t1 = (ClassGetConstructor) c1.newInstance(1);
        System.out.println(t1.i);
        System.out.println(c2);
        ClassGetConstructor t2 = (ClassGetConstructor) c2.newInstance();
        System.out.println(t2.i);
    }
    public static void getDeclaredConstructorsTest(){
        System.out.println("getDeclaredConstructorsTest測試結果:");
        Constructor[] cs = clazz.getDeclaredConstructors();
        for(Constructor c : cs)
            System.out.println(c);
    }
    public static void main(String[] args) throws Exception{
        try {
            getConstructorTest();
        } catch (Exception e) {
            e.printStackTrace();
        }
        getConstructorsTest();
        getDeclaredConstructorTest();
        getDeclaredConstructorsTest();
    }
}

輸出結果:oracle

  •  獲取Field
    • Field getField(String name) -- 得到指定命名的公共字段
    • Field[] getFields() -- 得到類的全部公共字段
    • Field getDeclaredField(String name) -- 得到類聲明的指定命名的字段
    • Field[] getDeclaredFields() -- 得到類聲明的全部字段
package reflect;

import java.lang.reflect.Field;

public class ClassGetField {
    private String s;
    private static int j;
    public static int i;
    public ClassGetField classGetField;
    public static Class clazz = ClassGetField.class;
    public static void getFieldTest() throws NoSuchFieldException, SecurityException{
        System.out.println("getFieldTest測試結果:");
        try {
            System.out.println(clazz.getField("s"));//報異常
        } catch (Exception e) {
            System.out.println("獲取屬性s失敗!");
//            e.printStackTrace();
        }
        System.out.println(clazz.getField("i"));
        try {
            System.out.println(clazz.getField("j"));//報異常
        } catch (Exception e) {
            System.out.println("獲取屬性j失敗!");
//            e.printStackTrace();
        }
        System.out.println(clazz.getField("classGetField"));
        System.out.println(clazz.getField("clazz"));
    }
    public static void getFieldsTest(){
        System.out.println("getFieldsTest測試結果:");
        Field[] fs = clazz.getFields();
        for (Field f : fs){
            System.out.println(f);
        }
    }
    public static void getDeclaredFieldTest() throws NoSuchFieldException, SecurityException{
        System.out.println("getDeclaredFieldTest測試結果:");
        System.out.println(clazz.getDeclaredField("s"));
        System.out.println(clazz.getDeclaredField("i"));
        System.out.println(clazz.getDeclaredField("j"));
        System.out.println(clazz.getDeclaredField("classGetField"));
        System.out.println(clazz.getDeclaredField("clazz"));
    }
    public static void getDeclaredFieldsTest(){
        System.out.println("getDeclaredFieldsTest測試結果:");
        Field[] fs = clazz.getDeclaredFields();
        for (Field f : fs){
            System.out.println(f);
        }
    }
    public static void main(String[] args) throws Exception{
        getFieldTest();
        getFieldsTest();
        getDeclaredFieldTest();
        getDeclaredFieldsTest();
    }
}

輸出結果:框架

 

  • 獲取Method對象
    • Method getMethod(String name, Class[] params) -- 使用特定的參數類型,得到命名的公共方法
    • Method[] getMethods() -- 得到類的全部公共方法
    • Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,得到類聲明的命名的方法
    • Method[] getDeclaredMethods() -- 得到類聲明的全部方法
package reflect;

public class ClassGetMethodParent {
    public void Method(int i , int j){}
    void Method(int i , int j,int k){}
}

package reflect;
import java.lang.reflect.Method;
public class ClassGetMethod extends ClassGetMethodParent{
    public void Method(String s){}
    void Method(int i){}
    public void Method(){}
    public void Method(String s , int i){}
    public static Class clazz = ClassGetMethod.class;
    
    public static void getMethodTest() throws NoSuchMethodException, SecurityException{
        System.out.println("getMethodTest測試結果:");
        Method m1 = clazz.getMethod("Method", new Class[]{String.class});
        System.out.println(m1);
        try {
            Method m2 = clazz.getMethod("Method", new Class[]{Integer.TYPE});
            System.out.println(m2);
        } catch (Exception e) {
            System.out.println("獲取方法Method(int i)失敗");
        }
        Method m3 = clazz.getMethod("Method", new Class[]{});//參數類型也可寫做null
        System.out.println(m3);
        Method m4 = clazz.getMethod("Method", new Class[]{String.class,Integer.TYPE});
        System.out.println(m4);
        Method m5 = clazz.getMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE});//父類方法
        System.out.println(m5);
        try {
            Method m6 = clazz.getMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE,Integer.TYPE});//父類方法
            System.out.println(m6);
        } catch (Exception e) {
            System.out.println("獲取父類方法方法Method(int i,int j, int k)失敗");
        }
    }
    public static void getMethodsTest(){
        //這裏會獲得父類繼承的公共方法
        System.out.println("getMethodsTest測試結果:");
        Method[] methods = clazz.getMethods();
        for (Method m : methods){
            System.out.println(m);
        }
    }
    public static void getDeclaredMethodTest() throws NoSuchMethodException, SecurityException{
        System.out.println("getDeclaredMethodTest測試結果:");
        Method m1 = clazz.getDeclaredMethod("Method", new Class[]{String.class});
        System.out.println(m1);
        try {
            Method m2 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE});
            System.out.println(m2);
        } catch (Exception e) {
            System.out.println("獲取方法Method(int i)失敗");
        }
        Method m3 = clazz.getDeclaredMethod("Method", new Class[]{});//參數類型也可寫做null
        System.out.println(m3);
        Method m4 = clazz.getDeclaredMethod("Method", new Class[]{String.class,Integer.TYPE});
        System.out.println(m4);
        try {
            Method m5 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE});//父類方法
            System.out.println(m5);
        } catch (Exception e1) {
            System.out.println("獲取父類方法方法Method(int i,int j)失敗");
        }
        try {
            Method m6 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE,Integer.TYPE});//父類方法
            System.out.println(m6);
        } catch (Exception e) {
            System.out.println("獲取父類方法方法Method(int i,int j, int k)失敗");
        }
    }
    public static void getDeclaredMethodsTest(){
        System.out.println("getDeclaredMethodsTest測試結果:");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method m : methods){
            System.out.println(m);
        }
    }
    public static void main(String[] args) throws Exception{
        getMethodTest();
        getMethodsTest();
        getDeclaredMethodTest();
        getDeclaredMethodsTest();
    }
}

輸出結果:jvm

 

從結果中能夠看出使用獲取類的公共方法的方法時(getMethod與getMethods)能夠獲取該類從父類繼承的全部共有方法,而使用getDeclaredMethod與getDeclaredMethods方法時只能獲取該類本身聲明的全部方法。這一點前面獲取Constructor、Field也是如此。class對象經常使用的方法還有newInstance(),該方法返回一個類的實例對象,調用的是類的無參構造函數。函數

  • Constructor

  當咱們使用class對象的newInstance方法來生成一個類的實例對象時,若是該類沒有默認的無參構造函數,咱們就不能使用class對象的newInstance方法了。或者咱們須要使用指定的構造函數來生成類的實例對象,這個時候咱們可使用Constructor類來實現。

  經過Constructor類的newInstance方法咱們可使用指定的構造方法來生成實例對象,Constructor類的newInstance與class類的newInstance不一樣之處在於:Constructor類的newInstance的參數爲Class對象數組,表示指定構造器的參數類型,而class類的newInstance方法沒有方法參數,默認調用類的無參構造器。

package reflect;

import java.lang.reflect.Constructor;

public class ConstructorTest {
    public int i;
    public static final Class clazz = ConstructorTest.class;
    public ConstructorTest(int i ){
        this.i = i;
    }
//    public ConstructorTest(){}
    public static void main(String[] args) throws Exception {
        Constructor c1 = clazz.getConstructor(new Class[]{int.class});
        ConstructorTest test1 = (ConstructorTest) c1.newInstance(1);
        System.out.println(test1.i);
        ConstructorTest test2 = (ConstructorTest) clazz.newInstance();
    }
}

測試結果:

 Constructor類比較簡單,能夠當作是對class類的newInstance方法的一個補充吧,經常使用的方法就是newInstance。

  • Field

  Field類表明某個類中一個成員變量,咱們經過class類以及Constructor類得到了某個類的實例後,若是想要進一步的對該實例對象進行控制,例如給實例對象的一些成員變量進行賦值等等,這個時候能夠用到Field類。這個時候咱們不由要問,既然咱們已經獲取到了該實例對象,那麼咱們能夠直接操做該對象,爲何還要用反射來進行成員變量的設置呢?咱們本身在寫代碼的時候獲取到了實例對象,在IDE環境中點一下就會出現該對象的方法和全部屬性,可是在jvm中獲取到了實例對象後若是要堆該對象的成員變量進行操做就只能依賴於Field類。

  獲取到了Field對象後能夠調用Field類的set方法對成員變量進行賦值,set方法的參數有兩個,第一個爲要操做的實例對象,第二個爲要賦的值。對於private的成員變量操做若是不是在該實例對象的類的內部進行的話是會拋出異常的,這種狀況下須要調用Field類的setAccessible方法,傳入參數爲boolean類型,這裏使用true的話表示暴力訪問。

package reflect;

import java.lang.reflect.Field;

public class FieldTest {
    private  String s = "111";
    private int i = 0;
    public String toString(){
        return "s: " + s + "  i: " + i ;
    }
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        FieldTest test1 = new FieldTest();
        Field str=FieldTest.class.getDeclaredField("s");
        str.setAccessible(true);  //若是是私有字段,要先將該私有字段進行取消權限檢查的能力。也稱暴力訪問
        str.set(test1, "222");
        System.out.println(test1.s);
        Field intf=FieldTest.class.getDeclaredField("i");
        intf.set(test1, 333);
        System.out.println(test1.i);
    }
}

能夠看到 在類的內部咱們使用Field來設置成員變量的值,不管成員變量的訪問修飾符是什麼,咱們均可以直接調用set方法來進行設置。可是在類的外部,對private的成員變量的操做前必須調用setAccessible(true)方法。

package reflect;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        FieldTest test1 = new FieldTest();
        Field str=FieldTest.class.getDeclaredField("s");
        str.setAccessible(true);  //若是是私有字段,要先將該私有字段進行取消權限檢查的能力。也稱暴力訪問
        str.set(test1, "222");
        System.out.println(test1);
        Field intf=FieldTest.class.getDeclaredField("i");
        intf.set(test1, 333);
        System.out.println(test1);
    }
}

除了最經常使用的set方法覺得,還有get方法,即得到當前的屬性值,getType方法獲取屬性的類型,以及針對基本數據類型的setInt、setFloat、setBoolean方法等。

  • Method

  Method類表示類中的成員方法,每一個方法都由 修飾符、返回值、參數、註解和拋出的異常組成。這個在咱們前面的class對象獲取Method對象的測試中能夠看到,當咱們打印Method對象時,會將這幾個部分都打印出來。

  Method類最經常使用的方法是invoke方法,該方法接受兩個參數,一個是執行該方法的對象,一個是傳入的參數數組。一樣的對於private的方法,咱們進行反射調用時須要調用setAccessible方法。

package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressWarnings({"all"})
public class MethodTest {
    private void Method(){
        System.out.println("private void Method");
    }
    public void Method(int i,int j){
        System.out.println("public void Method("+ i + j +")");
    }
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method m1 = MethodTest.class.getDeclaredMethod("Method", null);
        m1.invoke(new MethodTest(), null);
        
        Method m2 = MethodTest.class.getDeclaredMethod("Method", new Class[]{int.class,int.class});
        m2.invoke(new MethodTest(), new Object[]{1,1});
    }
}

在類中,不管是private仍是public均可以直接經過反射調用。

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception{
//        FieldTest test1 = new FieldTest();
//        Field str=FieldTest.class.getDeclaredField("s");
//        str.setAccessible(true);  //若是是私有字段,要先將該私有字段進行取消權限檢查的能力。也稱暴力訪問
//        str.set(test1, "222");
//        System.out.println(test1);
//        Field intf=FieldTest.class.getDeclaredField("i");
//        intf.set(test1, 333);
//        System.out.println(test1);
        testMethod();
    }
    public static  void testMethod() throws Exception{
        Method m1 = MethodTest.class.getDeclaredMethod("Method", null);
        m1.setAccessible(true);
        m1.invoke(new MethodTest(), null);
        
        Method m2 = MethodTest.class.getDeclaredMethod("Method", new Class[]{int.class,int.class});
        m2.invoke(new MethodTest(), new Object[]{1,1});
    }
}

在其餘類中,若是經過反射調用private方法必須調用setAccessible(true)來避開權限檢查。

相關文章
相關標籤/搜索