談?wù)凧ava的反射

什么是動態(tài)語言

我們都知道Java是動態(tài)語言,但什么是動態(tài)語言呢?大致認(rèn)同的定義是:

程序運(yùn)行時,允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動態(tài)語言。

從這個觀點(diǎn)看,Perl,Python,Ruby是動態(tài)語言,C++,Java,C#不是動態(tài)語言。


什么是反射

JAVA有著一個非常突出的動態(tài)相關(guān)機(jī)制:Reflection(反射)。先看一下反射的概念:

在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機(jī)制。

換句話說,Java程序可以加載一個運(yùn)行時才得知名稱的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。


反射機(jī)制能做什么

  • 在運(yùn)行時判斷任意一個對象所屬的類
  • 在運(yùn)行時構(gòu)造任意一個類的對象;
  • 在運(yùn)行時判斷任意一個類所具有的成員變量和方法;
  • 在運(yùn)行時調(diào)用任意一個對象的方法;
  • 生成動態(tài)代理

Class

Class 類繼承自O(shè)bject,其實(shí)體用以表達(dá)Java程序運(yùn)行時的classes和interfaces,也用來表達(dá)enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關(guān)鍵詞void。

當(dāng)一個class被加載,或當(dāng)加載器(class loader)的defineClass()被JVM調(diào)用,JVM 便自動產(chǎn)生一個Class 對象。

Class是Reflection故事起源。針對任何您想探勘的類,唯有先為它產(chǎn)生一個Class 對象,接下來才能經(jīng)由后者喚起為數(shù)十多個的Reflection APIs。


獲取Class三種方式

1. Object類中的getClass()
Date date = new Date();
Class c = date.getClass();
2. static method-Class.forName()
Class c = Class.forName("java.util.Date");
3. T.class
Class c = Date.class;

創(chuàng)建對象的五種方式

1. 使用new(最常用)
Date d = new Date();
2. 使用Class類的newInstance方法

newInstance方法調(diào)用無參的構(gòu)造函數(shù)創(chuàng)建對象

Date d =  Date.class.newInstance();
3. 使用Constructor類的newInstance方法

java.lang.reflect.Constructor類里也有一個newInstance方法可以創(chuàng)建對象。
可以通過這個newInstance方法調(diào)用有參數(shù)的和私有的構(gòu)造函數(shù)。

Constructor<Date> constructor= Date.class.getConstructor();
Date d = constructor.newInstance();
4. 使用clone

用clone方法創(chuàng)建對象并不會調(diào)用任何構(gòu)造函數(shù)。
要使用clone方法,我們需要先實(shí)現(xiàn)Cloneable接口并實(shí)現(xiàn)其定義的clone方法。

Date d1 = new Date();
Date d2 = (Date)d1.clone();
5. 使用反序列化
ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:/user.obj"));
User userTemp = (User) is.readObject();

Reflection API

Class的方法

1. 獲取Field

Field[] getFields() 獲取所有public域
Field[] getDeclaredFields() 獲取所有域

Class c = Class.forName("java.util.Date");
Field[] fields0 = c.getFields();
Field[] fields1 = c.getDeclaredFields();

Field getField(String name) 獲取指定名稱的域
Field getDeclaredField(String name) 獲取指定名稱的域

Field的方法

Object get(Object obj) 返回obj對象中用Field對象表示的域值
void set(Object obj,Object newValue) 設(shè)置Obj對象中Field對象表示的域的值

User user = new User("001","winson","w5566");
Class c = user.getClass();
Field[] fields =  c.getDeclaredFields();
for (Field field: fields) {
    field.setAccessible(true);
    Object object = field.get(user); //獲取user對象中Field對象表示的域的對象
    System.out.println(object.toString());
    field.set(user,new String("test")); //設(shè)置user對象中Field對象表示的域的值
}
System.out.println(user);

2. 獲取Method

Method[] getMethods() 獲取所有public方法
Method[] getDeclaredMethods() 獲取所有域

Class c = Class.forName("java.util.Date");
Method[] methods0 = c.getMethods();
Method[] methods1 = c.getDeclaredMethods();

3. 獲取Constructor

