java反射機制詳解


一、什么是反射

JAVA反射機制是在運行狀態中,對於任何一個類,都能夠知道這個類的所有屬性和方法;對於任何一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。

想要使用反射機制,就必須要先獲取到該類的字節碼文件對象(.class),通過字節碼文件對象,就能夠通過該類中的方法獲取到我們想要的所有信息(方法,屬性,類名,父類名,實現的所有接口等等),每一個類對應着一個字節碼文件也就對應着一個Class類型的對象,也就是字節碼文件對象。

獲取字節碼文件對象(獲取class對象的方式)的三種方式:

1、根據類名:類名.class

2、根據對象:對象.getClass()

3、根據全限定類名:Class.forName(全限定類名)

二、通過反射機制獲取信息

1、構造函數

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//先獲取有參構造,parameterTypes:表述參數列表,也可以不寫
Constructor constructor = classs.getConstructor(int.class,String.class,int.class,String.class);
//通過構造器來實例化對象,將實際的參數傳進去
User user = (User) constructor.newInstance(01,"小軒",13,"打球");

獲取全部構造函數:

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取所有構造函數
Constructor constructor[] = classs.getConstructors();
//遍歷所有構造函數
for(int i=0;i<constructor.length;i++){
    //獲取每個構造函數中的參數類型字節碼對象
    Class[] parameterTypes = constructor[i].getParameterTypes();
    System.out.println("第"+i+"個構造函數:");
    for (int j = 0; j < parameterTypes.length; j++) {
        System.out.println(parameterTypes[j].getName()+",");
    }
}

console:

2、屬性

在學習spring ioc之時,對未提供set方法的private屬性依然可以注入感到神奇萬分,現在看來,這神奇的根源自然是來自於java的反射,常用的方法如下:

2.1獲取指定成員變量

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取其實例對象
User user = (User) classs.newInstance();
//獲取成員變量classs.getField(name);通過name來獲取指定成員變量
//如果該成員變量是私有的,則應該使用getDeclaredField(name);
Field declaredField = classs.getDeclaredField("userName");
//因為屬性是私有的,獲得其對象后,還要讓打開可見權限
declaredField.setAccessible(true);
//對成員變量進行操作
//賦值操作
declaredField.set(user, "Richard");
System.out.println(user.getUserName());

2.2獲取全部屬性

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取其實例對象
User user = (User) classs.newInstance();
//賦值操作
user.setUserNum(01);
user.setUserName("小軒");
//將私有屬性一並獲得
Field[] fields = classs.getDeclaredFields();
//遍歷所有屬性
for (int i = 0; i < fields.length; i++) {
//打開可見權限
fields[i].setAccessible(true);
System.out.println(fields[i].get(user));
}

3、方法

3.1不帶參數的方法

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取其實例對象
User user = (User) classs.newInstance();
//不帶參數的方法,name為不帶參數的方法
/*
* classs.getMethod(name,paraMeterTypes)
* name:方法的名稱
* paraMeterTypes:方法的參數類型,沒有則什么都不填 例如:String.class 
*/
Method method = classs.getMethod("name");
//調用方法
/*
* method.invoke(obj,args)
* obj:方法的對象
* args:實際的參數值,沒有則不填
*/
method.invoke(user);

3.2帶參數的方法

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取其實例對象
// User user = (User) classs.newInstance();
//獲取帶參數的方法,為方法名
// Method method = classs.getDeclaredMethod("namess", String.class);
//設置可見性
// method.setAccessible(true);
//調用方法
// method.invoke(user, "text");

3.3獲取所有的方法

//獲取字節碼文件
Class classs = Class.forName("com.zcbq.reflect.User");
//獲取其實例對象
User user = (User) classs.newInstance();
//獲取所有的方法
Method[] methods = classs.getMethods();
//遍歷所有方法
for (Method method : methods) {
    //設置可見性
    method.setAccessible(true);
    System.out.println(method.getName());
    //獲得方法的參數
    Class<?>[] parameterTypes = method.getParameterTypes();
    for (int i = 0; i < parameterTypes.length; i++) {
        //獲得構造函數中參數類型
        System.out.println(parameterTypes[i].getName()+",");
    }
}

三、動態代理的概述及實現

1、動態代理概述

動態代理:利用Java的反射技術(Java Reflection),在運行時創建一個實現某些給定接口的新類(也稱“動態代理類”)及其實例(對象);

代理的是接口(Interfaces),不是類(Class),更不是抽象類。

2、動態代理的實現

分三步,但是注意JDK提供的代理正能針對接口做代理,也就是下面的第二步返回的必須要是一個接口。

2.1 new出代理對象,通過實現InvacationHandler接口,然后new出代理對象來。

2.2 通過Proxy類中的靜態方法newProxyInstance,來將代理對象假裝成那個被代理的對象,也就是如果叫人幫我們代買火車票一樣,那個代理就假裝成我們自己本人。

2.3 執行方法,代理成功

附屬代碼:

package com.zcbq.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHander implements InvocationHandler {
    private Object target;

    public MyInvocationHander() {
        super();
    }

    public MyInvocationHander(Object target) {
        super();
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.err.println("開始");
        method.invoke(target, args); //執行被代理的target對象的方法
        System.out.println("結束");
       return null;
    }

}

附屬代碼:

Student student = new StuImp();
MyInvocationHander m = new MyInvocationHander(student);
/**
* student.getClass().getClassLoader():類加載器
* student.getClass().getInterfaces():被代理對象的接口
* m:代理對象
*/
Student s = (Student) Proxy.newProxyInstance(student.getClass().getClassLoader(), 
student.getClass().getInterfaces(), m);
s.login();
s.logout();

注意newProxyInstance的三個參數,第一個,類加載器,第二個被代理對象的接口,第三個代理對象。


免責聲明!

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



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