一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Java反射各種應用

詳解Java反射各種應用

2020-07-24 13:50byhieg Java教程

Java除了給我們提供在編譯期得到類的各種信息之外,還通過反射讓我們可以在運行期間得到類的各種信息。通過反射獲取類的信息,得到類的信息之后,就可以獲取很多相關內容。下面跟著小編一起來看下吧

Java除了給我們提供在編譯期得到類的各種信息之外,還通過反射讓我們可以在運行期間得到類的各種信息。通過反射獲取類的信息,得到類的信息之后,就可以獲取以下相關內容:

  • Class對象
  • 構造器
  • 變量
  • 方法
  • 私有變量與私有方法
  • 注解
  • 泛型
  • 數組

本文也將從上面幾個方面來介紹Java反射。本文涉及的所有代碼均在反射代碼

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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對象,已經通過上面的代碼得到了。

?
1
2
3
4
String fullClassName = exampleObjectClass.getName();
String simpleClassName = exampleObjectClass.getSimpleName();
System.out.println(fullClassName);
System.out.println(simpleClassName);

結果如下:

?
1
2
cn.byhieg.reflectiontutorial.ExampleObject
ExampleObject

得到類的包名、父類和實現的接口

類的包名和父類,可以通過如下代碼得到。

?
1
2
3
4
5
6
//得到包信息
Package aPackage = exampleObjectClass.getPackage();
System.out.println(aPackage);
//得到父類
Class superClass = exampleObjectClass.getSuperclass();
System.out.println(superClass.getSimpleName());

結果如下:

?
1
2
package cn.byhieg.reflectiontutorial
FatherObject

很顯然,得到父類的返回值也是一個Class對象,那么可以利用這個對象得到父類的一些信息,比如判斷父類是不是抽象類

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

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

可以使用java.lang.reflect.Modifier類中的方法來檢查修飾符的類型:

?
1
2
3
4
5
6
7
8
9
10
11
12
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);

此外,我們還可以得到父類實現的接口

?
1
2
3
//得到接口
Class[] classes = superClass.getInterfaces();
System.out.println("父類的接口" + classes[0]);

因為Java類可以實現很多接口,所以用的數組,但在實際使用的時候,需要先判斷數組的長度。

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

構造器

利用Java反射可以得到一個類的構造器,并根據構造器,在運行時動態的創建一個對象。首先,Java通過以下方式獲取構造器的實例:

?
1
2
3
4
5
//構造器
Constructor[] constructors = exampleObjectClass.getConstructors();
for (Constructor constructor : constructors){
 System.out.println(constructor.toString());
}

結果如下:

?
1
2
3
public cn.byhieg.reflectiontutorial.ExampleObject(int,java.lang.Integer)
public cn.byhieg.reflectiontutorial.ExampleObject(java.lang.String)
public cn.byhieg.reflectiontutorial.ExampleObject()

如果,事先知道要訪問的構造方法的參數類型,可以利用如下方法獲取指定的構造方法,例子如下:

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

結果顯然是:

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

還可以用如下方式得到另一個構造器

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

此外,如果我們不知道構造器的參數,只能得到所有的構造器對象,那么可以用如下方式得到每一個構造器對想的參數:

?
1
2
3
4
5
6
7
8
Constructor[] constructors = exampleObjectClass.getConstructors();
for (Constructor constructor : constructors){
Class[] parameterTypes = constructor.getParameterTypes();
System.out.println("構造器參數如下========================");
for (Class clz : parameterTypes){
 System.out.println("參數類型 " + clz.toString());
}
}

結果如下:

?
1
2
3
4
5
6
7
8
9
構造器參數如下========================
 
參數類型 class java.lang.String
 
構造器參數如下========================
 
參數類型 int
 
參數類型 class java.lang.Integer

這里,可以看出無參構造方法,是不打印出結果的。基本類型的Class對象和引用類型的Class對象toString()方法是不一樣的。

現在,可以根據構造器的各種信息,動態創建一個對象。

?
1
2
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的變量:

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

結果如下:

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

很顯然,得到的都是public的變量,上述的private的變量score,并沒有得到。

和構造器一樣的得到方式一樣,我們可以指定一個參數名,然后得到指定的變量:

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

上述的變量的toString方法得到的名字太長,Java對Field類提供了getName的方法,返回類中寫的變量名字,上面的代碼就可以改成field.getName()。

反射不僅提供了得到變量的方法,還提供了設置變量值的方式。通過如下方法可以對一個動態生成的類,改變其變量值:

?
1
2
3
4
ExampleObject object = ((ExampleObject) constructor1.newInstance("byhieg"));
System.out.println("原先的age是 " + object.age);
field.set(object,10);
System.out.println("更改之后的age是" + object.age);

結果如下:

?
1
2
原先的age是 30
更改之后的age是10

根據上面的代碼,得到名字為age的Field對象,然后調用該對象的set方法,傳入一個對象與要更改的值,就可以改變該對象的值了。注意,此方法不僅僅對成員變量有用,對靜態變量也可以。當然,如果是靜態變量,傳入null,不用傳對象,也是可以的。

