程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 詳解Java反射各種使用

詳解Java反射各種使用

編輯:關於JAVA

詳解Java反射各種使用。本站提示廣大學習愛好者:(詳解Java反射各種使用)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解Java反射各種使用正文


Java除了給我們提供在編譯期失掉類的各種信息之外,還經過反射讓我們可以在運轉時期失掉類的各種信息。經過反射獲取類的信息,失掉類的信息之後,就可以獲取以下相關內容:

Class對象 結構器 變量 辦法 公有變量與公有辦法 注解 泛型 數組

本文也將從下面幾個方面來引見Java反射。本文觸及的一切代碼均在反射代碼

首先放出一個Java類作為反射的研討對象,類的內容如下:

public abstract class FatherObject implements Runnable{
 public void doSomething(){
 System.out.println("做事情......");
 }
}
public class ExampleObject extends FatherObject{
 public int age = 30;
 public String name = "byhieg";
 private Integer score = 60;
 public void printName(){
 System.out.println(name);
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getScore() {
 return score;
 }
 public void setScore(Integer score) {
 this.score = score;
 }
 public ExampleObject(){
 }
 public ExampleObject(String name){
 }
 public ExampleObject(int age,Integer score){
 }
 @Override
 public void doSomething() {
 super.doSomething();
 }
 @Override
 public void run() {
 System.out.println("run......");
 }
}

Class對象

我們使用會用到反射這個知識點,一定是想要在運轉時失掉類的信息,依據類的那些信息去做一些特定的操作。那麼,首先無疑就是失掉類的信息,在JDK中提供了Class對象來保管類的信息。所以,反射的第一步就是失掉Class對象。在JDK中提供了兩種方式失掉Class對象。

第一種,假如編寫代碼的時分,就知道Class的名字,可以直接用如下方式失掉Class對象:

Class exampleObjectClass = ExampleObject.class;

第二種,假如在編寫代碼的時分,不知道類的名字,但是在運轉時的時分,可以失掉一個類名的字符串,可以用如下的方式獲取Class對象:

Class exampleObjectClass = Class.forName("cn.byhieg.reflectiontutorial.ExampleObject");

留意,此辦法需求有2個條件,第一,forName中的字符串必需是全限定名,第二,這個Class類必需在classpath的途徑上面,由於該辦法會拋出ClassNotFoundException的異常。

獲取到這個Class對象之後,就可以失掉類的各種信息,掃尾曾經提及了一些信息,上面,說幾個沒提到的類的信息。

失掉類的名字

類的名字有兩種方式失掉,一種是getName(),一種是getSimpleName()。第一種失掉的是全限定名,第二種失掉的是這個類的名字,不帶包名。看上面的例子:Class對象,曾經經過下面的代碼失掉了。

 String fullClassName = exampleObjectClass.getName();
 String simpleClassName = exampleObjectClass.getSimpleName();
 System.out.println(fullClassName);
 System.out.println(simpleClassName);

後果如下:

cn.byhieg.reflectiontutorial.ExampleObject
ExampleObject

失掉類的包名、父類和完成的接口

類的包名和父類,可以經過如下代碼失掉。

 //失掉包信息
 Package aPackage = exampleObjectClass.getPackage();
 System.out.println(aPackage);
 //失掉父類
 Class superClass = exampleObjectClass.getSuperclass();
 System.out.println(superClass.getSimpleName());

後果如下:

package cn.byhieg.reflectiontutorial
FatherObject

很顯然,失掉父類的前往值也是一個Class對象,那麼可以應用這個對象失掉父類的一些信息,比方判別父類是不是籠統類

System.out.println("父類是不是籠統類 " + Modifier.isAbstract(superClass.getModifiers()));

getModifiers可以失掉類的修飾符,從而失掉類的修飾符,當然,這個getModifiers不只僅Class對象可以調用,Method對象可以調用。

可以運用java.lang.reflect.Modifier類中的辦法來反省修飾符的類型:

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);

此外,我們還可以失掉父類完成的接口

 //失掉接口
 Class[] classes = superClass.getInterfaces();
 System.out.println("父類的接口" + classes[0]);

由於Java類可以完成很多接口,所以用的數組,但在實踐運用的時分,需求先判別數組的長度。

上面,重點解說上述列出來的內容。

結構器

應用Java反射可以失掉一個類的結構器,並依據結構器,在運轉時靜態的創立一個對象。首先,Java經過以下方式獲取結構器的實例:

 //結構器
 Constructor[] constructors = exampleObjectClass.getConstructors();
 for (Constructor constructor : constructors){
  System.out.println(constructor.toString());
 }

