Java中的反射

Java反射API

Java反射指的是在運行狀態時,可以獲取類的屬性和方法或者修改類運行時行爲的過程。java

java.lang.Class類提供了不少方法用於獲取元數據、檢查和改變類運行時的行爲。spring

Java的反射主要涉及java.lang和java.lang.reflect包下的類。數組

反射應用場景舉例

  1. IDE, 如Eclipse、MyEclipse、NetBeans等;
  2. 調試器;
  3. 測試工具等;
  4. 各大框架、spring、hibernate等;

java.lang.Class類

java.lang.Class主要提供瞭如下兩個功能:框架

  1. 提供方法用於訪問運行期間類的元數據;
  2. 提供方法用於檢查和修改類的運行時行爲;

java.lang.Class類經常使用方法

Method Description
1) public String getName() 返回類名
2) public static Class forName(String className)throws ClassNotFoundException 加載類並返回Class對象
3) public Object newInstance()throws InstantiationException,IllegalAccessException 建立實例對象
4) public boolean isInterface() 判斷是不是接口
5) public boolean isArray() 判斷是不是數組
6) public boolean isPrimitive() 判斷是不是原始數據類型
7) public Class getSuperclass() 返回父類Class引用
8) public Field[] getDeclaredFields()throws SecurityException 返回類的成員屬性字段數組
9) public Method[] getDeclaredMethods()throws SecurityException 返回類的方法數組
10) public Constructor[] getDeclaredConstructors()throws SecurityException 返回類的構造方法數組
11) public Method getDeclaredMethod(String name,Class[] parameterTypes)throws NoSuchMethodException,SecurityException 返回類中指定參數類型的方法

怎樣獲取Class對象

有三種方式,以下:工具

  1. Class類的forName()方法,動態加載,運行時,開始裝入類, 並作類的靜態初始化
  2. 對象的getClass()方法,靜態加載(編譯時已加載)
  3. .class語法, 靜態加載(編譯時已加載)

forName()方法示例測試

可用於動態加載,當你知道類的全限定名時,可使用該方式。注意原始數據類型不適用該方法;this

package tmp;

class Simple
{
}

public class Test
{
    public static void main(String args[]) throws ClassNotFoundException
    {
        Class<?> c = Class.forName("tmp.Simple");
        System.out.println(c.getName());
        System.out.println(c.getSimpleName());
    }
}
tmp.Simple
Simple

getClass()方法示例:spa

從實例對象中獲取Class對象hibernate

package tmp;

class Simple
{
}

public class Test
{
    void printName(Object obj)
    {

    }

    public static void main(String args[])
    {
        Simple s = new Simple();
        Class<? extends Object> c = s.getClass();
        System.out.println(c.getName());
        System.out.println(c.getSimpleName());
    }
}
tmp.Simple
Simple

.class語法示例翻譯

做用於類名上,也可應用於原始數據類型,以下所示:

package tmp;

public class Test
{
    public static void main(String args[])
    {
        Class<Boolean> c = boolean.class;
        System.out.println(c.getName());

        Class<Test> c2 = Test.class;
        System.out.println(c2.getName());
    }
}
boolean
tmp.Test

判斷Class對象對應的類型

如下方法可用於判斷Class對象對應的類型:

1) public boolean isInterface(): 是否對應接口
2) public boolean isArray(): 是否對應數組
3) public boolean isPrimitive(): 是否對應原始數據類型

代碼示例:

package tmp;

class Simple
{
}

interface My
{
}

public class Test
{
    public static void main(String args[])
    {
        try
        {
            Class<?> c = Class.forName("tmp.Simple");
            System.out.println(c.isInterface());

            Class<?> c2 = Class.forName("tmp.My");
            System.out.println(c2.isInterface());

        }
        catch (Exception e)
        {
            System.out.println(e);
        }

    }
}
false
true

經過反射建立實例對象

有兩種方式,以下:

  1. 經過Class對象的newInstance()方法建立,這種方式只能調用無參構造方法;
  2. 經過Constructor對象的newInstance()方法建立,這種方式適用於有參構造方法,而且還能夠破壞單例模式,調用私有構造方法;

因此,一般來說,第二種方式比第一種使用範圍更廣。

Class對象調用newInstance()方法示例

package tmp;

class Simple
{
    void message()
    {
        System.out.println("Hello Java");
    }
}

public class Test
{
    public static void main(String args[])
    {
        try
        {
            Class<?> c = Class.forName("tmp.Simple");
            Simple s = (Simple) c.newInstance();
            s.message();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }

    }
}
Hello Java

Constructor對象調用newInstance()方法示例

注意這裏能夠根據傳入參數的類型來獲得指定的構造方法,還能夠改變構造方法的訪問權限限制。

package tmp;

import java.lang.reflect.Constructor;

class Simple
{
    private String msg;
    void message()
    {
        System.out.println("Hello Java," + msg);
    }
    private Simple(String s){
        this.msg = s;
    }
}

public class Test
{
    public static void main(String args[])
    {
        try
        {
            Class<?> c = Class.forName("tmp.Simple");
            Constructor<?> con = c.getDeclaredConstructor(String.class);
            con.setAccessible(true);
            Simple s = (Simple) con.newInstance("...");
            s.message();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }

    }
}
Hello Java,...

經過反射調用私有方法

經過反射,咱們能夠調用其它類的私有方法,主要涉及java.lang.Class和java.lang.reflect.Method類;

其中主要是用到了Method類的setAccessible方法和invoke方法,前者修改訪問權限,後者調用方法。

經過調用有參私有方法示例:

package tmp;

import java.lang.reflect.Method;

class A
{
    private void cube(int n)
    {
        System.out.println(n * n * n);
    }
}

class Test
{
    public static void main(String args[]) throws Exception
    {
        Class<A> c = A.class;
        Object obj = c.newInstance();

        Method m = c.getDeclaredMethod("cube", new Class[]{ int.class });
        m.setAccessible(true);
        m.invoke(obj, 4);
    }
}

關於javap工具

使用javap命令能夠反彙編java的字節碼文件,展現class文件中的字段屬性、構造方法、普通方法信息;

使用說明:

javap java.lang.Object示例

javap -c Test示例:

寫個簡單的Test類,以下:

package tmp;

class Simple
{

}

public class Test
{
    public static void main(String args[])
    {
        System.out.println("Hello");

    }
}

輸入javap -c Test:

 參考資料

基本屬於翻譯,作了小部分修改

http://www.javatpoint.com/java-reflection

相關文章
相關標籤/搜索