Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Java反射

Java反射

編輯:關於Android編程

什麼是反射

反射(Reflection)能夠讓運行於JVM中的程序檢測和修改運行時的行為。”這個概念常常會和內省(Introspection)混淆,以下是這兩個術語在Wikipedia中的解釋:

內省用於在運行時檢測某個對象的類型和其包含的屬性

反射用於在運行時檢測和修改某個對象的結構及其行為

從它們的定義可以看出,內省是反射的一個子集。有些語言支持內省,但並不支持反射,如C++

內省示例:instanceof 運算符用於檢測某個對象是否屬於特定的類
if (obj instanceof Dog) {
    Dog d = (Dog) obj;
    d.bark();
}
反射示例:Class.forName()方法可以通過類或接口的名稱(一個字符串或完全限定名)來獲取對應的Class對象。forName方法會觸發類的初始化
// 使用反射
Class c = Class.forName("classpath.and.classname");
Object dog = c.newInstance();
Method m = c.getDeclaredMethod("bark", new Class[0]);
m.invoke(dog);
在Java中,反射更接近於內省,因為你無法改變一個對象的結構。雖然一些API可以用來修改方法和屬性的可見性,但並不能修改結構

為什麼需要反射

反射能夠讓我們:

在運行時檢測對象的類型

動態構造某個類的對象

檢測類的屬性和方法

任意調用對象的方法

修改構造函數、方法、屬性的可見性

以及其他

反射是框架中常用的方法

例如,JUnit 通過反射來遍歷包含 @Test 注解的方法,並在運行單元測試時調用它們。(這個連接中包含了一些JUnit 的使用案例)

對於Web框架,開發人員在配置文件中定義他們對各種接口和類的實現。通過反射機制,框架能夠快速地動態初始化所需要的類

例如,Spring框架使用如下的配置文件:

    

當Spring容器處理元素時,會使用Class.forName(“com.programcreek.Foo”)來初始化這個類,並再次使用反射獲取元素對應的setter方法,為對象的屬性賦值

Servlet也會使用相同的機制:


    someServlet
    com.programcreek.WhyReflectionServlet

處理泛型

Java 5中引入了泛型的概念之後,Java反射API也做了相應的修改,以提供對泛型的支持。由於類型擦除機制的存在,泛型類中的類型參數等信息,在運行時刻是不存在的。JVM看到的都是原始類型。對此,Java 5對Java類文件的格式做了修訂,添加了Signature屬性,用來包含不在JVM類型系統中的類型信息。 在運行時刻,JVM會讀取Signature屬性的內容並提供給反射API來使用。
比如在代碼中聲明了一個域是List類型的,雖然在運行時刻其類型會變成原始類型List,但是仍然可以通過反射來獲取到所用的實際的類型參數
Field field = Pair.class.getDeclaredField("myList"); //myList的類型是List 
Type type = field.getGenericType(); 
if (type instanceof ParameterizedType) {     
    ParameterizedType paramType = (ParameterizedType) type;     
    Type[] actualTypes = paramType.getActualTypeArguments();     
    for (Type aType : actualTypes) {         
        if (aType instanceof Class) {         
            Class clz = (Class) aType;             
            System.out.println(clz.getName()); //輸出java.lang.String         
        }     
    } 
}

Class類

在程序運行期間,Java運行時系統始終為所有的對象維護一個被稱為運行時的類型標識。這個信息跟蹤著這個對象所屬的類。可以通過專門的Java類訪問這些信息,保存這些信息的類被稱為Class類
獲取Class對象的三種方法:
1. 通過調用Object類中的getClass()方法返回一個Class對象

Object myObject = new Object();
Class myObjectClass = myObject.getClass();
通過調用靜態方法forName獲得類名對應的Class對象
String className = "java.util.Date";
Class cl = Class.forName(className);

注意:在使用Class.forName()方法時,必須提供一個類的全名,這個全名包括類所在的包的名字。

如果在調用該方法時,沒有找到該路徑的類,將會拋出ClassNotFoundException。
獲得Class對象的第三種方法非常簡單,如果T是任意的Java類型,T.class將代表匹配的類對象
Class cl1 = Date.class;
Class cl2 = int.class;

補充:可通過下面方式訪問類的父類

Object myObject = new Object();
Class myObjectClass = myObject.getSuperclass();

獲取類名

通過Class對象可以獲取兩個版本的類名:
1. 通過getName()方法返回類的全限定類名(包含包名):

Class aClass = ... //獲取Class對象
String className = aClass.getName();
通過getSimpleName()方法返回類名(不包含包名)
Class aClass = ... //獲取Class對象
String className = aClass.getSimpleName();

獲取修飾符

可以通過Class對象的getModifiers()方法來訪問一個類的修飾符,該方法通過返回一個整型數值,用不同的位開關描述public/private/static等修飾符的使用狀況。

Class aClass = ... //獲取Class對象
int modifiers = aClass.getModifiers();

還可以使用Modifier類中的isPublic、isPrivate或isFinal判斷方法或構造器是否是public、private或final。