後果如下:

public cn.byhieg.reflectiontutorial.ExampleObject(int,java.lang.Integer)
public cn.byhieg.reflectiontutorial.ExampleObject(java.lang.String)
public cn.byhieg.reflectiontutorial.ExampleObject()

假如,事前知道要訪問的結構辦法的參數類型,可以應用如下辦法獲取指定的結構辦法,例子如下:

 Constructor constructor = exampleObjectClass.getConstructor(String.class);
 System.out.println(constructor.toString());

後果顯然是:

public cn.byhieg.reflectiontutorial.ExampleObject(java.lang.String)

還可以用如下方式失掉另一個結構器

 Constructor constructor = exampleObjectClass.getConstructor(int.class,Integer.class);
 System.out.println(constructor.toString());

此外,假如我們不知道結構器的參數,只能失掉一切的結構器對象,那麼可以用如下方式失掉每一個結構器對想的參數:

 Constructor[] constructors = exampleObjectClass.getConstructors();
 for (Constructor constructor : constructors){
 Class[] parameterTypes = constructor.getParameterTypes();
 System.out.println("結構器參數如下========================");
 for (Class clz : parameterTypes){
  System.out.println("參數類型 " + clz.toString());
 }
 }

後果如下:

結構器參數如下========================

參數類型 class java.lang.String

結構器參數如下========================

參數類型 int

參數類型 class java.lang.Integer

這裡,可以看出無參結構辦法,是不打印出後果的。根本類型的Class對象和援用類型的Class對象toString()辦法是不一樣的。

如今,可以依據結構器的各種信息,靜態創立一個對象。

 Object object = constructor.newInstance(1,100);
 System.out.println(object.toString());

這個創立對象的方式有2個條件,第一是經過有參結構器創立的,第二,結構器對象必需經過傳入參數信息的getConstructor失掉。

第一個條件,關於無參結構辦法就可以創立的對象,不需求失掉結構器對象,直接Class對象調用newInstance()辦法就直接創立對象。

第二個條件,結構器對象必需經過exampleObjectClass.getConstructor(String.class);這種方式失掉。假如經過getConstructors失掉結構器數組,然後調用指定的結構器對象去創立對象在JDK1.8是會錯的。但是JDK1.6是正常的。

變量

應用Java反射可以在運轉時失掉一個類的變量信息,並且可以依據下面講的方式,創立一個對象,設置他的變量值。首先,經過如下辦法,失掉一切public的變量:

 Field[] fields = exampleObjectClass.getFields();
 for (Field field : fields){
  System.out.println("變量為: " + field.toString());
 }

後果如下:

變量為: public int cn.byhieg.reflectiontutorial.ExampleObject.age
變量為: public java.lang.String cn.byhieg.reflectiontutorial.ExampleObject.name

很顯然,失掉的都是public的變量,上述的private的變量score,並沒有失掉。

和結構器一樣的失掉方式一樣,我們可以指定一個參數名,然後失掉指定的變量:

 Field field = exampleObjectClass.getField("age");
 System.out.println("變量為:" + field.toString());

上述的變量的toString辦法失掉的名字太長,Java對Field類提供了getName的辦法,前往類中寫的變量名字,下面的代碼就可以改成field.getName()。

反射不只提供了失掉變量的辦法,還提供了設置變量值的方式。經過如下辦法可以對一個靜態生成的類,改動其變量值:

 ExampleObject object = ((ExampleObject) constructor1.newInstance("byhieg"));
 System.out.println("原先的age是 " + object.age);
 field.set(object,10);
 System.out.println("更改之後的age是" + object.age);

後果如下:

原先的age是 30
更改之後的age是10

依據下面的代碼,失掉名字為age的Field對象,然後調用該對象的set辦法,傳入一個對象與要更改的值,就可以改動該對象的值了。留意,此辦法不只僅對成員變量有用,對靜態變量也可以。當然,假如是靜態變量,傳入null,不必傳對象,也是可以的。

辦法

Java反射給我們除了給我們提供類的變量信息之外,當然也給我們提供了辦法的信息,反射可以讓我們失掉辦法名,辦法的參數,辦法的前往類型,以及調用辦法等功用。

首先,經過如下代碼失掉辦法:

 //輸入類的public辦法
 Method[] methods = exampleObjectClass.getMethods();
 for (Method method : methods){
  System.out.println("method = "+ method.getName());
 }

和獲取變量一樣素昧平生的代碼,這裡直接調用了getName,來失掉類中寫的辦法名。寫到這裡,大家應該自然想到,Java異樣提供了依據參數,失掉詳細的辦法。

 Method method = exampleObjectClass.getMethod("setAge",int.class);
 System.out.println(method.getName());

這裡與失掉變量不同的是,getMethod辦法還需求傳入參數的類型信息,反射提供獲取辦法參數以及前往類型的辦法,失掉辦法參數的例子如下:

 Method method = exampleObjectClass.getMethod("setAge",int.class);
 System.out.println(method.getName());
 for (Class clz : method.getParameterTypes()){
  System.out.println("辦法的參數" + clz.getName());
 }

後果如下:

setAge
辦法的參數int

失掉辦法前往類型的例子如下:

System.out.println(method.getReturnType().getName());

後果如下:

void

此外,Java反射支持經過invoke調用失掉的辦法。例子如下:

method.invoke(exampleObjectClass.newInstance(),1);

invoke第一個參數是這個對象,第二個參數是變長數組,傳入該辦法的參數。和Field對象異樣,關於靜態辦法異樣,可以傳入null,調用靜態辦法。

公有變量與公有辦法

下面的辦法只能失掉public辦法和變量,無法失掉非public修飾的辦法和變量,Java提供了額定的辦法來失掉非public變量與辦法。即經過getDeclaredFields與getDeclaredMethods辦法失掉公有的變量與辦法,異樣也支持用getDeclaredField(變量名)與getDeclaredMethod(辦法名)的方式失掉指定的變量名與辦法名。但是這樣失掉的Field對象與Method對象無法直接運用,必需讓這些對象調用setAccessible(true),才干正常運用。之後的方式就可下面講的一樣了。

注解

先寫一個包括注解的類:

@MyAnnotation(name="byhieg",value = "hello world")
public class AnnotationObject {
 @MyAnnotation(name="field",value = "變量")
 public String field;
 @MyAnnotation(name="method",value = "辦法")
 public void doSomeThing(){
 System.out.println("做一些事情");
 }
 public void doOtherThing(@MyAnnotation(name="param",value = "參數") String param){ 
 }
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
 public String name();
 public String value();
}

Java給我們提供了在運轉時獲取類的注解信息,可以失掉類注解,辦法注解,參數注解,變量注解。

與下面獲取方式一樣,Java提供了2種獲取方式,一種是獲取全部的注解,前往一個數組,第二種是指定失掉指定的注解。

我們以一個類注解為例,解說以下這兩種獲取方式。

 Class clz = AnnotationObject.class;
 Annotation[] annotations = clz.getAnnotations();
 Annotation annotation = clz.getAnnotation(AnnotationObject.class);

然後,就可以依據失掉的注解停止後續的處置,上面是一個處置的例子:

 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對象調用getAnnotations失掉的,辦法注解和變量注解是一樣的,辨別用method對象與field對象調用getDeclaredAnnotations失掉注解,沒什麼多說的。例子看反射代碼

參數注解是比擬費事的一項,獲取方式比擬失掉,第一步,先獲得method對象,調用getParameterAnnotations,但是這個前往值是一個二維數組,由於method對象有很多參數,每個參數有能夠有很多注解。例子如下:

Method method1 = clz.getMethod("doOtherThing",String.class);
  Annotation[][] annotationInParam = method1.getParameterAnnotations();
  Class[] params = method1.getParameterTypes();
  int i = 0;
  for (Annotation[] annotations: annotationInParam){
   Class para = params[i++];
   for (Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
     MyAnnotation myAnnotation = (MyAnnotation) annotation;
     System.out.println("param: " + para.getName());
     System.out.println("name : " + myAnnotation.name());
     System.out.println("value :" + myAnnotation.value());
    }
   }
  }

泛型

由於Java泛型是經過擦除來完成的,很難直接失掉泛型詳細的參數化類型的信息,但是我們可以經過一種直接的方式應用反射失掉泛型信息。比方上面這個類:

public class GenericObject {
 public List<String> lists;
 public List<String> getLists() {
  return lists;
 }
 public void setLists(List<String> lists) {
  this.lists = lists;
 }
}

假如一個辦法前往一個泛型類,我們可以經過method對象去調用getGenericReturnType來失掉這個泛型類詳細的參數化類型是什麼。看上面的代碼:

 Class clz = GenericObject.class;
  Method method = clz.getMethod("getLists");
  Type genericType = method.getGenericReturnType();
  if(genericType instanceof ParameterizedType){
   ParameterizedType parameterizedType = ((ParameterizedType) genericType);
   Type[] types = parameterizedType.getActualTypeArguments();
   for (Type type : types){
    Class actualClz = ((Class) type);
    System.out.println("參數化類型為 : " + actualClz);
   }
  }

