对于任意一个运行时的类(Runtime),都可以知道类的属性与方法;对于任意一个对象,都可以调用它的任意的方法与属性。动态获取类中的信息和动态调用对象的属性与方法的功能就是反射(直接操作属性和方法)。主要运用在框架中,其实框架不仅仅只有反射,另外还包括设计模式和注解,三者结合运用就是框架。
获取Class实例的方式
对于class类就是java中通过javac进行编译生成的.class文件(字节码文件),一个class类就代表一个运行时的实例,加载到内存当中,会缓存一段时间,在这时间段中我们可以通过不同的方式获取此运行时的类,下面介绍如何获取Class实例。
获取之前准备一个Person类,具体属性,方法我已经在类中说明
/**
* @author jektong
* @Date 2020-7-27 10:31:01
*/
public class Person {
//人物名
private String name;
//年龄
private int age;
//四个不同范围的变量
public String a;
protected String b;
String c;
private String d;
//无参构造
public Person() {
}
//私有有参构造
private Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
//setter与getter方法
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;
}
//自定义printStr方法
public void printStr(String s) {
System.out.println(s);
}
//自定义默认的方法
String getString(String s){
return s+"hello";
}
//自定义show方法
private void show() {
System.out.println("show");
}
//重写toString
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
①. 方式1:通过类.class
的方式获取
Class<Person> c1 = Person.class;
②. 方式2:对象的getClass
获取
Person person = new Person();
Class<? extends Person> c2 = person.getClass();
③. 方式3:forName(className)
静态的获取
Class<?> c3 = Class.forName("com.jektong.reflect.Person");
④. 方式4:通过类加载器进行获取
ClassLoader classLoader = ReflectDemo01.class.getClassLoader();
Class<?> c4 = classLoader.loadClass("com.jektong.reflect.Person")
- 获取的类是全路径名,路径必须写全
- Class<?> 是泛型,里面的问号为何不可写Person,因为加载的时候还不确定Person是否存在
接下来介绍另一种获取Class的方式,可以将类名写入properties文件中使用Properties类进行流读取
类路径下创建person.Properties
文件(此文件需要在类路径下),配置全类名
ClassName=com.jektong.reflect.Person
代码实现:
Properties prop = new Properties();
//类加载
ClassLoader classLoader = ReflectDemo01.class.getClassLoader();
//读取proerties文件
InputStream is = classLoader.getResourceAsStream("person.properties");
//加载文件
prop.load(is);
//获取配置名
String className = prop.getProperty("ClassName");
Class<?> c4 = Class.forName(className);
System.out.println(forName);
以上的c1 ,c2,c3,c4输出的结果都是class com.jektong.reflect.Person
获取类的结构
通过反射我们可以获取到一个运行时的类的具体结构,比如属性,方法构造器,父类,接口,父类的泛型,包,注解和异常等等
-
获取类的成员变量
通过Field类的对象来获取,主要有以下方法:
-
Field[] getFields():获取类中由于public修饰的成员变量
-
Field[] getDeclaredFields():获取所有成员变量,不考虑修饰符
-
Field getField(String name) :获取指定名称的由public修饰的成员变量
-
Field getDeclaredField(String name) :获取指定的与变量,不考虑修饰符
System.out.println("==========获取类中所有public属性=========="); Class<?> forName = Class.forName("com.jektong.reflect.Person"); //获取类中所有public属性 Field[] fields = forName.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("==========获取类中指定的public属性=========="); //获取类中指定的public属性 //Field field = forName.getField("name");//namejava.lang.NoSuchFieldException Field field = forName.getField("a"); System.out.println(field); System.out.println("==========获取类中所有属性,不考虑修饰符=========="); //获取类中所有属性,不考虑修饰符 Field[] declaredFields = forName.getDeclaredFields(); for (Field field2 : declaredFields) { System.out.println(field2); }
-
从输出的结果来看,输出了变量的修饰符,数据类型,和定义的变量
Field:成员变量
- void set(Object obj, Object value) : 设置值
- get(Object obj) : 获取值
- setAccessible(true): 忽略访问权限修饰符的安全检查
- int getModifiers(): 获取修饰符
- Class getType() :获取类型
- String getName :获取字段名
//获取任意一个成员变量
Field declaredField = forName.getDeclaredField("name");//name为私有属性
//获取修饰符
int modifiers = declaredField.getModifiers();
String modName = Modifier.toString(modifiers);
System.out.println(modName);
//获取字段名
String name = declaredField.getName();
System.out.println(name);
-
获取类中的构造方法
- Constructor<?>[] getConstructors() :获取公共的构造
- Constructor<?>[] getDeclaredConstructors() :获取私有的构造
- Constructor getConstructor(类<?>… parameterTypes) :获取带有参数的公共构造
- Constructor getDeclaredConstructor(类<?>… parameterTypes) 获取带有参数的私有构造
- Constructor:构造方法
T newInstance(Object… initargs) 创建对象
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
System.out.println("==========获取公共的构造=========="); Class<?> c = Class.forName("com.jektong.reflect.Person"); Constructor<?>[] constructors = c.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } System.out.println("==========获取所有的构造=========="); Constructor<?>[] declaredConstructors = c.getDeclaredConstructors(); for (Constructor<?> constructor : declaredConstructors) { System.out.println(constructor); } System.out.println("==========获取无参构造=========="); Constructor<?> constructor = c.getConstructor(); System.out.println(constructor); System.out.println("==========获取有参构造=========="); Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class,int.class); System.out.println(declaredConstructor); System.out.println("==========无参构造创建对象=========="); Person p1 = (Person) c.newInstance(); p1.setName("张三"); p1.setAge(12); System.out.println(p1); //强制访问私有属性 declaredConstructor.setAccessible(true); System.out.println("==========私有构造创建对象=========="); Person p2 = (Person) declaredConstructor.newInstance("李四", 13); System.out.println(p2); Field declaredName = c.getDeclaredField("name"); Field declaredAge = c.getDeclaredField("age"); System.out.println("==========获取私有的对象名=========="); declaredName.setAccessible(true); System.out.println(declaredName.get(p2));
不要看这么长其实道理都是一样的。
上面已经对类中的属性和构造方法进行了访问和操作,下面讲解类中的成员方法
- 获取类中的成员方法
- Method[] getMethods() 获取当前运行时类及其所父类中声明为public权限的方法
- Method[] getDeclaredMethods() 获取当前运行时类中声明的所方法。(不包含父类中声明的方法)
- Method getMethod(String name, 类<?>… parameterTypes)
- Method getDeclaredMethod(String name, 类<?>… parameterTypes)
- Method:方法对象
- Object invoke(Object obj, Object… args) 获取方法名称
- String getName 获取方法名
- Annotation[] getAnnotations() 获取注解释
- int getModifiers 获取修饰符
- Modifier.toString(int modifier)
- Class getReturnType() 获取返回值类型
- Class[] getParameterTypes() 获取形参类型
- Class[] getExceptionTypes() 获取异常类型
方法的使用与上面的是大同小异,这里说一下获取方法上的注解和参数类型
准备一个注解类:MyAnno.class
,注解只是这里不说明
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
}
将这个注解使用到Person类中的printStr方法上
@MyAnno
@SuppressWarnings("all")
public void printStr(String s) {
System.out.println(s);
}
获取此方法上的注解以及参数类型
Method psMethod = c.getMethod("printStr", String.class);
System.out.println(psMethod);
//获取类上的注解
Annotation[] annotations = psMethod.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取参数类型
Class<?>[] parameterTypes = psMethod.getParameterTypes();
for (Class<?> class1 : parameterTypes) {
System.out.println(class1);
}
获取父类
定义一个Student.class
来继承Person.java
不做任何实现
public class Student extends Person {
}
- Class getSuperclass() 获取运行时类的父类
- Type getGenericSuperclass() 获取运行时类的带泛型的父类
主要就两个方法的实现:
Class<?> c = Class.forName("com.jektong.reflect.Student");
Class<?> superclass = c.getSuperclass();
System.out.println(superclass);
如果父类是泛型的话,只需要改为:
Type type = c.getGenericSuperclass();
System.out.println(type);
invoke()方法的使用
只要获取类中的方法,我们就可以通过反射invoke()方法去调用此方法
//获取printStr()方法
Method psMethod = c.getMethod("printStr", String.class);
//创建对象,调用方法
Person per = (Person) c.newInstance();
psMethod.invoke(per, "hello");
System.out.println(psMethod);
- invoke方法中提供了两个参数,第一个是调用方法的对象,第二个是方法的参数,如果是多个参数使用数组即可
- 如果调用的是静态的方法,第一个参数写成null
关于反射还有两个重要的概念