Modifier.isAbstract(int modifiers);
Modifier.isFinal(int modifiers);
Modifier.isInterface(int modifiers);
Modifier.isNative(int modifiers);
Modifier.isPrivate(int modifiers);
Modifier.isProtected(int modifiers);
Modifier.isPublic(int modifiers);
Modifier.isStatic(int modifiers);
Modifier.isStrict(int modifiers);
Modifier.isSynchronized(int modifiers);
Modifier.isTransient(int modifiers);
Modifier.isVolatile(int modifiers);

獲取包信息

可通過以下方式獲取包信息:

...
Object object = new Object();
Class cl = object.getClass();
System.out.println(cl.getPackage());
...
/*output
package java.lang, Java Platform API Specification, version 1.7
*/

獲取實現的接口集合

可通過調用Class對象的getInterfaces()方法獲取一個類實現的接口集合

Class aClass = ... //獲取Class對象
Class[] interfaces = aClass.getInterfaces();

注意:getInterfaces()方法僅僅只返回當前類所實現的接口,不包括當前類的父類所實現的接口。

獲取構造器

可通過調用Class對象的getConstructors()方法獲取一個類的構造函數

Class aClass = ... //獲取Class對象
Constructor[] constructors = aClass.getConstructors();

返回的Constructor數組包含每一個聲明為public的構造方法。


還可以通過給定的構造方法的參數類型獲取指定的構造方法,如下:返回的構造方法的方法參數為String類型

Class aClass = ... //獲取Class對象
Constructor constructor = aClass.getConstructor(new Class[]{String.class});

注意:如果沒有指定的構造方法能匹配給定的方法參數,則會拋出NoSuchMethodException異常。


還可以獲取指定構造方法的方法參數信息

Constructor constructor = ... //獲取Constructor對象
Class[] parameterTypes = constructor.getParameterTypes();

利用Constructor對象實例化一個類

Constructor constructor = ... //獲取Constructor對象
Class[] parameterTypes = constructor.getParameterTypes();

獲取方法

可通過調用Class對象的getMethods()方法獲取一個類的所有方法

Class aClass = ...//獲取Class對象
Method[] methods = aClass.getMethods();

返回的Method對象數組包含了指定類中聲明為public的所有變量集合。


還可以通過具體的參數類型來獲取指定的方法

Class  aClass = ...//獲取Class對象
Method method = aClass.getMethod("doSomething", new Class[]{String.class});

注意:如果根據給定的方法名稱以及參數類型無法匹配到相應的方法,則會拋出NoSuchMethodException


獲取指定方法的方法參數以及返回類型

Method method = ... //獲取Class對象
Class[] parameterTypes = method.getParameterTypes();
Class returnType = method.getReturnType();

通過Method對象調用方法

//獲取一個方法名為doSomesthing,參數類型為String的方法
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

獲取變量

可通過調用Class對象的getFields()方法獲取一個類的成員變量

Class aClass = ... //獲取Class對象
Field[] method = aClass.getFields();

返回的Field對象數組包含了指定類中聲明為public的所有變量集合


還可以通過具體的變量名稱獲取指定的變量

Class aClass = ... //獲取Class對象
Field[] method = aClass.getFields("someField");

注意:在調用getField()方法時,如果根據給定的方法參數沒有找到對應的變量,那麼就會拋出NoSuchFieldException


通過調用Field對象的getName()方法獲取它的變量名稱

Field field = ... //獲取Field對象
String fieldName = field.getName();

通過調用Field對象getType()方法來獲取一個變量的類型(如String, int等等)

Class aClass = ... //獲取Class對象
Field field = aClass.getField("someField");
Object fieldType = field.getType();

通過調用Field.get()或Field.set()方法獲取或設置(get/set)變量值

Class  aClass = MyObject.class
Field field = aClass.getField("someField");

MyObject objectInstance = new MyObject();

Object value = field.get(objectInstance);

field.set(objetInstance, value);

獲取指定類的getters和setters

Getter:Getter方法的名字以get開頭,沒有方法參數,返回一個值 Setter:Setter方法的名字以set開頭,有一個方法參數

一個獲取getter方法和setter方法的例子:

public static void printGettersSetters(Class aClass){
  Method[] methods = aClass.getMethods();

  for(Method method : methods){
    if(isGetter(method)) System.out.println("getter: " + method);
    if(isSetter(method)) System.out.println("setter: " + method);
  }
}

public static boolean isGetter(Method method){
  if(!method.getName().startsWith("get"))      return false;
  if(method.getParameterTypes().length != 0)   return false;
  if(void.class.equals(method.getReturnType()) return false;
  return true;
}

public static boolean isSetter(Method method){
  if(!method.getName().startsWith("set")) return false;
  if(method.getParameterTypes().length != 1) return false;
  return true;
}

獲取私有變量

可以通過調用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法獲取私有變量和受保護變量,不包括超類的成員;Class.getField(String name)和Class.getFields()只會返回公有變量,其中包括超類的公有變量,而無法獲取私有變量。

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}
/**********************************************************************/
PrivateObject privateObject = new PrivateObject("The Private Value");

Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString");

privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);