方法

Java反射給我們除了給我們提供類的變量信息之外,當然也給我們提供了方法的信息,反射可以讓我們得到方法名,方法的參數,方法的返回類型,以及調用方法等功能。

首先,通過如下代碼得到方法:

?
1
2
3
4
5
//輸出類的public方法
Method[] methods = exampleObjectClass.getMethods();
for (Method method : methods){
 System.out.println("method = "+ method.getName());
}

和獲取變量一樣似曾相識的代碼,這里直接調用了getName,來得到類中寫的方法名。寫到這里,大家應該自然想到,Java同樣提供了根據參數,得到具體的方法。

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

這里與得到變量不同的是,getMethod方法還需要傳入參數的類型信息,反射提供獲取方法參數以及返回類型的方法,得到方法參數的例子如下:

?
1
2
3
4
5
Method method = exampleObjectClass.getMethod("setAge",int.class);
System.out.println(method.getName());
for (Class clz : method.getParameterTypes()){
 System.out.println("方法的參數" + clz.getName());
}

結果如下:

?
1
2
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),才能正常運用。之后的方式就可上面講的一樣了。

注解

先寫一個包含注解的類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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種獲取方式,一種是獲取全部的注解,返回一個數組,第二種是指定得到指定的注解。

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

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

然后,就可以根據得到的注解進行后續的處理,下面是一個處理的例子:

?
1
2
3
4
5
6
7
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對象有很多參數,每個參數有可能有很多注解。例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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泛型是通過擦除來實現的,很難直接得到泛型具體的參數化類型的信息,但是我們可以通過一種間接的形式利用反射得到泛型信息。比如下面這個類:

?
1
2
3
4
5
6
7
8
9
public class GenericObject {
 public List<String> lists;
 public List<String> getLists() {
  return lists;
 }
 public void setLists(List<String> lists) {
  this.lists = lists;
 }
}

如果一個方法返回一個泛型類,我們可以通過method對象去調用getGenericReturnType來得到這個泛型類具體的參數化類型是什么。看下面的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
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

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

  1. 反射得到返回類型為泛型類的方法
  2. 調用getGenericReturnType得到方法返回類型中的參數化類型
  3. 判斷該type對象能不能向下轉型為ParameterizedType
  4. 轉型成功,調用getActualTypeArguments得到參數化類型的數組,因為有的泛型類,不只只有一個參數化類型如Map
  5. 取出數組中的每一個的值,轉型為Class對象輸出。

看結果確實得到了泛型的具體的信息。

如果沒有一個方法返回泛型類型,那么我們也可以通過方法的參數為泛型類,來得到泛型的參數化類型,如上面類中的setLists方法。例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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());
   }
  }
 }

執行的結果如下:

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

因為方法的參數為泛型類型的可能不止一個,所以通過getGenericParameterTypes得到是一個數組,我們需要確定每一個元素,是否是具有參數化類型。后續的步驟與上面類似,就不多說了。

如果連方法參數都不帶泛型類,那么只剩下最后一種情況,通過變量類型,即用Field類。例子如下:

?
1
2
3
4
5
6
7
8
9
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包下面的。

?
1
2
3
4
5
6
7
8
9
10
//創建一個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的形式得到,寫法比較奇怪:

?
1
2
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類里是對這些類的訪問。

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持服務器之家!

原文鏈接:http://www.cnblogs.com/qifengshi/p/6267511.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 青青草99久久精品国产综合 | 色婷婷综合久久久中文字幕 | 亚洲网色 | 大陆男男gayxxxxvideo | 精品国产91久久久久久久 | 91真人毛片一级在线播放 | www.毛片在线观看 | 无限资源在线观看高清 | 亚洲欧美日韩中文字幕久久 | 精品麻豆国产 | 91制片厂(果冻传媒)原档破解 | 国产小视频网站 | 26uuu久久| www.羞羞视频| 冰雪奇缘1完整版免费观看 变形金刚第一部 | 草草视频在线观看 | 日本无卡码一区二区三区 | 亚洲国产精品久久人人爱 | 亚洲区精品久久一区二区三区 | 亚洲香蕉伊在人在线观婷婷 | 亚洲国产精品自在自线观看 | 亚洲激情 欧美 | 久久99热狠狠色AV蜜臀 | 午夜精品网 | 国产精品女主播大秀在线 | 国内精品久久久久久久 | 欧美骚熟 | 好吊色视频988gao在线观看 | porno中国xxxxx| 国内精品久久久久小说网 | 岛国免费大片 | 含羞草国产亚洲精品岁国产精品 | chinese圣水黄金调教 | 校园纯肉H教室第一次 | 午夜想想爱午夜剧场 | 日韩一区在线播放 | 免费一级国产生活片 | 99精品国产美女福到在线不卡 | 青柠影视在线播放观看高清 | 91小视频在线观看免费版高清 | 探花 在线 |