一、反射
反射被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并且直接操作任意对象的内部属性及方法。
二、Java反射机制提供的功能
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类具有的成员变量和方法;
在运行时调用任意一个对象的成员变量和方法;
生成动态代理。
三、反射相关的API
java.lang.Class 代表一个类
java.lang.reflect.Method 代表类的方法
java.lang.reflect.Field 代表类的成员变量
java.lang.reflect.Constructor 代表类的构造方法
……
四、简单示例
public class Person { private String name; private int age; public Person() { } public Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void show(){ System.out.println("我是一个人"); } public void display(String nation){ System.out.println("我的国籍是"+nation); } @Override public String toString() { return "Person[name="+name+",age="+age; } } import org.junit.jupiter.api.Test; import java.lang.reflect.Field; public class TestReflection { //在有反射以前创建一个类的对象,并且调用其中的方法和属性 @Test public void test1(){ Person person=new Person(); person.setAge(10); person.setName("小明"); System.out.println(person); person.show(); person.display("中国"); } //通过反射以前创建一个类的对象,并且调用其中的结构 @Test public void test2() throws IllegalAccessException, InstantiationException, NoSuchFieldException { Class<Person> clazz=Person.class; //创建clazz对应的运行时类Person类的对象 Person person=clazz.newInstance(); System.out.println(person); Field f1=clazz.getField("name"); f1.set(person,"韩梅梅"); System.out.println(person); } }
可能运行test2()的时候回抛出如下异常:
没有“name”这个属性存在。出现这个异常的原因可能是对应字段不存在,或者对应的属性设置为私有,而私有的属性,反射也是无法获取的,改为public就可以。
修改以后的现象就是:
这就是通过反射调用运行时类的指定属性,可以调用公有也可以调用私有的属性,以及调用指定的方法:
//调用公有的属性
Field f1=clazz.getField("name");
f1.set(person,"韩梅梅"); System.out.println(person); //调用私有的属性,getDeclaredField调用声明过的属性, // setAccessible设置访问权限 Field f2=clazz.getDeclaredField("age"); f2.setAccessible(true); f2.set(person,20); System.out.println(person);
//通过反射调用运行时类的指定方法 Method method1=clazz.getMethod("show"); method1.invoke(person); Method method2=clazz.getMethod("display", String.class);//加上方法参数类型 method2.invoke(person,"中国中国");
五
1、Class类
首先了解Class类,它是反射的源头,
所有类都从Object类中继承了getClass()方法,它会返回一个Class类,即某个特定对象的运行时类。
产生对象的方式通常有两种:
正常方式:引入需要的“包类”名称——>通过new实例化——>取得实例化对象
反射方式:实例化对象——>getclass()方法——>得到完整的“包类”名称
简而言之:反射,即通过对象反射求出类的名称,从包类实例化,再反过来,再从实例获得运行时类,再由运行时类找倒其中的结构。某个对象对着镜子可以得到自己的属性、方法、构造器以及实现的接口等等信息。
Class本身就是一个类,Class对象只能由系统创建,一个类在JVM中只会有一个Class实例,一个Class对象对应的是已经加载到JVM的一个.class文件,每个类的实例都会记得自己由哪一个Class实例生成,所以通过Class可以得到一个类的完整结构。
@Test public void test3(){ Person person=new Person(); //通过运行时类对象的getClass(),返回其运行时类 Class clazz=person.getClass(); System.out.println(clazz); }
输出结果:
我们创建一个类,通过编译(javac.exe),生成对应的.class文件,
之后加载(java.exe,通过JVM的类加载器来完成)此.class文件,
此.class文件加载到内存以后,就是一个运行时类,存放在缓存区,那么这个运行时类本身就是一个Class实例。
每一个运行时类只加载一次。
有了Class实例之后,才可以创建对应的运行时类对象或者获取对应的运行时类的完整结构,包括属性方法构造器等等并且调用一些指定的结构。
反射还可以进行动态代理。
2、如何获取Class实例
@Test public void test4() throws ClassNotFoundException { //调用运行时类本身的.class类属性 Class clazz1=Person.class; System.out.println(clazz1.getName()); //通过运行时类对象获取 Person person=new Person(); Class clazz2=person.getClass(); System.out.println(clazz2.getName()); //通过Class的静态方法获取 Class clazz3=Class.forName("reflection.Person"); System.out.println(clazz3.getName()); //通过类的加载器 ClassLoader classLoader=this.getClass().getClassLoader(); Class clazz4=classLoader.loadClass("reflection.Person"); System.out.println(clazz4.getName()); System.out.println(clazz1==clazz2); System.out.println(clazz1==clazz3); System.out.println(clazz1==clazz4); }
3、有了Class类对象,就能获取类的对象并且获取完整的结构
要求:
类必须有一个无参数的构造器;
类的构造器的访问权限足够。
@Test public void test5() throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class clazz=Class.forName("reflection.Person"); //创建对应的运行时类对象,newInstance()调用的就是无参构造器 Person person=(Person) clazz.newInstance(); System.out.println(person); }
输出结果:
如果把Person类的无参构造器去除,将会出现异常,实例化的过程中出现问题。
将无参构造器的访问权限改为private同样会出现异常,
将Person这个类变得稍微复杂一些
@MyAnnotation(value = "testReflection") public class Person extends Creature<String> implements Comparable,MyInterface{ public String name; private int age; int height; public Person() { System.out.println("调用的无参构造器"); } public Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @MyAnnotation(value = "testShow") public void show(){ System.out.println("我是一个人"); } public void display(String nation) throws Exception{ System.out.println("我的国籍是"+nation); } @Override public String toString() { return "Person[name="+name+",age="+age+"]"; } @Override public int compareTo(Object o) { return 0; } //新增内部类 class Eye{ } }
double weight; public void breath(){ System.out.println("呼吸"); } }
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); }
获取对应的运行时类的属性
@Test public void test1(){ Class clazz=Person.class; //1.getFields()只能获取到运行时类及其父类中声明为public的属性 Field[] fields=clazz.getFields(); for (Field f:fields) { System.out.println(f.getName()); } System.out.println(); //2.getDeclaredFields()获取运行时类本身声明的所有属性 Field[] fieldsDeclared=clazz.getDeclaredFields(); for (Field f:fieldsDeclared) { System.out.println(f.getName()); } }
获取属性的各个部分的内容,权限修饰符 变量类型 变量名
@Test public void test2(){ Class clazz=Person.class; Field[] fieldsDeclared=clazz.getDeclaredFields(); for (Field f:fieldsDeclared) { //1.获取每个属性的权限修饰符 int i=f.getModifiers(); String access=Modifier.toString(i); //2.获取属性类型 Class type=f.getType(); //3.获取属性名 System.out.println(access+" "+type.getName()+" "+f.getName()); } }
获取运行时类的方法
@Test public void test1(){ Class clazz=Person.class; //1.getMethods()获取运行时类及其父类当中的所有声明为public的方法 Method[] methods=clazz.getMethods(); for (Method m:methods) { System.out.println(m); } System.out.println(); //2.获取运行时类本身声明的所有的方法 Method[] methods2=clazz.getDeclaredMethods(); for (Method m:methods2) { System.out.println(m); } }
注解、权限修饰符、返回值类型、方法名、形参列表、异常
@Test public void test2(){ Class clazz=Person.class; Method[] methods=clazz.getDeclaredMethods(); for (Method m:methods) { //1.注解 Annotation[] annotations=m.getDeclaredAnnotations(); for (Annotation a:annotations) { System.out.print(a+" "); } //2.权限修饰符 String access=Modifier.toString(m.getModifiers()); System.out.print(access+" "); //3.返回值类型 Class returnType=m.getReturnType(); System.out.print(returnType.getName()+" "); //4.方法名 System.out.print(m.getName()); //5.形参列表 System.out.print(" ("); Class[] params=m.getParameterTypes(); for (Class p:params){ System.out.print(p.getName()+","); } System.out.print(")"); //6.异常类型 Class[] exceptions=m.getExceptionTypes(); if (exceptions.length>0){ System.out.print(" throws"); } for (Class e:exceptions) { System.out.print(" "+e.getName()); } System.out.println(); } }