1. 相关介绍
1.1 什么是反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
1.2 反射的作用
- 运行时判断任意对象所属的类
- 运行时构造任意类的对象
- 运行时判断任意类所具有的成员变量和方法
- 运行时获取泛型信息
- 运行时调用任意对象的成员变量和方法(包括私有成员变量和方法)
- 运行时处理注解
- 生成代理对象
2. java.lang.Class
程序经过javac.exe命令后,会生成字节码(.class)文件,然后java.exe命令对字节码文件进行解释运行,相当于把字节码文件加载到内存中,充当一个运行时类,作为Class的一个实例。即Class的实例就是一个运行时类,一个具体类的Class实例只有一个。
2.1 获取运行时类的实例
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 方法一
Class clazz1 = People.class;
System.out.println(clazz1);
// 方法二:调用对象的getClass()方法
People people = new People();
Class clazz2 = people.getClass();
System.out.println(clazz2);
// 方法三:调用Class的静态方法forName(),要写全类名。会抛异常
// 常用
Class clazz3 = Class.forName("pers.ljc.test.People");
System.out.println(clazz3);
// 方法四:使用类加载器
ClassLoader classLoader = Test.class.getClassLoader();
Class clazz4 = classLoader.loadClass("pers.ljc.test.People");
// 这四个指向的是同一个
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // true
System.out.println(clazz1 == clazz4); // true
}
}
可以充当Class实例的有
- 具体类(外部类,成员内部类,静态内部类,局部内部类,匿名内部类均可)
- 接口
- 数组
- 枚举类
- 注解
- 基本数据类型
- void
- Class自身
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Object.class;
Class c2 = Map.class;
Class c3 = int[][].class;
Class c4 = ElementType.class;
Class c5 = Override.class;
Class c6 = int.class;
Class c7 = void.class;
Class c8 = Class.class;
// 数组的类型和维度一样时,Class就是同一个
int[] a = new int[20];
int[] b = new int[100];
System.out.println(a.getClass() == b.getClass()); // true
}
}
2.2 创建运行时类的对象
public class Test {
public static void main(String[] args) throws Exception {
Class<People> clazz = People.class;
People people = clazz.newInstance(); // 相当于调用空参构造器
System.out.println(people);
}
}
2.3 获取运行时类的结构
现有如下几种类
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "123";
}
public interface MyInterface {
void interfaceMethod();
}
public class People<T> implements Serializable {
private char gender;
public float weight;
private void breath() {
System.out.println("呼吸");
}
public void eat() {
System.out.println("吃");
}
}
@MyAnnotation(value = "hello")
public class Student extends People<String> implements MyInterface, Comparable<Student> {
private String name;
public int age;
int id;
public Student() {
}
@MyAnnotation(value = "ljc")
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
@MyAnnotation
private String show(String address) {
System.out.println("我住在" + address);
return address;
}
public String say(String interest) {
System.out.println("我的兴趣是" + interest);
return interest;
}
private static void sleep() {
System.out.println("睡觉");
}
@Override
public void interfaceMethod() {
System.out.println("interfaceMethod");
}
@Override
public int compareTo(Student o) {
return 0;
}
// set, get, toString方法...
}
2.3.1 获取属性
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
// 获取当前运行时类及其父类的public属性
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}
System.out.println("---------------");
// 获取当前运行时类的所有属性,不包含父类
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
}
}
2.3.2 获取方法
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
// 获取当前运行时类及其父类(包括Object类)的public方法
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------");
// 获取当前运行时类的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
// 获取方法的注解,只有被RetentionPolicy.RUNTIME修饰的注解才可以
Annotation[] annos = m.getAnnotations();
for (Annotation a : annos) {
System.out.println(a);
}
// 获取方法的修饰符
System.out.println(Modifier.toString(m.getModifiers()));
// 通过各种get方法可以获取到方法参数、返回类型、抛出的异常等
}
}
}
2.3.3 获取构造器
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
// 获取当前运行时类的public构造器
Constructor[] constructors = clazz.getConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
System.out.println("----------------");
// 获取当前运行类的所有构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c : declaredConstructors) {
System.out.println(c);
}
}
}
2.3.4 获取父类
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
// 获取当前运行时类的父类(无泛型信息)
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
// 获取当前运行时类的带泛型父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
// 获取泛型类型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(((Class) actualTypeArguments[0]).getName());
}
}
2.3.5 获取其他内容
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
// 获取运行时类的接口
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
System.out.println(c);
}
// 获取运行时类的包
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
// 获取运行时类的注解,需要被RetentionPolicy.RUNTIME修饰
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
}
}
还有很多方法可以获取到不同的内容,这里不一一演示了。
2.4 调用运行时类的结构
2.4.1 调用属性相关方法
public class Test {
public static void main(String[] args) throws Exception {
Class<Student> clazz = Student.class;
// 创建对象
Student student = clazz.newInstance();
// 获取运行时类的属性
Field name = clazz.getDeclaredField("name");
// 加了这句才可以为私有属性赋值
name.setAccessible(true);
// 为指定对象设置属性值
name.set(student, "2");
System.out.println(student);
}
}
2.4.2 调用指定方法
public class Test {
public static void main(String[] args) throws Exception {
Class<Student> clazz = Student.class;
// 创建对象
Student student = clazz.newInstance();
// 获取指定方法,并指明参数列表的类型
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
// 为指定对象调用该方法
Object address = show.invoke(student, "广州");
System.out.println(address);
// 调用静态方法
Method sleep = clazz.getDeclaredMethod("sleep");
sleep.setAccessible(true);
sleep.invoke(clazz); // 这里的参数为任意对象
}
}
2.4.3 调用构造器
这种方式用得少,因为通用性较低(不同的类,构造器不同),一般通过运行时类的newInstance()方法来创建对象。
public class Test {
public static void main(String[] args) throws Exception {
Class<Student> clazz = Student.class;
// 获取构造器,指明构造器的参数列表的类型
Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class, int.class);
constructor.setAccessible(true);
// 通过构造器创建对象
Student student = constructor.newInstance("小明", 20, 1001);
System.out.println(student);
}
}