這個例子會輸出”fieldValue = The Private Value”,The Private Value是PrivateObject實例的privateString私有變量的值,注意調用PrivateObject.class.getDeclaredField("privateString")方法會返回一個私有變量,這個方法返回的變量是定義在PrivateObject類中的而不是在它的父類中定義的變量。
注意:privateStringField.setAccessible(true)這行代碼,通過調用setAccessible()方法會關閉指定類Field實例的反射訪問檢查,這行代碼執行之後不論是私有的、受保護的以及包訪問的作用域,你都可以在任何地方訪問,即使你不在他的訪問權限作用域之內。但是你如果你用一般代碼來訪問這些不在你權限作用域之內的代碼依然是不可以的,在編譯的時候就會報錯。

獲取私有方法

可以通過調用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()方法獲取私有方法,不包括超類的成員;Class.getMethod(String name, Class[] parameterTypes)和Class.getMethods()方法,只會返回公有的方法,其中包括超類的公有方法,而無法獲取私有方法。

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }

  private String getPrivateString(){
    return this.privateString;
  }
}
/*******************************************************************/
PrivateObject privateObject = new PrivateObject("The Private Value");

Method privateStringMethod = PrivateObject.class.
        getDeclaredMethod("getPrivateString", null);

privateStringMethod.setAccessible(true);

String returnValue = (String)
        privateStringMethod.invoke(privateObject, null);

System.out.println("returnValue = " + returnValue);

這個例子會輸出"returnValue = The Private Value",The Private Value是PrivateObject實例的getPrivateString()方法的返回值。PrivateObject.class.getDeclaredMethod("privateString")方法會返回一個私有方法,這個方法是定義在PrivateObject類中的而不是在它的父類中定義的。
注意:Method.setAcessible(true)這行代碼,通過調用setAccessible()方法會關閉指定類的Method實例的反射訪問檢查,這行代碼執行之後不論是私有的、受保護的以及包訪問的作用域,你都可以在任何地方訪問,即使你不在他的訪問權限作用域之內。但是你如果你用一般代碼來訪問這些不在你權限作用域之內的代碼依然是不可以的,在編譯的時候就會報錯。

獲取注解

什麼是注解?
注解是Java 5的一個新特性。注解是插入你代碼中的一種注釋或者說是一種元數據(meta data)。這些注解信息可以在編譯期使用預編譯工具進行處理(pre-compiler tools),也可以在運行期使用Java反射機制進行處理。
下面是一個類注解的例子:

@MyAnnotation(name="someName",  value = "Hello World")
public class TheClass {
}

在TheClass類定義的上面有一個@MyAnnotation的注解。注解的定義與接口的定義相似,下面是MyAnnotation注解的定義:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)

public @interface MyAnnotation {
  public String name();
  public String value();
}

在interface前面的@符號表名這是一個注解,一旦你定義了一個注解之後你就可以將其應用到你的代碼中。

說明:在注解定義中的兩個指示@Retention(RetentionPolicy.RUNTIME)和@Target(ElementType.TYPE),說明了這個注解該如何使用。
1. @Retention(RetentionPolicy.RUNTIME)表示這個注解可以在運行期通過反射訪問。如果你沒有在注解定義的時候使用這個指示那麼這個注解的信息不會保留到運行期,這樣反射就無法獲取它的信息。
2. @Target(ElementType.TYPE) 表示這個注解只能用在類型上面(比如類跟接口)。你同樣可以把Type改為Field或者Method,或者你可以不用這個指示,這樣的話你的注解在類,方法和變量上就都可以使用了。


類注解
下是一個訪問類注解的例子

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你還可以像下面這樣指定訪問一個類的注解

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

方法注解
下面是一個方法注解的例子

public class TheClass {
  @MyAnnotation(name="someName",  value = "Hello World")
  public void doSomething(){}
}

你可以像這樣訪問方法注解:

Method method = ... //獲取方法對象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你可以像這樣訪問指定的方法注解

Method method = ... // 獲取方法對象
Annotation annotation = method.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

參數注解
方法參數也可以添加注解,就像下面這樣

public class TheClass {
  public static void doSomethingElse(
        @MyAnnotation(name="aName", value="aValue") String parameter){
  }
}

你可以通過Method對象來訪問方法參數注解

Method method = ... //獲取方法對象
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();

int i=0;
for(Annotation[] annotations : parameterAnnotations){
  Class parameterType = parameterTypes[i++];

  for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("param: " + parameterType.getName());
        System.out.println("name : " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
  }
}

需要注意的是Method.getParameterAnnotations()方法返回一個注解類型的二維數組,每一個方法的參數包含一個注解數組。


變量注解
下面是一個變量注解的例子

public class TheClass {

  @MyAnnotation(name="someName",  value = "Hello World")
  public String myField = null;
}

你可以像這樣來訪問變量的注解

Field field = ... //獲取方法對象
Annotation[] annotations = field.getDeclaredAnnotations();

for(Annotation annotation : annotations){
 if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
 }
}

你可以像這樣訪問指定的變量注解

Field field = ...//獲取方法對象

Annotation annotation = field.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved