文章目錄
- 一、反射的作用
- 二、反射的用法
- 2.1 類
- 2.1.1 修飾符
- 2. 1.2 類名
- 2.1.3 繼承的父類
- 2.1.3 實作的接口
- 2.1.4 包資訊
- 2.2 變量
- 2.2.1 特定變量
- 2.2.2 變量類型和變量名
- 2.3 構造器
- 2.3.1 擷取Constructor對象以及對象的參數清單
- 2.3.2 利用Constructor對象執行個體化一個類
- 2.4 方法
- 2.4.1 擷取類中所有方法
- 2.4.2 調用指定方法
- 2.5 數組
- 2.5.1 一維數組指派
- 2.5.1 多元數組指派
- 2.6 泛型
- 2.6.1 方法的傳回類型的泛型
- 2.7 注解
- 三、源碼檢視位址
一、反射的作用
可以通過反射來擷取類的各個組成部分,還可以通過反射來建立對象,甚至構造器是私有的也可以;調用方法,傳參以及擷取方法的傳回值;總體來說可以對構造器,字段,方法,注釋等進行一系列操作。
二、反射的用法
下面将會通過對類,字段,構造器,方法,數組,泛型分别進行描述反射的使用
将對這個類進行案例分析
Animal類
package top.clearlight.coretech.reflect;
public abstract class Animal {
public abstract void move();
abstract void shout();
public void Help() {
System.out.println("Help Ohter");
}
}
People類
package top.clearlight.coretech.reflect;
public class People extends Animal implements Comparable<People> {
private String name;
public int age;
static String sex;
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public People() {
}
public People(String name) {
this.name = name;
}
private People(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(People o) {
return Double.compare(this.age, o.age);
}
@Override
public void move() {
System.out.println("行走");
}
@Override
void shout() {
System.out.println("交流");
}
public List<String> getStringList() {
ArrayList<String> list = new ArrayList<>();
list.add("hhhh");
return list;
}
}
2.1 類
擷取類對象的方式有三種.對象.
- 對象.getClass()
- Class.forName(“全限定類名”); (需要try-catch,類名有可能寫錯)
- 類名.class
2.1.1 修飾符
Class clazz = People.class;
int modifiers = clazz.getModifiers(); // modifiers : 1 (public)
Modifier源碼 : https://gitee.com/huangtianyu/jdk-source/blob/master/java/lang/reflect/Modifier.java
2. 1.2 類名
String name = clazz.getName(); // top.clearlight.coretech.reflect.People
String simpleName = clazz.getSimpleName(); // People
2.1.3 繼承的父類
Class superclass = clazz.getSuperclass(); // class java.lang.Object
2.1.3 實作的接口
類可以實作多個接口,是以傳回Class數組.且隻會傳回目前類所實作的接口 , 父類所實作的接口并不會在傳回的Class數組中.
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
輸出結果 :
interface java.lang.Comparable
2.1.4 包資訊
Package aPackage = clazz.getPackage(); // package top.clearlight.coretech.reflect
2.2 變量
擷取一個類的成員變量
Field[] fields = clazz.getFields(); // 隻能擷取修飾符為public的變量
Field[] declaredFields = clazz.getDeclaredFields(); // 擷取類中所有的變量,包括私有,預設,公有..
2.2.1 特定變量
通過變量名來擷取指定變量,并且對該變量進行指派
- 擷取指定變量
如果是public修飾的變量,可以使用來擷取;但是對于其他修飾符需要使用
getField(String)
方法
getDeclaredField(String)
Field field = clazz.getDeclaredField("name");
-
為特定對象的變量指派
首先建立對象
People p = new People();
field.setAccessible(true); // 将域設定為可通路的
field.set(p, "Jack"); // 若變量為static, 第一個參數設為null,即不需要傳入對象,隻需直接設定值即可.
System.out.println(p.getName()); // Jack
- 為靜态變量指派
Field sex = clazz.getDeclaredField("sex");
sex.set(null, "girl"); // 當然,你傳入類的執行個體效果相同
System.out.println(People.sex); // girl
若對私有變量使用getField(String)方法的話,将會抛出NoSuchFieldException ; 或者傳入的String确實沒有該字段同樣會抛出該異常
2.2.2 變量類型和變量名
Field ageField = clazz.getField("age");
Class<?> type = ageField.getType(); // int
String name1 = ageField.getName(); // age
2.3 構造器
對于構造器,不僅可以擷取所有Constructor對象,來列出參數清單,還可以擷取指定參數清單的Constructor對象;同樣可以通過獲得的Constructor對象來執行個體化一個類
擴充閱讀 : Java反射 - 通過反射建立有參與無參構造函數的對象
2.3.1 擷取Constructor對象以及對象的參數清單
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 傳回所有的Constructor對象
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("構造器:" + declaredConstructor);
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); // 傳回該Constructor對象的參數清單
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
}
輸出結果:
2.3.2 利用Constructor對象執行個體化一個類
1、擷取指定的構造器對象
Constructor<People> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);
2、将私有構造器設為可通路的
declaredConstructor.setAccessible(true);
3、建立執行個體并為構造器傳入相應的參數
People mary = declaredConstructor.newInstance("Mary", 18);
對于上述,擷取構造器對象的方法有
getConstructor
與
getDeclaredConstructor
,兩者的差別與擷取字段是的兩個方法一樣,一個隻能擷取public,而另一個還可以擷取預設,甚至是private所修飾的,是以可以建立私有構造器的執行個體。
2.4 方法
與構造器的使用類似,其中不同的是擷取所有方法:
-
傳回的方法集是本類以及父類還有Object的所有
getMethods
修飾的方法;
public
-
傳回的方法集隻是本類所有的方法集合。
getDeclaredMethods
2.4.1 擷取類中所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("方法名:" + declaredMethod.getName());
int modifiers1 = declaredMethod.getModifiers();
System.out.println("修飾符:" + modifiers);
System.out.println("傳回類型:" + declaredMethod.getReturnType());
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
System.out.println("參數清單:");
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
System.out.println();
}
輸出結果 :
方法名:move
修飾符:1
傳回類型:void
參數清單:
方法名:getAge
修飾符:1
傳回類型:int
參數清單:
… …
2.4.2 調用指定方法
Constructor<People> declaredConstructor1 = People.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor1.setAccessible(true);
People p2 = declaredConstructor1.newInstance("Bob", 21);
p.setAge(23);
Method compareTo = clazz.getMethod("compareTo", People.class);
Object comPareMethod = compareTo.invoke(p, p2);
System.out.println(comPareMethod); // 1
-
getMethod
方法
第一個參數 : 方法名
第二個參數 : (可變參數)方法的參數類型
-
invoke
方法
第一個參數 : 非靜态方法的話傳入調用該方法的執行個體對象 ; 若是靜态方法的話傳入 null
第二個參數 : (可變參數) 對應類型的參數的值
2.5 數組
擴充閱讀 :
Java反射 - 建立數組執行個體Java中的反射機制(三) 反射與數組
Java反射機制通過 java.lang.reflect.Array
這個類來處理數組.
2.5.1 一維數組指派
// 建立int類型的數組,長度為10
int[] ints = (int[]) Array.newInstance(int.class, 10);
System.out.println(Arrays.toString(ints));
Array.set(ints, 3, 18);
int i = (int) Array.get(ints, 3);
System.out.println(i);
輸出結果:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
18
2.5.1 多元數組指派
int[][] ints2 = (int[][]) Array.newInstance(int.class, 5, 10);
System.out.println(Arrays.toString(ints2));
int[] ints3 = (int[]) Array.get(ints2, 3);
Array.setInt(ints3, 5, 21);
for (int[] ints4 : ints2) {
System.out.println(Arrays.toString(ints4));
}
輸出結果:
2.6 泛型
在一些情況下是可以在運作期擷取到泛型的資訊
2.6.1 方法的傳回類型的泛型
Method getStringList = People.class.getMethod("getStringList", null);
Type genericReturnType = getStringList.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
ParameterizedType type1 = (ParameterizedType) genericReturnType;
Type[] actualTypeArguments = type1.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
輸出結果:
class java.lang.String
2.7 注解
擷取注解的方式無論是擷取類注解,方法注解還是參數注解,方式都是相似的,隻要掌握一種就可以快速學會其他擷取注解内容的方法.
關于注解的學習請看: 注解知識點總結 - Java
任意位置的自定義注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Container {
Person[] value();
}
自定義的變量注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Inherited
public @interface FieldAnnotation {
String value();
}
容器注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Container {
Person[] value();
}
通過@Repeatable修飾的注解
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Container.class)
public @interface Person {
String role() default "artist";
}
定義方法來擷取字段以及指定方法的注解
package top.clearlight.blog.annotation.reflecttest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
// @MyAnnotation(msg = "type annotation", id = 1)
public class ReflectTest1 extends SuperClass{
@FieldAnnotation(value = "file annotation")
private int age;
@Person(role = "student")
@Person(role = "cooker")
@Person(role = "superman")
public void test() {
System.out.println("用來測試Repeatable注解");
}
@MyAnnotation(id = 4)
public void test2() {
System.out.println("直接擷取多個方法的注解");
}
public static void main(String[] args) {
Class<ReflectTest1> rt = ReflectTest1.class;
// 獲得的父類的類注解,該注解已經通過@Inherited修飾
System.out.println("擷取類注解");
Annotation[] typeAns = rt.getAnnotations();
for (Annotation typeAn : typeAns) {
if (typeAn instanceof MyAnnotation) {
MyAnnotation man = (MyAnnotation) typeAn;
System.out.println("msg(): " + man.msg());
System.out.println("id(): " + man.id());
}通過@Repeatable修飾的方法的注解
}
// 擷取的字段的注解
System.out.println("\n擷取字段注解");
try {
Field age = rt.getDeclaredField("age");
// 通路private修飾的字段, 需要修改通路權限
age.setAccessible(true);
// 獲得指定的注解
FieldAnnotation fieldAn = age.getAnnotation(FieldAnnotation.class);
if (fieldAn != null) {
System.out.println("字段的value(): " + fieldAn.value());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 擷取所有方法的注解
System.out.println("\n擷取方法注解");
Method[] methodsAns = rt.getDeclaredMethods();
for (Method methodsAn : methodsAns) {
Annotation[] ans = methodsAn.getAnnotations();
System.out.println(methodsAn.getName() + "方法的注解:");
if (ans.length == 0) {
System.out.println("無");
}
for (Annotation an : ans) {
System.out.println(an);
}
}
// 擷取通過@Repeatable修飾的方法的注解,
System.out.println("\n擷取指定方法的注解");
try {
Method test = rt.getDeclaredMethod("test", null);
Container conAn = test.getAnnotation(Container.class);
System.out.println("人的角色: ");
for (Person person : conAn.value()) {
System.out.println(person.role());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
結果:
擷取類注解
msg(): 由父類而得
id(): 2
擷取字段注解
字段的value(): file annotation
擷取方法注解
test2方法的注解:
@top.clearlight.blog.annotation.reflecttest.MyAnnotation(msg=“hhhh”, id=4)
main方法的注解:
無
test方法的注解:
@top.clearlight.blog.annotation.reflecttest.Container(value={@top.clearlight.blog.annotation.reflecttest.Person(role=“student”), @top.clearlight.blog.annotation.reflecttest.Person(role=“cooker”), @top.clearlight.blog.annotation.reflecttest.Person(role=“superman”)})
擷取指定方法的注解
人的角色:
student
cooker
superman