1. 获取运行时类的完整结构
通过反射获取运行时类的完整结构Field, Method, Constructor,Superclass,Interface, Annotation。
我们可以通过反射获得如下内容:
[1] 实现的全部接口
[2] 所继承的父类
[3] 全部的构造器
[4] 全部的方法
[5] 全部的Field
[6] 注解
…
相关实例代码
package com.gs.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.gs.reflection.User");
//获取类的名字
System.out.println(c1.getName()); //获取包名+类名
System.out.println(c1.getSimpleName()); //获取类名
//获取类的属性
System.out.println("=====获取类的属性====");
Field[] fields = c1.getFields(); //只能找到public属性
for (Field field : fields) {
System.out.println(field);
}
//找到全部的属性
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
System.out.println("====获得类的方法=====");
//获得本类及其父类的全部public方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println("public方法:"+method);
}
//获得本类的所有方法
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println("getDeclaredMethods:"+method);
}
//获得指定的方法(因为有重载,获得方法时,需要传递参数类型)
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println( getName);
System.out.println(setName);
//获得所有的构造器
System.out.println("====获得指定的构造器=====");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("#Declared:"+constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定:"+declaredConstructor);
}
}
小结: 从上面的例子我们不难发现
getXXXs() : 获得public相关的全部东西。如: getMethods() 获得该类的所有public方法。
getDeclaredXXXs(): 获得包括private,public等相关的全部东西。如: getDeclaredMethods(): 获得该类所有的方法(包括private类型的)
getXXX(): 获得public相关的指定东西。如: getMethod(名字,参数类型),获得该类指定的public方法
getDeclaredXXX():获取包括private,public等相关的指定东西。如:getDeclaredMethod(名字,参数类型),获得该类指定的方法(包括private 类型的)
既然我们能通过反射获得指定的方法,属性。那我们应该怎么进行调用呢?
2. Class对象
在了解如何调用之前,我们先来聊一聊如何通过反射的Class类来参加类的对象呢?
[1] 创建类的对象:先获取Class对象,再调用newInstance() 方法 ===>在这个大前提,原来的类必须具备:
1.1】类必须要有一个无参构造器
1.2】类的构造器的访问权限需要足够(即需要是public类型)
通过一个实例说明:
我们有一个实体类User
// 实体类 pojo,entity
class User{
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
通过反射new出对象
public class Test09 {
public static void main(String[] args) throws Exception, {
//获得Class对象
Class c1 = Class.forName("com.gs.reflection.User");
//构造一个对象
User user = (User) c1.newInstance(); //本质上是调用了无参构造器
System.out.println(user);
}
[2] 思考:通过上面的例子我们可以通过Class对象调用类的无参构造器新建对象,那我们没有无参构造器就不能创建对象吗?
答案肯定是否定的,只要在操作的时候明确的调用类的构造器,并将参数传递进去之后,便可以完成实例化操作。
相关的步骤如下;
- 通过Class类的getDeclaredConstructor(Class… parameterTypes)取得奔雷指定形参类型的构造器
- 向构造器的形参中传递一个对象数据进去,里面包含了构造器中所需要的各个参数。
- 通过Constructor实例化对象
上面的User对象还可以通过下面方法创建
public class Test09 {
public static void main(String[] args) throws Exception {
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("zs", 001, 18);
System.out.println(user2);
[3] 上面我们已经了解了Class类的方法,以及如何通过Class类new出一个对象,那接下来就是我们应该如何去调用这些方法。
调用指定的方法
通过反射,调用类中的方法,通过Method类实现
3.1】通过Class类的getMethod(String name, Class …parameterTypes) 方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
3.2 】之后使用Object inovke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
下面通过一个实例进行说明
package com.gs.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//动态创建对象,通过反射
public class Test09 {
public static void main(String[] args) throws Exception {
//获得Class对象
Class c1 = Class.forName("com.gs.reflection.User");
//通过反射调用普通方法
User user3 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke(对象,"方法的值") ===>表示激活调用的意思
setName.invoke(user3, "zs");
System.out.println(user3.getName());
//通过反射操作属性
System.out.println("=====通过反射操作属性=====");
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
name.setAccessible(true);
name.set(user4,"ls");
System.out.println(user4.getName());
}
}
相关解析:
Object invoke(Object obj, Object… args)
- 第一个Object对应原方法的返回值,若原方法无返回值,此处为null
- 若原方法为静态方法,此时形参Object obj可为null
System.out.println("====== 静态方法=======");
//其中test01为静态方法
Method test01 = c1.getDeclaredMethod("test01");
Object invoke1 = test01.invoke(null);
System.out.println(invoke1);
- 若原方法形参列表为空,则Object[] args为null
- 若原方法声明为private,则需要在调用invoke()方法前,先调用方对象的setAcessible(true).这才可以访问private方法
setAccessible
- Method和Field,Constructor对象都有setAcessible() 方法
- setAcessible的作用就是启动和禁用访问安全检查的开关
- 参数值为true则指示反射对象在使用时取消java语言访问检查。
- 参数值为false则提示反射对象应该实施java语言访问检查
作用:
【1】提高反射效率,如果代码中必须使用反射,而该句代码需要频繁的被调用,则设置为true
【2】使得原本无法访问的私有成员可以访问