文章目录
- 一、反射的作用
- 二、反射的用法
- 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