Java反射給咱們提供了在運行時檢查甚至修改應用行爲的機制。 反射是java高級的核心技術,全部有經驗的程序員都應該理解。java
經過反射機制,咱們能夠在運行時檢視 類、接口、枚舉,得到他們的結構、方法以及屬性信息,即便在編譯期類是不可訪問的。 咱們也能夠經過反射建立類實例,調用它的方法,或者改變屬性值。程序員
Java的反射是一種很強大的機制,在正常的編程中使用並很少,但它是java的主幹,不少Java EE 框架均使用了反射技術:web
JUnit 利用反射技術解析@Test註解,從而獲得測試的方法並調用它們。編程
Spring 依賴注入是java反射的典型應用數組
Tomcat web容器經過解析web.xml文件和請求url,將請求正確的轉發到對應的模塊。安全
Eclipse 自動完成方法的名稱輸入bash
Struts框架
Hibernatedom
以上這個清單只是小部分,它們所有使用了反射技術,由於正常狀況下,它們沒法訪問用戶編寫的類、接口以及方法等。ide
可是咱們不建議在正常編程中濫用反射技術,由於咱們擁有本身編寫的類的訪問權限了,反射存在如下幾個缺陷:
性能較差 儘管反射解決了動態類型的問題,可是也引入了在classpath 掃描類進行加載的過程,會影響性能。
安全限制 反射須要在運行時得到訪問權限,可是在security manager中多是不容許的。 這可能會致使應用運行失敗。
安全問題 經過反射咱們能夠訪問那些不建議咱們訪問的類,例如咱們能夠訪問private的屬性並修改其值。 這可能引起安全問題致使應用異常。
較高的維護代價 反射相關的代碼難以理解以及調試,代碼的錯誤不能在編譯期展示出來,使用反射的代碼靈活性不高並難以維護。
在java中,任何對象要麼是原始類型或者引用類型。 全部的類、枚舉、數據和其餘引用類型均繼承自Object類。
java.lang.Class是全部反射操做的入口。對於任何類型的對象,JVM 會初始化其一個不可變的java.lang.Class 實例來提供檢查對象的運行時的屬性、建立新對象、調用方法、get/set 屬性。
咱們來看看Class的重要方法,爲了方便起見,咱們先建立一些類和接口。
package com.byron4j.hightLevel.reflection;
public interface BaseInterface {
public int interfaceInt=0;
void method1();
int method2(String str);
}
複製代碼
package com.byron4j.hightLevel.reflection;
public class BaseClass {
public int baseInt;
private static void method3(){
System.out.println("Method3");
}
public int method4(){
System.out.println("Method4");
return 0;
}
public static int method5(){
System.out.println("Method5");
return 0;
}
void method6(){
System.out.println("Method6");
}
// piblic 的內部類
public class BaseClassInnerClass{}
// public 的枚舉
public enum BaseClassMemberEnum{}
}
複製代碼
package com.byron4j.hightLevel.reflection;
public class ConcreteClass extends BaseClass implements BaseInterface{
public int publicInt;
private String privateString="private string";
protected boolean protectedBoolean;
Object defaultObject;
public ConcreteClass(int i){
this.publicInt=i;
}
@Override
public void method1() {
System.out.println("Method1 impl.");
}
@Override
public int method2(String str) {
System.out.println("Method2 impl.");
return 0;
}
@Override
public int method4(){
System.out.println("Method4 overriden.");
return 0;
}
public int method5(int i){
System.out.println("Method4 overriden.");
return 0;
}
// inner classes
public class ConcreteClassPublicClass{}
private class ConcreteClassPrivateClass{}
protected class ConcreteClassProtectedClass{}
class ConcreteClassDefaultClass{}
//member enum
enum ConcreteClassDefaultEnum{}
public enum ConcreteClassPublicEnum{}
//member interface
public interface ConcreteClassPublicInterface{}
}
複製代碼
下面來看看使用反射的經常使用方法。
咱們能夠經過三種方式獲取對象的Class實例:
經過靜態變量class
使用示例的getClass()方法
java.lang.Class.forName(String 完整的類名),完整的類名包含包名。
原始類型的class、包裝類型的TYPE都可以得到Class對象。
package com.byron4j.hightLevel.reflection;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
//方式一: 經過累的靜態變量class獲取Class對象
Class concreteClass = ConcreteClass.class;
//方式二:經過實例的getClass()方法獲取Class對象
concreteClass = new ConcreteClass(7).getClass();
//方式三:
concreteClass = Class.forName("com.byron4j.hightLevel.reflection.ConcreteClass");
//打印類相關信息
System.out.println(concreteClass.getCanonicalName());
System.out.println(concreteClass.getName());
/*++++++++++++++++++++++++++++++++++++++++++++++++ * 原始類型的class、包裝類型的TYPE * +++++++++++++++++++++++++++++++++++++++++++++++ */
Class primative = boolean.class;
System.out.println(primative.getCanonicalName());
Class doubleClass = Double.TYPE;
System.out.println(doubleClass.getName());
//數組類型的class示例
Class<?> arrayClass = Class.forName("[D");
System.out.println(arrayClass.getCanonicalName());
arrayClass = Class.forName("[B");
System.out.println(arrayClass.getCanonicalName());
arrayClass = Class.forName("[S");
System.out.println(arrayClass.getCanonicalName());
arrayClass = Class.forName("[C");
System.out.println(arrayClass.getCanonicalName());
arrayClass = Class.forName("[F");
System.out.println(arrayClass.getCanonicalName());
}
}
複製代碼
輸出以下所示:
com.byron4j.hightLevel.reflection.ConcreteClass
com.byron4j.hightLevel.reflection.ConcreteClass
boolean
double
double[]
byte[]
short[]
char[]
float[]
複製代碼
Class的getCanonicalName()方法返回類的名稱。在泛型中使用 java.lang.Class,能夠幫助框架獲取子類。
getSuperclass() 方法,返回類的超類(基類、父類)的class實例,若是該類是java.lang.Object、原始類型、接口則返回null。若是該class是數組形式,則該方法返回java.lang.Object。
Class<?> superClass = Class.forName("com.byron4j.hightLevel.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass);
System.out.println(Object.class.getSuperclass());
System.out.println(String[][].class.getSuperclass());
複製代碼
輸入以下:
class com.byron4j.hightLevel.reflection.BaseClass
null
class java.lang.Object
複製代碼
Class的getClasses() 方法能夠獲取class的全部繼承的超類、接口和本身定義的公有類、接口、枚舉等的數組形式。
Class[] classARR = concreteClass.getClasses();
System.out.println(Arrays.toString(classARR));
複製代碼
輸出:
[class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicClass,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicEnum,
interface com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicInterface,
class com.byron4j.hightLevel.reflection.BaseClass$BaseClassInnerClass,
class com.byron4j.hightLevel.reflection.BaseClass$BaseClassMemberEnum]
複製代碼
getDeclaredClasses()獲取當前類型自身定義的全部類、接口,並不包含從父類繼承過來的來、接口。
Class[] declareClassARR = concreteClass.getDeclaredClasses();
System.out.println("Arrays.toString(declareClassARR));
複製代碼
輸出:
[class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassDefaultClass,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassDefaultEnum,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPrivateClass,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassProtectedClass,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicClass,
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicEnum,
interface com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicInterface]
複製代碼
class.getDeclaringClass()獲取定義class的類。若是該類不是任何類或接口的成員,則返回null。
/**================================================ * getDeclaringClass * ================================================ */
System.out.println(BaseClassInnerClass.class.getDeclaringClass());
System.out.println(Double.TYPE.getDeclaringClass());
複製代碼
該類BaseClassInnerClass是在BaseClass中定義的,因而輸出:
class com.byron4j.hightLevel.reflection.BaseClass
null
複製代碼
getPackage() 方法獲取包的class實例。
/*=========================================== * getPackage() * ========================================== */
System.out.println(concreteClass.getPackage().getName());
複製代碼
輸出:
com.byron4j.hightLevel.reflection
複製代碼
getModifiers()方法能夠獲取class實例的訪問修飾符的個數。java.lang.reflect.Modifier.toString()能夠獲取class的修飾符的字符串形式。
/*=========================================== * getModifiers()、Modifier.toString() * ========================================== */
System.out.println(concreteClass.getModifiers());
System.out.println(Modifier.toString(concreteClass.getModifiers()));
複製代碼
輸出:
1
public
複製代碼
getTypeParameters()方法獲取class的類型聲明參數,若是有的話。好比集合框架的接口均制定了泛型。
Arrays.asList(Class.forName("java.util.Map").getTypeParameters()).forEach(
s -> { System.out.println(s); }
);
複製代碼
輸出:
K V
getGenericInterfaces() 能夠獲取class已經實現的接口的數組形式,幷包含泛型接口。 getInterfaces()方法會返回全部實現的接口,可是不包含泛型接口。
/**============================================= * getGenericInterfaces()、getInterfaces() * ============================================= */
Arrays.asList(concreteClass.getInterfaces()).forEach(
s -> { System.out.println("com.byron4j.hightLevel.reflection.ConcreteClass實現的接口:" + s); }
);
System.out.println("========================================");
Arrays.asList(concreteClass.getGenericInterfaces()).forEach(
s -> { System.out.println("com.byron4j.hightLevel.reflection.ConcreteClass實現的接口:" + s); }
);
System.out.println("========================================");
System.out.println("========================================");
Arrays.asList(Class.forName("java.util.ArrayList").getInterfaces()).forEach(
s -> { System.out.println("java.util.ArrayList實現的接口:" + s); }
);
System.out.println("========================================");
Arrays.asList(Class.forName("java.util.ArrayList").getGenericInterfaces()).forEach(
s -> { System.out.println("java.util.ArrayList實現的接口:" + s); }
);
複製代碼
輸出:
com.byron4j.hightLevel.reflection.ConcreteClass實現的接口:interface com.byron4j.hightLevel.reflection.BaseInterface
========================================
com.byron4j.hightLevel.reflection.ConcreteClass實現的接口:interface com.byron4j.hightLevel.reflection.BaseInterface
========================================
========================================
java.util.ArrayList實現的接口:interface java.util.List
java.util.ArrayList實現的接口:interface java.util.RandomAccess
java.util.ArrayList實現的接口:interface java.lang.Cloneable
java.util.ArrayList實現的接口:interface java.io.Serializable
========================================
java.util.ArrayList實現的接口:java.util.List<E>
java.util.ArrayList實現的接口:interface java.util.RandomAccess
java.util.ArrayList實現的接口:interface java.lang.Cloneable
java.util.ArrayList實現的接口:interface java.io.Serializable
複製代碼
getMethods()方法能夠獲取全部的public方法,包含父類、接口中繼承來的public方法。
/**============================================= * getMethods() * ============================================= */
System.out.println("========================================");
Arrays.asList(concreteClass.getMethods()).forEach(
s -> { System.out.println("public類型的方法:" + s); }
);
複製代碼
輸出:
public類型的方法:public void com.byron4j.hightLevel.reflection.ConcreteClass.method1()
public類型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method2(java.lang.String)
public類型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method4()
public類型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method5(int)
public類型的方法:public static int com.byron4j.hightLevel.reflection.BaseClass.method5()
public類型的方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
public類型的方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public類型的方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public類型的方法:public boolean java.lang.Object.equals(java.lang.Object)
public類型的方法:public java.lang.String java.lang.Object.toString()
public類型的方法:public native int java.lang.Object.hashCode()
public類型的方法:public final native java.lang.Class java.lang.Object.getClass()
public類型的方法:public final native void java.lang.Object.notify()
public類型的方法:public final native void java.lang.Object.notifyAll()
複製代碼
getConstructors()方法可以獲取全部的public類型構造器
/**============================================= * getConstructors() * ============================================= */
System.out.println("========================================");
Arrays.asList(concreteClass.getConstructors()).forEach(
s -> { System.out.println("public類型的構造器:" + s); }
);
複製代碼
輸出:
public類型的構造器:public com.byron4j.hightLevel.reflection.ConcreteClass(int)
getFields()方法能夠獲取全部的public屬性。包含父類、接口中的屬性。
/**============================================= * getFields() * ============================================= */
System.out.println("========================================");
Arrays.asList(concreteClass.getFields()).forEach(
s -> { System.out.println("public類型的屬性:" + s); }
);
複製代碼
輸出:
public類型的屬性:public int com.byron4j.hightLevel.reflection.ConcreteClass.publicInt
public類型的屬性:public static final int com.byron4j.hightLevel.reflection.BaseInterface.interfaceInt
public類型的屬性:public int com.byron4j.hightLevel.reflection.BaseClass.baseInt
複製代碼
getAnnotations()方法能夠獲取全部的註解。可是隻有保留策略爲RUNTIME的註解。
咱們給 類加上註解@Deprecated。
@Deprecated
public class ConcreteClass extends BaseClass implements BaseInterface{...}
/////////////////////////////////////
/**============================================= * getAnnotations() * ============================================= */
System.out.println("========================================");
Arrays.asList(concreteClass.getAnnotations()).forEach(
s -> { System.out.println("註解:" + s); }
);
複製代碼
輸出:
註解:@java.lang.Deprecated()
反射API提供了幾個方法能夠在運行時分解類的成員變量以及設置其值。
除了前面的getFields()方法能獲取所有public屬性以外,還提供了一個獲取指定屬性名稱的方法getField()。這個方法會在該class-->接口-->父類的順序尋找指定的屬性。
場景一:咱們在ConcreteClass類、BaseClass類、BaseInterface接口分別增長一個屬性: public String name = "currClass---NAME";
public String name = "superClass---NAME";
public String name = "superInterface---NAME";
System.out.println(concreteClass.getField("name"));
輸出爲:
public java.lang.String com.byron4j.hightLevel.reflection.ConcreteClass.name
複製代碼
場景二:咱們在BaseClass類、BaseInterface接口分別增長一個屬性: public String name = "superClass---NAME";
public String name = "superInterface---NAME";
這時候獲取的屬性name是接口中的屬性:
public static final java.lang.String com.byron4j.hightLevel.reflection.BaseInterface.name
場景三:咱們僅僅在BaseClass類增長一個屬性: public String name = "superClass---NAME";
這時候獲取的屬性name是父類中的屬性:
public java.lang.String com.byron4j.hightLevel.reflection.BaseClass.name
若是是獲取不存在的屬性,則出現異常:
System.out.println(concreteClass.getField("hello"));
輸出:
java.lang.NoSuchFieldException: hello
try {
System.out.println( concreteClass.getField("interfaceInt").getDeclaringClass() );
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
複製代碼
輸出:
interface com.byron4j.hightLevel.reflection.BaseInterface
getType()方法返回屬性的類型的class實例。
System.out.println(concreteClass.getField("interfaceInt").getType().getCanonicalName());
複製代碼
輸出:
int
Field.get(Object) 獲取該屬性的值。
Field field = concreteClass.getField("publicInt");
System.out.println("屬性的類型:"+field.getType());
ConcreteClass obj= new ConcreteClass(7);
System.out.println("獲取屬性值:" + field.get(obj));
field.set(obj, 77);
System.out.println("獲取屬性值:" + field.get(obj));
複製代碼
輸出:
屬性的類型:int 獲取屬性值:7 獲取屬性值:77
Field.get()返回的是一個Object類型,若是是原始類型則返回其包裝類型。若是是final屬性,set() 方法拋出java.lang.IllegalAccessException。
java中在類以外是不能訪問private變量的。可是經過反射能夠關閉檢查訪問修飾符的機制。
Field privateField = concreteClass.getDeclaredField("privateString");
System.out.println(privateField.get(obj));
複製代碼
輸出,不能訪問private的屬性:
java.lang.IllegalAccessException: Class com.byron4j.hightLevel.reflection.ReflectionDemo2 can not access a member of class com.byron4j.hightLevel.reflection.ConcreteClass with modifiers "private"
設置可訪問機制Field.setAccessible(true);:
Field privateField = concreteClass.getDeclaredField("privateString");
privateField.setAccessible(true);
System.out.println(privateField.get(obj));
privateField.set(obj, "新的私有屬性值[value]");
System.out.println(privateField.get(obj));
複製代碼
輸出:
private string 新的私有屬性值[value]
使用反射技術能夠得到方法的信息以及調用執行它。咱們來學習得到方法、調用方法並訪問私有方法。
咱們可使用 getMethod()方法獲的public class的方法,須要提供方法的名稱、參數類型。若是class找不到指定的方法,則會繼續向上從其父類中查找。
下面咱們以一個獲取HashMap 的put方法的例子來展現如何方法的參數類型、方法訪問修飾符和返回類型。
/*========================================= * 方法 * ======================================== */
Class mapClass = HashMap.class;
Method mapPut = mapClass.getMethod("put", Object.class, Object.class);
//獲取參數類型
System.out.println(Arrays.toString(mapPut.getParameterTypes()));
//獲取返回類型
System.out.println(mapPut.getReturnType());
//訪問修飾符
System.out.println(Modifier.toString(mapPut.getModifiers()));
複製代碼
輸出:
[class java.lang.Object, class java.lang.Object] class java.lang.Object public
能夠利用Method.invoke() 方法調用指定的方法。
//調用方法
Map<String, String> map = new HashMap<String, String>();
mapPut.invoke(map, "key", "val");
System.out.println(map);
複製代碼
輸出:
{key=val}
咱們可使用getDeclaredMethod()方法獲取私有方法,而後關閉訪問限制,便可調用。
/** * 訪問私有方法 */
Method method3 = BaseClass.class.getDeclaredMethod("method3", null);
method3.setAccessible(true);
//靜態方法的調用對象能夠傳入null
method3.invoke(null, null);
複製代碼
輸出:
Method3
咱們可使用getConstructor()方法獲取指定的public構造器。
/** * 反射在構造器中的使用 */
Constructor<?> constructor = ConcreteClass.class.getConstructor(int.class);
System.out.println(Arrays.toString(constructor.getParameterTypes()));
Constructor<?> hashMapConstructor = HashMap.class.getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes()));
複製代碼
輸出:
[int] []
咱們能夠利用constructor 實例的newInstance() 方法獲初始化實例。
/** * 初始化實例 */
Object myObj = constructor.newInstance(10);
Method myObjMethod = myObj.getClass().getMethod("method1", null);
myObjMethod.invoke(myObj, null); //prints "Method1 impl."
HashMap<String,String> myMap = (HashMap<String,String>)hashMapConstructor.newInstance(null);
複製代碼