java反射機制,Java反射機制的原理和用途

本文目錄一覽:

北大青鳥java培訓:Java的反射機制?

Java反射機制是一個非常強大的功能,在很多大型項目比如Spring,Mybatis都可以看見反射的身影。

通過反射機制我們可以在運行期間獲取對象的類型信息,利用這一特性我們可以實現工廠模式和代理模式等設計模式,同時也可以解決Java泛型擦除等令人苦惱的問題。

下面java課程就從實際應用的角度出發,來應用一下Java的反射機制。

反射基礎p.s:本文需要讀者對反射機制的API有一定程度的了解,如果之前沒有接觸過的話,建議先看一下官方文檔的QuickStart。

在應用反射機制之前,首先我們先來看一下如何獲取一個對象對應的反射類Class,在Java中我們有三種方法可以獲取一個對象的反射類。

通過getClass方法在Java中,每一個Object都有一個getClass方法,通過getClass方法我們可以獲取到這個對象對應的反射類:Strings=”ziwenxie”;Class?c=s.getClass();通過forName方法我們也可以調用Class類的靜態方法forName:Class?c=Class.forName(“java.lang.String”);使用.class或者我們也可以直接使用.class:Class?c=String.class;獲取類型信息在文章開頭我們就提到反射的一大好處就是可以允許我們在運行期間獲取對象的類型信息,下面我們通過一個例子來具體看一下。

首先我們在typeinfo.interfacea包下面新建一個接口A:packagetypeinfo.interfacea;publicinterfaceA{voidf();}接着我們在typeinfo.packageaccess包下面新建一個接口C,接口C繼承自接口A,並且我們還另外創建了幾個用於測試的方法,注意下面幾個方法的權限都是不同的。

什麼是JAVA的反射機制?有什麼作用?

Java的反射(reflection)機制是指在程序的運行狀態中,可以構造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法。這種動態獲取程序信息以及動態調用對象的功能稱為Java語言的反射機制。反射被視為動態語言的關鍵

java的反射機制是什麼?

Java 的反射機制是使其具有動態特性的非常關鍵的一種機制,也是在JavaBean 中廣泛應用的一種特性。

運用JavaBean 的最常見的問題是:根據指定的類名,類字段名和所對應的數據,得到該類的實例,下面的一個例子演示了這一實現。

-|Base.java //抽象基類

|Son1.java //基類擴展1

|Son2.java //基類擴展2

|Util.java

/**

* @author metaphy

* create 2005-4-14 9:06:56

* 說明:

*/

(1)Base.java 抽象基類只是一個定義

public abstract class Base {

}

(2)Son1.java /Son2.java 是已經實現的JavaBean

public class Son1 extends Base{

private int id ;

private String name ;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public void son1Method(String s){

System.out.println(s) ;

}

}

(3)

public class Son2 extends Base{

private int id;

private double salary;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public double getSalary() {

return salary;

}

public void setSalary(double salary) {

this.salary = salary;

}

}

(4)Util.java 演示了如何根據指定的類名,類字段名和所對應的數據,得到一個類的實例

import java.lang.reflect.Method;

public class Util {

//此方法的最大好處是沒有類名Son1,Son2 可以通過參數來指定,程序裏面根本不用出現

public static Base convertStr2ServiceBean(String beanName,String fieldSetter,String paraValue){

Base base = null ;

try {

Class cls = Class.forName(beanName) ;

base = (Base)cls.newInstance() ;

Class[] paraTypes = new Class[]{String.class };

Method method = cls.getMethod(fieldSetter, paraTypes) ;

String[] paraValues = new String[]{paraValue} ;

method.invoke(base, paraValues) ;

} catch (Throwable e) {

System.err.println(e);

}

return base ;

}

public static void main(String[] args){

Son1 son1 =(Son1) Util.convertStr2ServiceBean(“trying.reflect.Son1″,”setName”,”wang da sha”);

System.out.println(“son1.getName() :”+son1.getName()) ;

}

}

//調用結果:

//son1.getName() :wang da sha

謝謝!希望能給大家一點啟發!

--------------------

附:

//下面這篇文檔來源於Internet,作者不詳

Reflection 是 Java 程序開發語言的特徵之一,它允許運行中的 Java 程序對自身進行檢查,或者說「自審」,並能直接操作程序的內部屬性。例如,使用它能獲得 Java 類中各成員的名稱並顯示出來。

Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。