Constructor[] getConstructors() 獲取所有public構(gòu)造器
Constructor[] getDeclaredConstructors() 獲取所有構(gòu)造器

Class c = Class.forName("java.util.Date");
Constructor[] constructors0 = c.getConstructors();
Constructor[] constructors1 = c.getDeclaredConstructors();

Field、Method、Constructor的通用的方法

1. 獲取Class對象

Class getDeclaringClass()

2.獲取方法或構(gòu)造器拋出的異常類型 (Contructor|Method)

Class[] getExceptionTypes()

3. 返回修飾權(quán)限的整型值,使用MOdifier類可以分析這個返回值

int getModifiers()

4. 返回一個用于描述構(gòu)造器、方法或域名的字符串

String getName()

5. 返回一個用于描述參數(shù)類型的Class對象數(shù)組 (Contructor|Method)

Class[] getParameterTypes()

5. 返回一個用于描述返回類型的的Class對象 (Method)

Class getReturnTypes()


Modifier的方法

image
public class ReflectTestDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("java.util.Date");
        System.out.println(c.getModifiers());   //輸出1
        System.out.println(Modifier.toString(c.getModifiers()));    //輸出public

        Method[] methods = c.getDeclaredMethods();
        for (Method m: methods) {
            Class aClass= m.getDeclaringClass();
            Class[] exceptionClass = m.getExceptionTypes();     //獲取拋出的異常類型
            System.out.println(Modifier.toString(m.getModifiers()));   //獲取方法的權(quán)限
            String name = m.getName();  //獲取方法名
            Class[] parameterClass = m.getParameterTypes(); //獲取參數(shù)的類型
            Class returnClass = m.getReturnType();  //獲取返回值類型
        }
    }
}

AccessibleObject的方法

1. 為反射對象設(shè)置可訪問標(biāo)志,flag為true表明屏蔽Java語言的訪問檢查,使私有屬性也可以被查詢或設(shè)置。

void setAccessible(boolean flag)

2.返回反射對象的可訪問標(biāo)準(zhǔn)的值

boolean isAccessible()

3.設(shè)置對象數(shù)組可訪問標(biāo)志的快捷方法

static void setAccessible(AccessibleObject[] array,boolean flag)

Class c0 = Class.forName("java.util.Date");
Field[] fields =  c.getDeclaredFields();
for (Field field: fields) {
    field.setAccessible(true);  //如果不設(shè)置Accessible為true,則下面代碼會拋出IllegalAccessException異常
    field.set(user,new String("test")); 
}

Method的invoke方法

調(diào)用對象所描述的方法,傳給定參數(shù),并返回方法的返回值。
在使用包裝器傳遞基本類型的值時,基本類型的返回值必須是未包裝的

public Object invoke(Object implicitParameter,Object[] explicitParamenters)

第一個參數(shù)是隱式參數(shù)傳入被獲取方法的對象,如果是靜態(tài)方法則傳入null

public class MethodDemo {
    public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method0 = MethodDemo.class.getMethod("square", double.class);
        Method method1 = Math.class.getMethod("sqrt", double.class);
        double d0 = (Double)method0.invoke(null,3.3);   //如果是static第一個參數(shù)可以為null
        double d1 = (Double)method1.invoke(null,4.4);
        System.out.println(d0);
        System.out.println(d1);

        User user = new User("007","Winson","w5566");
        Method method2 = user.getClass().getMethod("getName");
        String name = (String)method2.invoke(user,new Object[0]);   //如果沒有參數(shù),則傳入new Object[0]
        System.out.println(name);
    }
    public static double square(double x){
        return x*x;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,753評論 18 399
  • 一、概述 Java反射機(jī)制定義 Java反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類中的所有屬性和方法...
    CoderZS閱讀 1,699評論 0 25
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,654評論 19 139
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful 2、Retrofit解析...
    隔壁老李頭閱讀 4,804評論 2 12
  • 今天,我和媽媽看了一個故事。里面講的是一個熊孩子在飛機(jī)上惹怒了一個日本小伙兒,那個小伙兒就對熊孩子的爸爸說...
    娜娜愛林林閱讀 238評論 0 0

友情鏈接更多精彩內(nèi)容