你必須掌的握反射用法
什么是反射
反射(Reflection)是Java 程序開(kāi)發(fā)語(yǔ)言的特征之一惑芭,它允許運(yùn)行中的 Java 程序獲取自身的信息立莉,并且可以操作類或?qū)ο蟮膬?nèi)部屬性悦施。
Oracle官方對(duì)反射的解釋是
Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.
簡(jiǎn)而言之,通過(guò)反射尺迂,我們可以在運(yùn)行時(shí)獲得程序或程序集中每一個(gè)類型的成員和成員的信息掠拳。
程序中一般的對(duì)象的類型都是在編譯期就確定下來(lái)的,而Java反射機(jī)制可以動(dòng)態(tài)地創(chuàng)建對(duì)象并調(diào)用其屬性筐赔,這樣的對(duì)象的類型在編譯期是未知的铣猩。所以我們可以通過(guò)反射機(jī)制直接創(chuàng)建對(duì)象,即使這個(gè)對(duì)象的類型在編譯期是未知的茴丰。
?反射的核心是JVM在運(yùn)行時(shí)才動(dòng)態(tài)加載類或調(diào)用方法/訪問(wèn)屬性达皿,它不需要事先(寫代碼的時(shí)候或編譯期)知道運(yùn)行對(duì)象是誰(shuí)。
Java反射框架主要提供以下功能:
- 判斷任意對(duì)象所屬的類贿肩;
- 獲取一個(gè)類的任意對(duì)象(獲得Class對(duì)象)峦椰;
- 獲取一個(gè)類所具有的任意成員變量和方法;
- 構(gòu)造任意一個(gè)對(duì)象以及調(diào)用一個(gè)對(duì)象
這里說(shuō)的獲取特質(zhì)運(yùn)行時(shí)汰规,既編譯之后的行為汤功。通俗的講就是我們可以獲取Android 系統(tǒng)所有內(nèi)部類,private方法 變量控轿,@hide 的方法和變量 @internal 的方法和變量冤竹。。茬射。
Internal和hidden API的區(qū)別:
Hidden API之所以被隱藏鹦蠕,是想阻止開(kāi)發(fā)者使用SDK中那些未完成或不穩(wěn)定的部分(接口或架構(gòu))。
舉個(gè)例子在抛,Bluetooth API在API 5(Android 2.0)上才開(kāi)放钟病;在API 3 和4上都是用@hide屬性隱藏了。當(dāng)這些API被驗(yàn)證和清理后刚梭,Google的開(kāi)發(fā)者會(huì)移除@hide屬性肠阱,并讓其在API 5官方化。很多地方在API 4 和5之間發(fā)生了變化朴读。如果你的程序依賴某些隱藏的API屹徘,當(dāng)其部署到新的平臺(tái)上時(shí),就有可能陷入困境衅金。
對(duì)于internal API來(lái)說(shuō)噪伊,從來(lái)都沒(méi)有計(jì)劃將其開(kāi)放出來(lái)。它就是Android的“內(nèi)部廚房”氮唯,對(duì)開(kāi)發(fā)者來(lái)說(shuō)鉴吹,應(yīng)該將其視作黑盒。凡事都會(huì)有變化的惩琉。如果你依賴某些internal API豆励,也有可能在新的Android release上,這些internal API發(fā)生變化瞒渠,從而令你失望良蒸。
總結(jié)一下區(qū)別:
Hidden API = 進(jìn)行中的工作;
Internal API = 黑盒在孝;
反射的基本運(yùn)用
1. 獲得Class對(duì)象
方法有三種
(1)使用Class類的forName靜態(tài)方法:
方法:
public static Class<?> forName(String className)
java 調(diào)用:
Class.forName("XXX.XXX.XXX");
(2)直接獲取某一個(gè)對(duì)象的class诚啃,比如:
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
(3)調(diào)用某個(gè)對(duì)象的getClass()方法,比如:
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
2. 判斷是否為某個(gè)類的實(shí)例
代碼調(diào)用:
String label = " XXXXX ";
if (label instanceof Objects) {
//do something
}
反射native方法如下:
public native boolean isInstance(Object obj);
3. 創(chuàng)建實(shí)例
通過(guò)反射來(lái)生成對(duì)象主要有兩種方式。
(1)使用Class對(duì)象的newInstance()方法來(lái)創(chuàng)建Class對(duì)象對(duì)應(yīng)類的實(shí)例私沮。
Class<?> c = String.class;
Object str = c.newInstance();
(2)先通過(guò)Class對(duì)象獲取指定的Constructor對(duì)象始赎,再調(diào)用Constructor對(duì)象的newInstance()方法來(lái)創(chuàng)建實(shí)例。這種方法可以用指定的構(gòu)造器構(gòu)造類的實(shí)例仔燕。
//獲取String所對(duì)應(yīng)的Class對(duì)象
Class<?> c = String.class;
//獲取String類帶一個(gè)String參數(shù)的構(gòu)造器
Constructor constructor = c.getConstructor(String.class);
//根據(jù)構(gòu)造器創(chuàng)建實(shí)例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
4. 獲取方法
獲取某個(gè)Class對(duì)象的方法集合造垛,主要有以下幾個(gè)方法:
getDeclaredMethods()方法返回類或接口聲明的所有方法,包括公共晰搀、保護(hù)五辽、默認(rèn)(包)訪問(wèn)和私有方法,但不包括繼承的方法外恕。 (除了父類的)
public Method[] getDeclaredMethods() throws SecurityException
getMethods()方法返回某個(gè)類的所有公用(public)方法杆逗,包括其繼承類的公用方法乡翅。(只能拿到公共的, 包括父類的)
public Method[] getMethods() throws SecurityException
getMethod方法返回一個(gè)特定的方法,其中第一個(gè)參數(shù)為方法名稱罪郊,后面的參數(shù)為方法的參數(shù)對(duì)應(yīng)Class的對(duì)象(無(wú)限制)
public Method getMethod(String name, Class<?>... parameterTypes)
5. 獲取構(gòu)造器信息
獲取類構(gòu)造器的用法與上述獲取方法的用法類似蠕蚜。主要是通過(guò)Class類的getConstructor方法得到Constructor類的一個(gè)實(shí)例,而Constructor類有一個(gè)newInstance方法可以創(chuàng)建一個(gè)對(duì)象實(shí)例:
public T newInstance(Object ... initargs)
此方法可以根據(jù)傳入的參數(shù)來(lái)調(diào)用對(duì)應(yīng)的Constructor創(chuàng)建對(duì)象實(shí)例~
6. 獲取類的成員變量(字段)信息
主要以下這幾個(gè)方法:
getFiled: 訪問(wèn)公有的成員變量
getDeclaredField:所有已聲明的成員變量悔橄。但不能得到其父類的成員變量
getFileds和getDeclaredFields用法同上(參照Method)
7. 調(diào)用方法
當(dāng)我們從類中獲取了一個(gè)方法后靶累,我們就可以用invoke()方法來(lái)調(diào)用這個(gè)方法。invoke方法的原型為:
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
8. 利用反射調(diào)用 android @hide 方法的一個(gè)例子
package android.text;
public class StaticLayout extends Layout {}
里面有一個(gè)構(gòu)造方法是@hide 怎么調(diào)用癣疟,答案在后面一篇文章給出
注意 參數(shù)int l類型的應(yīng)該使用 int.class ( Integer.TYPE;)
/**
* @hide
*/
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
super((ellipsize == null)
? source
: (source instanceof Spanned)
? new SpannedEllipsizer(source)
: new Ellipsizer(source),
paint, outerwidth, align, textDir, spacingmult, spacingadd);
Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
.setTextDirection(textDir)
.setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsizedWidth(ellipsizedWidth)
.setEllipsize(ellipsize)
.setMaxLines(maxLines);
/*
* This is annoying, but we can't refer to the layout until
* superclass construction is finished, and the superclass
* constructor wants the reference to the display text.
*
* This will break if the superclass constructor ever actually
* cares about the content instead of just holding the reference.
*/
if (ellipsize != null) {
Ellipsizer e = (Ellipsizer) getText();
e.mLayout = this;
e.mWidth = ellipsizedWidth;
e.mMethod = ellipsize;
mEllipsizedWidth = ellipsizedWidth;
mColumns = COLUMNS_ELLIPSIZE;
} else {
mColumns = COLUMNS_NORMAL;
mEllipsizedWidth = outerwidth;
}
mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
mLines = new int[mLineDirections.length];
mMaximumVisibleLineCount = maxLines;
generate(b, b.mIncludePad, b.mIncludePad);
Builder.recycle(b);
}