JavaBean 是 reflection 的實際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過 reflection 動態的載入並取得 Java 組件(類) 的屬性。

1. 一個簡單的例子

考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

import java.lang.reflect.*;

public class DumpMethods {

public static void main(String args[]) {

try {

Class c = Class.forName(args[0]);

Method m[] = c.getDeclaredMethods();

for (int i = 0; i m.length; i++)

System.out.println(m[i].toString());

} catch (Throwable e) {

System.err.println(e);

}

}

}

按如下語句執行:

java DumpMethods java.util.Stack

它的結果輸出為:

public java.lang.Object java.util.Stack.push(java.lang.Object)

public synchronized java.lang.Object java.util.Stack.pop()

public synchronized java.lang.Object java.util.Stack.peek()

public boolean java.util.Stack.empty()

public synchronized int java.util.Stack.search(java.lang.Object)

這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。

這個程序使用 Class.forName 載入指定的類,然後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

2.開始使用 Reflection

用於 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。

下面就是獲得一個 Class 對象的方法之一:

Class c = Class.forName(“java.lang.String”);

這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:

Class c = int.class;

或者

Class c = Integer.TYPE;

它們可獲得基本類型的類信息。其中後一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段。

第二步是調用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

一旦取得這個信息,就可以進行第三步了——使用 reflection API 來操作這些信息,如下面這段代碼:

Class c = Class.forName(“java.lang.String”);

Method m[] = c.getDeclaredMethods();

System.out.println(m[0].toString());

它將以文本方式打印出 String 中定義的第一個方法的原型。

在下面的例子中,這三個步驟將為使用 reflection 處理特殊應用程序提供例證。

模擬 instanceof 操作符

得到類信息之後,通常下一個步驟就是解決關於 Class 對象的一些基本的問題。例如,Class.isInstance 方法可以用於模擬 instanceof 操作符:

class A {

}

public class instance1 {

public static void main(String args[]) {

try {

Class cls = Class.forName(“A”);

boolean b1 = cls.isInstance(new Integer(37));

System.out.println(b1);

boolean b2 = cls.isInstance(new A());

System.out.println(b2);

} catch (Throwable e) {

System.err.println(e);

}

}

}

在這個例子中創建了一個 A 類的 Class 對象,然後檢查一些對象是否是 A 的實例。Integer(37) 不是,但 new A() 是。

3.找出類的方法

找出一個類中定義了些什麼方法,這是一個非常有價值也非常基礎的 reflection 用法。下面的代碼就實現了這一用法:

import java.lang.reflect.*;

public class method1 {

private int f1(Object p, int x) throws NullPointerException {

if (p == null)

throw new NullPointerException();

return x;

}

public static void main(String args[]) {

try {

Class cls = Class.forName(“method1”);

Method methlist[] = cls.getDeclaredMethods();

for (int i = 0; i methlist.length; i++) {

Method m = methlist[i];

System.out.println(“name = ” + m.getName());

System.out.println(“decl class = ” + m.getDeclaringClass());

Class pvec[] = m.getParameterTypes();

for (int j = 0; j pvec.length; j++)

System.out.println(“param #” + j + ” ” + pvec[j]);

Class evec[] = m.getExceptionTypes();

for (int j = 0; j evec.length; j++)

System.out.println(“exc #” + j + ” ” + evec[j]);

System.out.println(“return type = ” + m.getReturnType());

System.out.println(“—–“);

}

} catch (Throwable e) {

System.err.println(e);

}

}

}

這個程序首先取得 method1 類的描述,然後調用 getDeclaredMethods 來獲取一系列的 Method 對象,它們分別描述了定義在類中的每一個方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 來代替 getDeclaredMethods,你還能獲得繼承來的各個方法的信息。

取得了 Method 對象列表之後,要顯示這些方法的參數類型、異常類型和返回值類型等就不難了。這些類型是基本類型還是類類型,都可以由描述類的對象按順序給出。

輸出的結果如下:

name = f1

decl class = class method1

param #0 class java.lang.Object

param #1 int

exc #0 class java.lang.NullPointerException

return type = int

—–

name = main

decl class = class method1

param #0 class [Ljava.lang.String;

return type = void

—–

4.獲取構造器信息

獲取類構造器的用法與上述獲取方法的用法類似,如:

import java.lang.reflect.*;

public class constructor1 {

public constructor1() {

}

protected constructor1(int i, double d) {

}

public static void main(String args[]) {

try {

Class cls = Class.forName(“constructor1”);

Constructor ctorlist[] = cls.getDeclaredConstructors();

for (int i = 0; i ctorlist.length; i++) {

Constructor ct = ctorlist[i];

System.out.println(“name = ” + ct.getName());

System.out.println(“decl class = ” + ct.getDeclaringClass());

Class pvec[] = ct.getParameterTypes();

for (int j = 0; j pvec.length; j++)

System.out.println(“param #” + j + ” ” + pvec[j]);

Class evec[] = ct.getExceptionTypes();

for (int j = 0; j evec.length; j++)

System.out.println(“exc #” + j + ” ” + evec[j]);

System.out.println(“—–“);

}

} catch (Throwable e) {

System.err.println(e);

}

}

}

這個例子中沒能獲得返回類型的相關信息,那是因為構造器沒有返回類型。

這個程序運行的結果是:

name = constructor1

decl class = class constructor1

—–

name = constructor1

decl class = class constructor1

param #0 int

param #1 double

—–

5.獲取類的字段(域)

找出一個類中定義了哪些數據字段也是可能的,下面的代碼就在干這個事情:

import java.lang.reflect.*;

public class field1 {

private double d;

public static final int i = 37;

String s = “testing”;

public static void main(String args[]) {

try {

Class cls = Class.forName(“field1”);

Field fieldlist[] = cls.getDeclaredFields();

for (int i = 0; i fieldlist.length; i++) {

Field fld = fieldlist[i];

System.out.println(“name = ” + fld.getName());

System.out.println(“decl class = ” + fld.getDeclaringClass());

System.out.println(“type = ” + fld.getType());

int mod = fld.getModifiers();

System.out.println(“modifiers = ” + Modifier.toString(mod));

System.out.println(“—–“);

}

} catch (Throwable e) {

System.err.println(e);

}

}

}

這個例子和前面那個例子非常相似。例中使用了一個新東西 Modifier,它也是一個 reflection 類,用來描述字段成員的修飾語,如「private int」。這些修飾語自身由整數描述,而且使用 Modifier.toString 來返回以「官方」順序排列的字符串描述 (如「static」在「final」之前)。這個程序的輸出是:

name = d

decl class = class field1

type = double

modifiers = private

—–

name = i

decl class = class field1

type = int

modifiers = public static final

—–

name = s

decl class = class field1

type = class java.lang.String

modifiers =

—–

和獲取方法的情況一下,獲取字段的時候也可以只取得在當前類中申明了的字段信息 (getDeclaredFields),或者也可以取得父類中定義的字段 (getFields) 。

6.根據方法的名稱來執行方法

文本到這裡,所舉的例子無一例外都與如何獲取類的信息有關。我們也可以用 reflection 來做一些其它的事情,比如執行一個指定了名稱的方法。下面的示例演示了這一操作:

import java.lang.reflect.*;

public class method2 {

public int add(int a, int b) {

return a + b;

}

public static void main(String args[]) {

try {

Class cls = Class.forName(“method2”);

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Method meth = cls.getMethod(“add”, partypes);

method2 methobj = new method2();

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj = meth.invoke(methobj, arglist);

Integer retval = (Integer) retobj;

System.out.println(retval.intvalue());

} catch (Throwable e) {

System.err.println(e);

}

}

}

假如一個程序在執行的某處的時候才知道需要執行某個方法,這個方法的名稱是在程序的運行過程中指定的 (例如,JavaBean 開發環境中就會做這樣的事),那麼上面的程序演示了如何做到。

上例中,getMethod 用於查找一個具有兩個整型參數且名為 add 的方法。找到該方法並創建了相應的 Method 對象之後,在正確的對象實例中執行它。執行該方法的時候,需要提供一個參數列表,這在上例中是分別包裝了整數 37 和 47 的兩個 Integer 對象。執行方法的返回的同樣是一個 Integer 對象,它封裝了返回值 84。

7.創建新的對象

對於構造器,則不能像執行方法那樣進行,因為執行一個構造器就意味着創建了一個新的對象 (準確的說,創建一個對象的過程包括分配內存和構造對象)。所以,與上例最相似的例子如下:

import java.lang.reflect.*;

public class constructor2 {

public constructor2() {

}

public constructor2(int a, int b) {

System.out.println(“a = ” + a + ” b = ” + b);

}

public static void main(String args[]) {

try {

Class cls = Class.forName(“constructor2”);

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Constructor ct = cls.getConstructor(partypes);

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj = ct.newInstance(arglist);

} catch (Throwable e) {

System.err.println(e);

}

}

}

根據指定的參數類型找到相應的構造函數並執行它,以創建一個新的對象實例。使用這種方法可以在程序運行時動態地創建對象,而不是在編譯的時候創建對象,這一點非常有價值。

8.改變字段(域)的值

reflection 的還有一個用處就是改變對象數據字段的值。reflection 可以從正在運行的程序中根據名稱找到對象的字段並改變它,下面的例子可以說明這一點:

import java.lang.reflect.*;

public class field2 {

public double d;

public static void main(String args[]) {

try {

Class cls = Class.forName(“field2”);

Field fld = cls.getField(“d”);

field2 f2obj = new field2();

System.out.println(“d = ” + f2obj.d);

fld.setDouble(f2obj, 12.34);

System.out.println(“d = ” + f2obj.d);

} catch (Throwable e) {

System.err.println(e);

}

}

}

這個例子中,字段 d 的值被變為了 12.34。

9.使用數組

本文介紹的 reflection 的最後一種用法是創建的操作數組。數組在 Java 語言中是一種特殊的類類型,一個數組的引用可以賦給 Object 引用。觀察下面的例子看看數組是怎麼工作的:

import java.lang.reflect.*;

public class array1 {

public static void main(String args[]) {

try {

Class cls = Class.forName(“java.lang.String”);

Object arr = Array.newInstance(cls, 10);

Array.set(arr, 5, “this is a test”);

String s = (String) Array.get(arr, 5);

System.out.println(s);

} catch (Throwable e) {

System.err.println(e);

}

}

}

例中創建了 10 個單位長度的 String 數組,為第 5 個位置的字符串賦了值,最後將這個字符串從數組中取得並打印了出來。

下面這段代碼提供了一個更複雜的例子:

import java.lang.reflect.*;

public class array2 {

public static void main(String args[]) {

int dims[] = new int[]{5, 10, 15};

Object arr = Array.newInstance(Integer.TYPE, dims);

Object arrobj = Array.get(arr, 3);

Class cls = arrobj.getClass().getComponentType();

System.out.println(cls);

arrobj = Array.get(arrobj, 5);

Array.setInt(arrobj, 10, 37);

int arrcast[][][] = (int[][][]) arr;

System.out.println(arrcast[3][5][10]);

}

}

例中創建了一個 5 x 10 x 15 的整型數組,並為處於 [3][5][10] 的元素賦了值為 37。注意,多維數組實際上就是數組的數組,例如,第一個 Array.get 之後,arrobj 是一個 10 x 15 的數組。進而取得其中的一個元素,即長度為 15 的數組,並使用 Array.setInt 為它的第 10 個元素賦值。

注意創建數組時的類型是動態的,在編譯時並不知道其類型。

java反射機制的實現原理

反射機制:所謂的反射機制就是java語言在運行時擁有一項自觀的能力。通過這種能力可以徹底的了解自身的情況為下一步的動作做準備。下面具體介紹一下java的反射機制。這裡你將顛覆原來對java的理解。

Java的反射機制的實現要藉助於4個類:class,Constructor,Field,Method;其中class代表的時類對 象,Constructor-類的構造器對象,Field-類的屬性對象,Method-類的方法對象。通過這四個對象我們可以粗略的看到一個類的各個組 成部分。

Class:程序運行時,java運行時系統會對所有的對象進行運行時類型的處理。這項信息記錄了每個對象所屬的類,虛擬機通常使用運行時類型信息選擇正 確的方法來執行(摘自:白皮書)。但是這些信息我們怎麼得到啊,就要藉助於class類對象了啊。在Object類中定義了getClass()方法。我 們可以通過這個方法獲得指定對象的類對象。然後我們通過分析這個對象就可以得到我們要的信息了。

比如:ArrayList arrayList;

Class clazz = arrayList.getClass();

然後我來處理這個對象clazz。

當然了Class類具有很多的方法,這裡重點將和Constructor,Field,Method類有關係的方法。

Reflection 是 Java 程序開發語言的特徵之一,它允許運行中的 Java 程序對自身進行檢查,或者說「自審」,並能直接操作程序的內部屬性。Java 的這一能力在實際應用中也許用得不是很多,但是個人認為要想對java有個更加深入的了解還是應該掌握的。

1.檢測類:

reflection的工作機制

考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

import java.lang.reflect.*;

public class DumpMethods {

public static void main(String args[]) {

try {

Class c = Class.forName(args[0]);

Method m[] = c.getDeclaredMethods();

for (int i = 0; i m.length; i++)

System.out.println(m[i].toString());

} catch (Throwable e) {

System.err.println(e);

}

}

}

按如下語句執行:

java DumpMethods java.util.ArrayList

這個程序使用 Class.forName 載入指定的類,然後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

Java類反射中的主要方法

對於以下三類組件中的任何一類來說 — 構造函數、字段和方法 — java.lang.Class 提供四種獨立的反射調用,以不同的方式來獲得信息。調用都遵循一種標準格式。以下是用於查找構造函數的一組反射調用:

Constructor getConstructor(Class[] params) — 獲得使用特殊的參數類型的公共構造函數,

Constructor[] getConstructors() — 獲得類的所有公共構造函數

Constructor getDeclaredConstructor(Class[] params) — 獲得使用特定參數類型的構造函數(與接入級別無關)

Constructor[] getDeclaredConstructors() — 獲得類的所有構造函數(與接入級別無關)

獲得字段信息的Class 反射調用不同於那些用於接入構造函數的調用,在參數類型數組中使用了字段名:

Field getField(String name) — 獲得命名的公共字段

Field[] getFields() — 獲得類的所有公共字段

Field getDeclaredField(String name) — 獲得類聲明的命名的字段

Field[] getDeclaredFields() — 獲得類聲明的所有字段

用於獲得方法信息函數:

Method getMethod(String name, Class[] params) — 使用特定的參數類型,獲得命名的公共方法

Method[] getMethods() — 獲得類的所有公共方法

Method getDeclaredMethod(String name, Class[] params) — 使用特寫的參數類型,獲得類聲明的命名的方法

Method[] getDeclaredMethods() — 獲得類聲明的所有方法

使用 Reflection:

用於 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。

下面就是獲得一個 Class 對象的方法之一:

Class c = Class.forName(“java.lang.String”);

這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:

Class c = int.class;

或者

Class c = Integer.TYPE;

它們可獲得基本類型的類信息。其中後一種方法中訪問的是基本類型的封裝類 (如 Intege ) 中預先定義好的 TYPE 字段。

第二步是調用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

一旦取得這個信息,就可以進行第三步了——使用 reflection API 來操作這些信息,如下面這段代碼:

Class c = Class.forName(“java.lang.String”);

Method m[] = c.getDeclaredMethods();

System.out.println(m[0].toString());

它將以文本方式打印出 String 中定義的第一個方法的原型。

處理對象:

a.創建一個Class對象

b.通過getField 創建一個Field對象

c.調用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對象就省略;Object是指實例).

例如:

import java.lang.reflect.*;

import java.awt.*;

class SampleGet {

public static void main(String[] args) {

Rectangle r = new Rectangle(100, 325);

printHeight(r);

}

static void printHeight(Rectangle r) {

Field heightField;

Integer heightValue;

Class c = r.getClass();

try {

heightField = c.getField(“height”);

heightValue = (Integer) heightField.get(r);

System.out.println(“Height: ” + heightValue.toString());

} catch (NoSuchFieldException e) {

System.out.println(e);

} catch (SecurityException e) {

System.out.println(e);

} catch (IllegalAccessException e) {

System.out.println(e);

}

}

}

安全性和反射:

在處理反射時安全性是一個較複雜的問題。反射經常由框架型代碼使用,由於這一點,我們可能希望框架能夠全面接入代碼,無需考慮常規的接入限制。但是,在其它情況下,不受控制的接入會帶來嚴重的安全性風險,例如當代碼在不值得信任的代碼共享的環境中運行時。

由於這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用於源代碼接入相同的限制:

從任意位置到類公共組件的接入

類自身外部無任何到私有組件的接入

受保護和打包(缺省接入)組件的有限接入

不過至少有些時候,圍繞這些限制還有一種簡單的方法。我們可以在我們所寫的類中,擴展一個普通的基本類 java.lang.reflect.AccessibleObject 類。這個類定義了一種setAccessible方法,使我們能夠啟動或關閉對這些類中其中一個類的實例的接入檢測。唯一的問題在於如果使用了安全性管理 器,它將檢測正在關閉接入檢測的代碼是否許可了這樣做。如果未許可,安全性管理器拋出一個例外。

下面是一段程序,在TwoString 類的一個實例上使用反射來顯示安全性正在運行:

public class ReflectSecurity {

public static void main(String[] args) {

try {

TwoString ts = new TwoString(“a”, “b”);

Field field = clas.getDeclaredField(“m_s1”);

// field.setAccessible(true);

System.out.println(“Retrieved value is ” +

field.get(inst));

} catch (Exception ex) {

ex.printStackTrace(System.out);

}

}

}

如果我們編譯這一程序時,不使用任何特定參數直接從命令行運行,它將在field .get(inst)調用中拋出一個IllegalAccessException異常。如果我們不注釋 field.setAccessible(true)代碼行,那麼重新編譯並重新運行該代碼,它將編譯成功。最後,如果我們在命令行添加了JVM參數 -Djava.security.manager以實現安全性管理器,它仍然將不能通過編譯,除非我們定義了ReflectSecurity類的許可權 限。

反射性能:(轉錄別人的啊)

反射是一種強大的工具,但也存在一些不足。一個主要的缺點是對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於只直接執行相同的操作。

下面的程序是字段接入性能測試的一個例子,包括基本的測試方法。每種方法測試字段接入的一種形式 — accessSame 與同一對象的成員字段協作,accessOther 使用可直接接入的另一對象的字段,accessReflection 使用可通過反射接入的另一對象的字段。在每種情況下,方法執行相同的計算 — 循環中簡單的加/乘順序。

程序如下:

public int accessSame(int loops) {

m_value = 0;

for (int index = 0; index loops; index++) {

m_value = (m_value + ADDITIVE_VALUE) *

MULTIPLIER_VALUE;

}

return m_value;

}

public int acces

sReference(int loops) {

TimingClass timing = new TimingClass();

for (int index = 0; index loops; index++) {

timing.m_value = (timing.m_value + ADDITIVE_VALUE) *

MULTIPLIER_VALUE;

}

return timing.m_value;

}

public int accessReflection(int loops) throws Exception {

TimingClass timing = new TimingClass();

try {

Field field = TimingClass.class.

getDeclaredField(“m_value”);

for (int index = 0; index loops; index++) {

int value = (field.getInt(timing) +

ADDITIVE_VALUE) * MULTIPLIER_VALUE;

field.setInt(timing, value);

}

return timing.m_value;

} catch (Exception ex) {

System.out.println(“Error using reflection”);

throw ex;

}

}

在上面的例子中,測試程序重複調用每種方法,使用一個大循環數,從而平均多次調用的時間衡量結果。平均值中不包括每種方法第一次調用的時間,因此初始化時間不是結果中的一個因素。下面的圖清楚的向我們展示了每種方法字段接入的時間:

圖 1:字段接入時間 :

我們可以看出:在前兩副圖中(Sun JVM),使用反射的執行時間超過使用直接接入的1000倍以上。通過比較,IBM JVM可能稍好一些,但反射方法仍舊需要比其它方法長700倍以上的時間。任何JVM上其它兩種方法之間時間方面無任何顯著差異,但IBM JVM幾乎比Sun JVM快一倍。最有可能的是這種差異反映了Sun Hot Spot JVM的專業優化,它在簡單基準方面表現得很糟糕。反射性能是Sun開發1.4 JVM時關注的一個方面,它在反射方法調用結果中顯示。在這類操作的性能方面,Sun 1.4.1 JVM顯示了比1.3.1版本很大的改進。

如果為為創建使用反射的對象編寫了類似的計時測試程序,我們會發現這種情況下的差異不象字段和方法調用情況下那麼顯著。使用newInstance()調 用創建一個簡單的java.lang.Object實例耗用的時間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的兩部。使用Array.newInstance(type, size)創建一個數組耗用的時間是任何測試的JVM上使用new type[size]的兩倍,隨着數組大小的增加,差異逐步縮小。隨着jdk6.0的推出,反射機制的性能也有了很大的提升。期待中….

總結:

Java語言反射提供一種動態鏈接程序組件的多功能方法。它允許程序創建和控制任何類的對象(根據安全性限制),無需提前硬編碼目標類。這些特性使得反射 特別適用於創建以非常普通的方式與對象協作的庫。例如,反射經常在持續存儲對象為數據庫、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使類和數據結構能按名稱動態檢索相關信息,並允許在運行着的程序中操作這些信息。Java 的這一特性非常強大,並且是其它一些常用語言,如 C、C++、Fortran 或者 Pascal 等都不具備的。

但反射有兩個缺點。第一個是性能問題。用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。如果它作為程序運行中相 對很少涉及的部分,緩慢的性能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性 能問題才變得至關重要。

許多應用中更嚴重的一個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問 題。反射代碼比相應的直接代碼更複雜,正如性能比較的代碼實例中看到的一樣。解決這些問題的最佳方案是保守地使用反射——僅在它可以真正增加靈活性的地方 ——記錄其在目標類中的使用。

一下是對應各個部分的例子:

具體的應用:

1、 模仿instanceof 運算符號

class A {}

public class instance1 {

public static void main(String args[])

{

try {

Class cls = Class.forName(“A”);

boolean b1

= cls.isInstance(new Integer(37));

System.out.println(b1);

boolean b2 = cls.isInstance(new A());

System.out.println(b2);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

2、 在類中尋找指定的方法,同時獲取該方法的參數列表,例外和返回值

import java.lang.reflect.*;

public class method1 {

private int f1(

Object p, int x) throws NullPointerException

{

if (p == null)

throw new NullPointerException();

return x;

}

public static void main(String args[])

{

try {

Class cls = Class.forName(“method1”);

Method methlist[]

= cls.getDeclaredMethods();

for (int i = 0; i methlist.length;

i++)

Method m = methlist[i];

System.out.println(“name

= ” + m.getName());

System.out.println(“decl class = ” +

m.getDeclaringClass());

Class pvec[] = m.getParameterTypes();

for (int j = 0; j pvec.length; j++)

System.out.println(“

param #” + j + ” ” + pvec[j]);

Class evec[] = m.getExceptionTypes();

for (int j = 0; j evec.length; j++)

System.out.println(“exc #” + j

+ ” ” + evec[j]);

System.out.println(“return type = ” +

m.getReturnType());

System.out.println(“—–“);

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

3、 獲取類的構造函數信息,基本上與獲取方法的方式相同

import java.lang.reflect.*;

public class constructor1 {

public constructor1()

{

}

protected constructor1(int i, double d)

{

}

public static void main(String args[])

{

try {

Class cls = Class.forName(“constructor1”);

Constructor ctorlist[]

= cls.getDeclaredConstructors();

for (int i = 0; i ctorlist.length; i++) {

Constructor ct = ctorlist[i];

System.out.println(“name

= ” + ct.getName());

System.out.println(“decl class = ” +

ct.getDeclaringClass());

Class pvec[] = ct.getParameterTypes();

for (int j = 0; j pvec.length; j++)

System.out.println(“param #”

+ j + ” ” + pvec[j]);

Class evec[] = ct.getExceptionTypes();

for (int j = 0; j evec.length; j++)

System.out.println(

“exc #” + j + ” ” + evec[j]);

System.out.println(“—–“);

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

4、 獲取類中的各個數據成員對象,包括名稱。類型和訪問修飾符號

import java.lang.reflect.*;

public class field1 {

private double d;

public static final int i = 37;

String s = “testing”;

public static void main(String args[])

{

try {

Class cls = Class.forName(“field1”);

Field fieldlist[]

= cls.getDeclaredFields();

for (int i

= 0; i fieldlist.length; i++) {

Field fld = fieldlist[i];

System.out.println(“name

= ” + fld.getName());

System.out.println(“decl class = ” +

fld.getDeclaringClass());

System.out.println(“type

= ” + fld.getType());

int mod = fld.getModifiers();

System.out.println(“modifiers = ” +

Modifier.toString(mod));

System.out.println(“—–“);

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

5、 通過使用方法的名字調用方法

import java.lang.reflect.*;

public class method2 {

public int add(int a, int b)

{

return a + b;

}

public static void main(String args[])

{

try {

Class cls = Class.forName(“method2”);

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Method meth = cls.getMethod(

“add”, partypes);

method2 methobj = new method2();

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj

= meth.invoke(methobj, arglist);

Integer retval = (Integer)retobj;

System.out.println(retval.intValue());

}

catch (Throwable e) {

System.err.println(e);

}

}

}

6、 創建新的對象

import java.lang.reflect.*;

public class constructor2 {

public constructor2()

{

}

public constructor2(int a, int b)

{

System.out.println(

“a = ” + a + ” b = ” + b);

}

public static void main(String args[])

{

try {

Class cls = Class.forName(“constructor2”);

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Constructor ct

= cls.getConstructor(partypes);

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj = ct.newInstance(arglist);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

7、 變更類實例中的數據的值

import java.lang.reflect.*;

public class field2 {

public double d;

public static void main(String args[])

{

try {

Class cls = Class.forName(“field2”);

Field fld = cls.getField(“d”);

field2 f2obj = new field2();

System.out.println(“d = ” + f2obj.d);

fld.setDouble(f2obj, 12.34);

System.out.println(“d = ” + f2obj.d);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

使用反射創建可重用代碼:

1、 對象工廠

Object factory(String p) {

Class c;

Object o=null;

try {

c = Class.forName(p);// get class def

o = c.newInstance(); // make a new one

} catch (Exception e) {

System.err.println(“Can’t make a ” + p);

}

return o;

}

public class ObjectFoundry {

public static Object factory(String p)

throws ClassNotFoundException,

InstantiationException,

IllegalAccessException {

Class c = Class.forName(p);

Object o = c.newInstance();

return o;

}

}

2、 動態檢測對象的身份,替代instanceof

public static boolean

isKindOf(Object obj, String type)

throws ClassNotFoundException {

// get the class def for obj and type

Class c = obj.getClass();

Class tClass = Class.forName(type);

while ( c!=null ) {

if ( c==tClass ) return true;

c = c.getSuperclass();

}

return false;

}

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/230391.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-10 18:16
下一篇 2024-12-10 18:16

相關推薦

  • Harris角點檢測算法原理與實現

    本文將從多個方面對Harris角點檢測算法進行詳細的闡述,包括算法原理、實現步驟、代碼實現等。 一、Harris角點檢測算法原理 Harris角點檢測算法是一種經典的計算機視覺算法…

    編程 2025-04-29
  • 瘦臉算法 Python 原理與實現

    本文將從多個方面詳細闡述瘦臉算法 Python 實現的原理和方法,包括該算法的意義、流程、代碼實現、優化等內容。 一、算法意義 隨着科技的發展,瘦臉算法已經成為了人們修圖中不可缺少…

    編程 2025-04-29
  • 神經網絡BP算法原理

    本文將從多個方面對神經網絡BP算法原理進行詳細闡述,並給出完整的代碼示例。 一、BP算法簡介 BP算法是一種常用的神經網絡訓練算法,其全稱為反向傳播算法。BP算法的基本思想是通過正…

    編程 2025-04-29
  • Spring S_CSRF防護機制實現及應用

    Spring S_CSRF防護機制是Spring Security框架提供的一個針對跨站請求偽造攻擊(CSRF)的保護機制。本文將從以下幾個方面詳細介紹Spring S_CSRF防…

    編程 2025-04-28
  • 異或什麼意思及其用途

    異或操作是一種比較常見的位運算操作,也稱為「異或運算」,這個運算符用符號「^」表示。它是指對兩個相應位進行邏輯異或,並返回結果。 我們來看下異或的運算規則: 0 ^ 0 = 0 0…

    編程 2025-04-28
  • GloVe詞向量:從原理到應用

    本文將從多個方面對GloVe詞向量進行詳細的闡述,包括其原理、優缺點、應用以及代碼實現。如果你對詞向量感興趣,那麼這篇文章將會是一次很好的學習體驗。 一、原理 GloVe(Glob…

    編程 2025-04-27
  • 編譯原理語法分析思維導圖

    本文將從以下幾個方面詳細闡述編譯原理語法分析思維導圖: 一、語法分析介紹 1.1 語法分析的定義 語法分析是編譯器中將輸入的字符流轉換成抽象語法樹的一個過程。該過程的目的是確保輸入…

    編程 2025-04-27
  • Python的垃圾回收機制

    本文將對Python的垃圾回收機制進行詳細闡述,着重介紹它的基本原理和實現方式。此外,我們還將介紹常見的問題及解決方法,並給出相應的代碼示例。 一、Python的垃圾回收概述 垃圾…

    編程 2025-04-27
  • 機制與策略分離

    了解機制與策略分離的解決方法與優勢 一、概述 機制與策略分離是一種軟件設計理念,它將複雜的系統、組件等模塊化,通過分離機制與策略,把模塊實現的方式與具體使用方式分開。 機制是實現某…

    編程 2025-04-27
  • Python字典底層原理用法介紹

    本文將以Python字典底層原理為中心,從多個方面詳細闡述。字典是Python語言的重要組成部分,具有非常強大的功能,掌握其底層原理對於學習和使用Python將是非常有幫助的。 一…

    編程 2025-04-25

發表回復

登錄後才能評論