Java中反射的學(xué)習(xí)筆記分享
簡(jiǎn)介
反射是Java編程語(yǔ)言中的一個(gè)特性。它允許執(zhí)行的Java程序檢查或 操作 自身,并操作程序的內(nèi)部屬性。例如,Java類可以獲取其所有成員的名稱并顯示它們。
從程序內(nèi)部檢查和操作Java類的能力聽(tīng)起來(lái)可能不太顯示,但是在其他編程語(yǔ)言中,這個(gè)特性根本不存在。例如,在C或C ++ 程序中無(wú)法獲取有關(guān)該程序中定義的函數(shù)的信息。
反射的一個(gè)具體用途是在JavaBeans中,軟件組件可以通過(guò)一個(gè)構(gòu)建工具進(jìn)行可視化操作。該工具使用反射來(lái)獲取Java組件 (類) 動(dòng)態(tài)加載時(shí)的屬性。

一個(gè)簡(jiǎn)單的例子
要了解反射是如何工作的,請(qǐng)考慮以下簡(jiǎn)單示例:
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);
}
}
}
對(duì)于以下項(xiàng)的調(diào)用:
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)
也就是說(shuō),類的方法名稱java.util.Stack列出了它們及其完全限定的參數(shù)和返回類型。
此程序使用加載指定的類 class.forName, 然后調(diào)用 getDeclaredMethods 方法檢索類中定義的方法列表. java.lang.reflect.Method是表示單個(gè)類方法的類。
設(shè)置使用反射
反射類,例如Method,在java.lang.反射中找到。使用這些類必須遵循三個(gè)步驟。第一步是獲得一個(gè)java.lang.Class要操作的類的對(duì)象。java.lang.Class用于表示正在運(yùn)行的Java程序中的類和接口。
獲取類對(duì)象的一種方法是:
Class c = Class.forName("java.lang.String");
上述代碼獲取的類對(duì)象 String.
另一種方法是使用:
Class c = int.class;
或者
Class c = Integer.TYPE;
獲取基本類型的類信息。后一種方法訪問(wèn)預(yù)定義TYPE 包裝類型 (例如Integer) 為基本類型。
第二步是調(diào)用方法,例如getDeclaredMethods,以獲取該類聲明的所有方法的列表。
一旦掌握了這些信息,那么第三步就是使用反射API來(lái)操作這些信息。例如:
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
在下面的示例中,將三個(gè)步驟結(jié)合在一起,以呈現(xiàn)如何使用反射處理特定應(yīng)用的獨(dú)立插圖。
模擬instanceof運(yùn)算
一旦掌握了類信息,下一步通常是詢問(wèn)有關(guān)類對(duì)象的基本問(wèn)題。
例如,Class.isInstance方法可以用來(lái)模擬instanceof 運(yùn)算:
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);
}
}
}
在此示例中,類對(duì)象A被創(chuàng)建,然后我們檢查類實(shí)例對(duì)象,以查看它們是否是A。Integer(37)不是,但是new A()是。
了解類的方法
反射最有價(jià)值和最基本的用途之一是找出類中定義了哪些方法。為此,可以使用以下代碼:
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的類描述,然后調(diào)用getDeclaredMethods(一個(gè)用于獲取類中定義的每個(gè)方法的函數(shù))檢索Method 對(duì)象列表。這些方法包括public、protect、package和priva。如果你在程序中使用getMethods 而不是getDeclaredMethods,還可以獲取繼承方法的信息。
程序的輸出為:
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 -----
獲取有關(guān)構(gòu)造函數(shù)的信息
使用類似的方法來(lái)找出類的構(gòu)造函數(shù)。例如:
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);
}
}
}
在此示例中沒(méi)有檢索到返回類型信息,因?yàn)闃?gòu)造函數(shù)實(shí)際上沒(méi)有真正的返回類型。
運(yùn)行此程序時(shí),輸出為:
name = constructor1 decl class = class constructor1 ----- name = constructor1 decl class = class constructor1 param #0 int param #1 double -----
查找類字段
還可以找出類中定義了哪些數(shù)據(jù)字段。為此,可以使用以下代碼:
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);
}
}
}
此示例與前面的示例相似。一個(gè)新功能是使用Modifier。這是一個(gè)反射類,表示在字段成員上找到的修飾符,例如private int。修飾符本身由整數(shù)表示,并且Modifier.toString用于返回默認(rèn)聲明順序中的字符串表示形式 (例如final之前的static)。程序的輸出為:
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 = -----
與方法一樣,可以僅獲取有關(guān)類中聲明的字段的信息 (getDeclaredFields),或獲取有關(guān)超類中定義的字段的信息 (getFields)。
按名稱調(diào)用方法
到目前為止,已經(jīng)提出的例子都與獲取class有關(guān)。但是也可以以其他方式使用反射,例如調(diào)用指定名稱的方法。
要了解其工作原理,請(qǐng)考慮以下示例:
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);
}
}
}
假設(shè)一個(gè)程序想要調(diào)用add方法,但直到執(zhí)行時(shí)才知道。也就是說(shuō),在執(zhí)行期間指定方法的名稱 (例如,這可以由JavaBeans開(kāi)發(fā)環(huán)境完成)。上面的程序展示了一種方法。
getMethod用于在類中查找具有兩個(gè)integer參數(shù)類型并具有適當(dāng)名稱的方法。一旦找到此方法并將其捕獲到Method 對(duì)象,它是在適當(dāng)類型的對(duì)象實(shí)例上調(diào)用的。要調(diào)用方法,必須構(gòu)造一個(gè)參數(shù)列表,基本整數(shù)值為37和47Integer 對(duì)象。返回值 (84) 也被包含在Integer 對(duì)象。
創(chuàng)建新對(duì)象
構(gòu)造函數(shù)不等同于方法調(diào)用,因?yàn)檎{(diào)用構(gòu)造函數(shù)等同于創(chuàng)建新對(duì)象 (最準(zhǔn)確地說(shuō),創(chuàng)建新對(duì)象涉及內(nèi)存分配和對(duì)象構(gòu)造)。所以最接近前面例子的是:
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);
}
}
}
它查找處理指定參數(shù)類型并調(diào)用它的構(gòu)造函數(shù),以創(chuàng)建對(duì)象的新實(shí)例。這種方法的價(jià)值在于它純粹是動(dòng)態(tài)的,在執(zhí)行時(shí)而不是在編譯時(shí)使用構(gòu)造函數(shù)查找和調(diào)用。
更改字段的值
反射的另一個(gè)用途是改變對(duì)象中數(shù)據(jù)字段的值。它的值再次從反射的動(dòng)態(tài)性質(zhì)中導(dǎo)出,其中可以在執(zhí)行程序中按名稱查找字段,然后更改其值。以下示例說(shuō)明了這一點(diǎn):
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字段的值設(shè)置為12.34。
使用數(shù)組
反射的一個(gè)用途是創(chuàng)建和操作數(shù)組。Java語(yǔ)言中的數(shù)組是類的一種特殊類型,并且可以將數(shù)組引用分配給Object。
要查看數(shù)組的工作方式,請(qǐng)考慮以下示例:
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);
}
}
}
此示例創(chuàng)建一個(gè)10長(zhǎng)的字符串?dāng)?shù)組,然后將數(shù)組中的位置5設(shè)置為字符串值。將檢索并顯示該值。
以下代碼說(shuō)明了對(duì)數(shù)組的更復(fù)雜的操作:
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]);
}
}
此示例創(chuàng)建一個(gè)5x10x15的int數(shù)組,然后繼續(xù)將數(shù)組中的位置 [3][5][10] 設(shè)置為值37。請(qǐng)注意,多維數(shù)組實(shí)際上是數(shù)組數(shù)組,因此,例如,在第一個(gè)array.get之后,arrobj中的結(jié)果是10x15數(shù)組。再次將其剝離以獲得15長(zhǎng)的數(shù)組,并使用Array.setInt。
請(qǐng)注意,創(chuàng)建的數(shù)組類型是動(dòng)態(tài)的,不必在編譯時(shí)知道。
總結(jié)
Java反射非常有用,因?yàn)樗С职疵Q動(dòng)態(tài)檢索有關(guān)類和數(shù)據(jù)結(jié)構(gòu)的信息,并允許在執(zhí)行的Java程序中進(jìn)行操作。此功能非常強(qiáng)大,但是也要謹(jǐn)慎使用
以上就是Java中反射的學(xué)習(xí)筆記分享的詳細(xì)內(nèi)容,更多關(guān)于Java反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解SpringCloudGateway內(nèi)存泄漏問(wèn)題
這篇文章主要介紹了詳解SpringCloudGateway內(nèi)存泄漏問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析
這篇文章主要介紹了Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
SpringCloud?openfeign聲明式服務(wù)調(diào)用實(shí)現(xiàn)方法介紹
在springcloud中,openfeign是取代了feign作為負(fù)載均衡組件的,feign最早是netflix提供的,他是一個(gè)輕量級(jí)的支持RESTful的http服務(wù)調(diào)用框架,內(nèi)置了ribbon,而ribbon可以提供負(fù)載均衡機(jī)制,因此feign可以作為一個(gè)負(fù)載均衡的遠(yuǎn)程服務(wù)調(diào)用框架使用2022-12-12
Java數(shù)據(jù)結(jié)構(gòu)之鏈表的增刪查改詳解
在這篇文章中,小編將帶大家了解一下Java數(shù)據(jù)結(jié)構(gòu)中鏈表的增刪查改(以下結(jié)果均在IDEA中編譯)希望在方便自己復(fù)習(xí)的同時(shí)也能幫助到大家2022-09-09
GC算法實(shí)現(xiàn)篇之并發(fā)標(biāo)記清除
這篇文章主要為大家介紹了GC算法實(shí)現(xiàn)篇之并發(fā)-標(biāo)記-清除,?CMS垃圾收集器在減少停頓時(shí)間上做了很多給力的工作,?大量的并發(fā)線程執(zhí)行的工作并不需要暫停應(yīng)用線程2022-01-01
spring @Lazy延遲注入的邏輯實(shí)現(xiàn)
有時(shí)候我們會(huì)在屬性注入的時(shí)候添加@Lazy注解實(shí)現(xiàn)延遲注入,今天咱們通過(guò)閱讀源碼來(lái)分析下原因,感興趣的可以了解一下2021-08-08

