java cookbook 3_CookBook/Java核心/3-Java反射.md at master · zhgdbut/CookBook · GitHub
#Java核心(三)反射
Java反射給我們提供了在運行時檢查甚至修改應用行為的機制。 反射是java高級的核心技術,所有有經驗的程序員都應該理解。
通過反射機制,我們可以在運行時檢視 類、接口、枚舉,獲得他們的結構、方法以及屬性信息,即使在編譯期類是不可訪問的。 我們也可以通過反射創建類實例,調用它的方法,或者改變屬性值。
##Java中的反射
Java的反射是一種很強大的機制,在正常的編程中使用并不多,但它是java的主干,很多Java EE 框架均使用了反射技術:
- **JUnit** 利用反射技術解析@Test注解,從而得到測試的方法并調用它們。
- **Spring** 依賴注入是java反射的典型應用
- **Tomcat** web容器通過解析web.xml文件和請求url,將請求正確的轉發到對應的模塊。
- **Eclipse** 自動完成方法的名稱輸入
- **Struts**
- **Hibernate**
以上這個清單只是小部分,它們全部使用了反射技術,因為正常情況下,它們無法訪問用戶編寫的類、接口以及方法等。
但是我們不建議在正常編程中濫用反射技術,因為我們擁有自己編寫的類的訪問權限了,反射存在以下幾個缺陷:
- **性能較差** 盡管反射解決了動態類型的問題,但是也引入了在classpath 掃描類進行加載的過程,會影響性能。
- **安全限制** 反射需要在運行時獲得訪問權限,但是在security manager中可能是不允許的。 這可能會導致應用運行失敗。
- **安全問題** 通過反射我們可以訪問那些不建議我們訪問的類,例如我們可以訪問private的屬性并修改其值。 這可能引發安全問題導致應用異常。
- **較高的維護代價** 反射相關的代碼難以理解以及調試,代碼的錯誤不能在編譯期展現出來,使用反射的代碼靈活性不高并難以維護。
##反射在類中的使用
在java中,任何對象要么是原始類型或者引用類型。 所有的類、枚舉、數據和其他引用類型均繼承自Object類。
java.lang.Class是所有反射操作的入口。對于任何類型的對象,JVM 會初始化其一個不可變的java.lang.Class 實例來提供檢查對象的運行時的屬性、創建新對象、調用方法、get/set 屬性。
我們來看看Class的重要方法,為了方便起見,我們先創建一些類和接口。
```java
package com.byron4j.hightLevel.reflection;
public interface BaseInterface {
public int interfaceInt=0;
void method1();
int method2(String str);
}
```
```java
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{}
}
```
```java
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實例:
- 通過靜態變量class
- 使用示例的getClass()方法
- java.lang.Class.forName(String 完整的類名),完整的類名包含包名。
原始類型的class、包裝類型的TYPE均可以獲得Class對象。
```java
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,可以幫助框架獲取子類。
####獲取超類Super Class
**getSuperclass()** 方法,返回類的超類(基類、父類)的class實例,如果該類是java.lang.Object、原始類型、接口則返回null。如果該class是數組形式,則該方法返回java.lang.Object。
```java
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
Class的getClasses() 方法可以獲取class的所有繼承的超類、接口和自己定義的公有類、接口、枚舉等的數組形式。
```java
Class[] classARR = concreteClass.getClasses();
System.out.println(Arrays.toString(classARR));
```
輸出:
```java
[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()**獲取當前類型自身定義的所有類、接口,并不包含從父類繼承過來的來、接口。
```java
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的類
class.getDeclaringClass()獲取定義class的類。如果該類不是任何類或接口的成員,則返回null。
```java
/**================================================
* getDeclaringClass
* ================================================
*/
System.out.println(BaseClassInnerClass.class.getDeclaringClass());
System.out.println(Double.TYPE.getDeclaringClass());
```
該類BaseClassInnerClass是在BaseClass中定義的,于是輸出:
```
class com.byron4j.hightLevel.reflection.BaseClass
null
```
#### 獲取包名
getPackage() 方法獲取包的class實例。
```java
/*===========================================
* getPackage()
* ==========================================
*/
System.out.println(concreteClass.getPackage().getName());
```
輸出:
```
com.byron4j.hightLevel.reflection
```
#### 獲取類的修飾符
getModifiers()方法可以獲取class實例的訪問修飾符的個數。java.lang.reflect.Modifier.toString()可以獲取class的修飾符的字符串形式。
```java
/*===========================================
* getModifiers()、Modifier.toString()
* ==========================================
*/
System.out.println(concreteClass.getModifiers());
System.out.println(Modifier.toString(concreteClass.getModifiers()));
```
輸出:
```
1
public
```
#### 獲取類型參數
getTypeParameters()方法獲取class的類型聲明參數,如果有的話。比如集合框架的接口均制定了泛型。
```java
Arrays.asList(Class.forName("java.util.Map").getTypeParameters()).forEach(
s -> { System.out.println(s); }
);
```
輸出:
> K
> V
####獲取class實現的接口
getGenericInterfaces() 可以獲取class已經實現的接口的數組形式,并包含泛型接口。 getInterfaces()方法會返回所有實現的接口,但是不包含泛型接口。
```java
/**=============================================
* 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
java.util.ArrayList實現的接口:interface java.util.RandomAccess
java.util.ArrayList實現的接口:interface java.lang.Cloneable
java.util.ArrayList實現的接口:interface java.io.Serializable
```
####獲取所有的public方法
getMethods()方法可以獲取所有的public方法,包含父類、接口中繼承來的public方法。
```java
/**=============================================
* 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()
```
####獲取class的所有public構造器
getConstructors()方法能夠獲取所有的public類型構造器
```java
/**=============================================
* getConstructors()
* =============================================
*/
System.out.println("========================================");
Arrays.asList(concreteClass.getConstructors()).forEach(
s -> { System.out.println("public類型的構造器:" + s); }
);
```
輸出:
> public類型的構造器:public com.byron4j.hightLevel.reflection.ConcreteClass(int)
####獲取所有的public屬性(成員變量)
getFields()方法可以獲取所有的public屬性。包含父類、接口中的屬性。
```java
/**=============================================
* 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。
```java
@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提供了幾個方法可以在運行時分解類的成員變量以及設置其值。
####獲取public屬性
除了前面的getFields()方法能獲取全部public屬性之外,還提供了一個獲取指定屬性名稱的方法getField()。這個方法會在該class-->接口-->父類的順序尋找指定的屬性。
**場景一**:我們在ConcreteClass類、BaseClass類、BaseInterface接口分別增加一個屬性:
public String name = "currClass---NAME";
public String name = "superClass---NAME";
public String name = "superInterface---NAME";
```java
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
####獲取聲明屬性的類型
```java
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
####Get/Set public類型的屬性值
Field.get(Object) 獲取該屬性的值。
```java
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**。
####Get/Set private類型的屬性值
java中在類之外是不能訪問private變量的。但是通過反射可以關閉檢查訪問修飾符的機制。
```java
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);:
```java
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]
##與方法相關的反射方法
使用反射技術可以獲得方法的信息以及調用執行它。我們來學習獲得方法、調用方法并訪問私有方法。
####獲得public方法
我們可以使用 getMethod()方法獲的public class的方法,需要提供方法的名稱、參數類型。如果class找不到指定的方法,則會繼續向上從其父類中查找。
下面我們以一個獲取HashMap 的put方法的例子來展示如何方法的參數類型、方法訪問修飾符和返回類型。
```java
/*=========================================
* 方法
* ========================================
*/
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
####調用public方法
可以利用Method.invoke() 方法調用指定的方法。
```java
//調用方法
Map map = new HashMap();
mapPut.invoke(map, "key", "val");
System.out.println(map);
```
輸出:
> {key=val}
####調用private方法
我們可以使用getDeclaredMethod()方法獲取私有方法,然后關閉訪問限制,即可調用。
```java
/**
* 訪問私有方法
*/
Method method3 = BaseClass.class.getDeclaredMethod("method3", null);
method3.setAccessible(true);
//靜態方法的調用對象可以傳入null
method3.invoke(null, null);
```
輸出:
> Method3
總結
以上是生活随笔為你收集整理的java cookbook 3_CookBook/Java核心/3-Java反射.md at master · zhgdbut/CookBook · GitHub的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中怎么把生成文件到项目根目录_[
- 下一篇: java并发访问_Java并发访问