後果如下:

參數化類型為 : class java.lang.String

步驟有點繁瑣,上面一步步解釋:

    反射失掉前往類型為泛型類的辦法 調用getGenericReturnType失掉辦法前往類型中的參數化類型 判別該type對象能不能向下轉型為ParameterizedType 轉型成功,調用getActualTypeArguments失掉參數化類型的數組,由於有的泛型類,不只只要一個參數化類型如Map 取出數組中的每一個的值,轉型為Class對象輸入。

看後果的確失掉了泛型的詳細的信息。

假如沒有一個辦法前往泛型類型,那麼我們也可以經過辦法的參數為泛型類,來失掉泛型的參數化類型,如下面類中的setLists辦法。例子如下:

 Method setMethod = clz.getMethod("setLists", List.class);
  Type[] genericParameterTypes = setMethod.getGenericParameterTypes();
  for (Type genericParameterType: genericParameterTypes){
   System.out.println("GenericParameterTypes為 : " + genericParameterType.getTypeName());
   if(genericParameterType instanceof ParameterizedType){
    ParameterizedType parameterizedType = ((ParameterizedType) genericParameterType);
    System.out.println("ParameterizedType為 :" + parameterizedType.getTypeName());
    Type types[] = parameterizedType.getActualTypeArguments();
    for (Type type : types){
     System.out.println("參數化類型為 : " + ((Class) type).getName());
    }
   }
  }

執行的後果如下:

GenericParameterTypes為 : java.util.List<java.lang.String>
ParameterizedType為 :java.util.List<java.lang.String>
參數化類型為 : java.lang.String

由於辦法的參數為泛型類型的能夠不止一個,所以經過getGenericParameterTypes失掉是一個數組,我們需求確定每一個元素,能否是具有參數化類型。後續的步驟與下面相似,就不多說了。

假如連辦法參數都不帶泛型類,那麼只剩下最後一種狀況,經過變量類型,即用Field類。例子如下:

  Field field = clz.getField("lists");
  Type type = field.getGenericType();
  if (type instanceof ParameterizedType){
   ParameterizedType parameterizedType = ((ParameterizedType) type);
   Type [] types = parameterizedType.getActualTypeArguments();
   for (Type type1 : types) {
    System.out.println("參數化類型 : " + ((Class) type1).getTypeName());
   }
  }

原理和下面的一樣,只不過Type對象是經過field.getGenericType(),剩下的操作相似就不多說了。

關於經過反射獲取泛型的參數化類型的信息的引見就到此為止。

數組

Java反射可以對數組停止操作,包括創立一個數組,訪問數組中的值,以及失掉一個數組的Class對象。

上面,先說復雜的,創立數組以及訪問數組中的值:在反射中運用Array這個類,是reflect包上面的。

 //創立一個int類型的數組,長度為3
 int[] intArray = (int[])Array.newInstance(int.class,3);
 //經過反射的方式,給數組賦值
  for (int i = 0 ;i < intArray.length;i++){
   Array.set(intArray,i,i + 2);
  }
//經過反射的方式,失掉數組中的值
  for (int i = 0 ; i < intArray.length;i++){
   System.out.println(Array.get(intArray,i));
  }

上述就是創立數組,訪問數組中的值應用反射方式。

關於失掉一個數組的Class對象,復雜的可以用int[].class,或許應用Class.forName的方式失掉,寫法比擬奇異:

 Class clz = Class.forName("[I");
 System.out.println(clz.getTypeName());

後果為:

int[]

這個forName中的字符串,[ 表示是數組,I 表示是int,float就是 F,double就是 D 等等,假如要失掉一個普通對象的數組,則用上面的方式:

Class stringClz = Class.forName("[Ljava.lang.String;");

 [ 表示是數組, L 的左邊是類名,類型的左邊是一個;;

這種方式獲取數組的Class對象真實是太繁瑣了。

在失掉數組的Class對象之後,就可以調用他的一些共同的辦法,比方調用getComponentType來失掉數組成員的類型信息,如int數組就是成員類型就是int。

System.out.println(clz.getComponentType().getTypeName());

後果為int

總結

這次,關於反射的各種使用就到此為止,後續能夠會有深化的知識解說。詳細的代碼可以去看反射代碼

在src包外面是各品種,在test類裡是對這些類的訪問。

以上就是本文的全部內容,希望本文的內容對大家的學習或許任務能帶來一定的協助,同時也希望多多支持!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved