Java反射详解
- 获取类的字节码
- 反射获取里的构造器
- 反射获取类构造器的作用
- 反射获取成员变量
- 反射获取成员方法
反射技术,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)。
由于Java的设计原则是万物皆对象,获取到的类其实也是以对象的形式体现的,叫字节码对象,用Class类来表示。获取到字节码对象之后,再通过字节码对象就可以获取到类的组成成分了,这些组成成分其实也是对象,其中每一个成员变量用Field类的对象来表示、每一个成员方法用Method类的对象来表示,每一个构造器用Constructor类的对象来表示。
获取类的字节码
反射的第一步:是将字节码加载到内存,我们需要获取到的字节码对象。
-
获取类字节码的方式有三种:
-
通过类名获取: 这是最常用的方式,每个类都有一个名为 “class” 的静态成员变量,可以直接通过类名来获取。
// 1、方法一:通过类名获取类字节码 Class<Student> stringClass = Student.class; System.out.println(stringClass.getName()); //获取全类名 System.out.println(stringClass.getSimpleName()); //获取简单类名
-
**通过对象获取:**每个对象都有一个getClass()方法,可以通过这个方法获取对象所属的字节码。
// 2、方法二:通过对象获取 Student student = new Student(); Class<? extends Student> aClass = student.getClass(); System.out.println(aClass.getName()); //获取全类名 System.out.println(aClass.getSimpleName()); //获取简单类名
-
**通过全限定类名获取:**如果我们只知道类的全限定名,可以使用Class类的静态方法 forName() 来获取字节码。
// 3、方法三:通过全限定类名获取 try { Class<?> aClass = Class.forName("com.liuyi.test.Student"); System.out.println(aClass.getName()); System.out.println(aClass.getSimpleName()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); }
这种方式需要抛出 ClassNotFoundException 异常,需要使用try-catcch 语句块来处理。
-
反射获取里的构造器
获取构造器,需要用到Class类提供的几个方法,如下图所示:
想要快速记住这个方法的区别,按照规律来记就很方便了。
get:获取
Declared: 有这个单词表示可以获取任意一个,没有这个单词表示只能获取一个public修饰的
Constructor: 构造方法的意思
后缀s: 表示可以获取多个,没有后缀s只能获取一个
1、获取所以类的构造器
1)User类
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
}
2)编写测试方式来获取类中的所有的构造器
@Test
public void testGetConstructors(){
// 1、反射第一步:必须先得到这个类的Class对象
Class<User> userClass = User.class;
// 2、获取类的全部构造器
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
// 3、变量数组中的每一个构造器对象
for (Constructor<?> constructor : declaredConstructors) {
System.out.println(constructor.getName() + "-->参数个数" + constructor.getParameterCount());
}
}
打印输出的结果如下:
2、获取一个类构造器
@Test
public void testGetConstructor(){
// 1、获取类的Class对象
Class<User> userClass = User.class;
// 2、获取类public修饰的构造器
try {
Constructor<User> constructor = userClass.getConstructor();
System.out.println("public 修饰的构造器" + constructor.getName() + "-->参数个数:" + constructor.getParameterCount());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// 3、获取private修饰的有三个参数的构造器,第一个参数int类型,第二个参数Sting类型,第三个参数Sting类型
try {
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class, String.class);
System.out.println("private修饰的有三个参数的构造器:" + declaredConstructor.getName()
+ "-->参数个数:" + declaredConstructor.getParameterCount());;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
打印输出的结果如下:
反射获取类构造器的作用
获取到了Cat类中的构造器。获取到构造器后,我们就需要使用类构造器初始化对象并返回。
这里我们需要用到如下的两个方法,注意:这两个方法时属于Constructor的,需要用Constructor对象来调用。
如下图所示,constructor1和constructor2分别表示Cat类中的两个构造器。现在我要把这两个构造器执行起来。
由于构造器是private修饰的,先需要调用setAccessible(true)
表示禁止检查访问控制,然后再调用newInstance(实参列表)
就可以执行构造器,完成对象的初始化了。
代码如下:为了看到构造器真的执行, 故意在两个构造器中分别加了两个打印语句
代码的执行结果如下图所示:
反射获取成员变量
在Class类中提供了获取成员变量的方法,如下图所示:
这些方法的记忆规则,如下:
get:获取
Declared: 有这个单词表示可以获取任意一个,没有这个单词表示只能获取一个public修饰的
Field: 成员变量的意思
后缀s: 表示可以获取多个,没有后缀s只能获取一个
1、获取成员变量
@Test
public void testGetFields(){
Class<User> userClass = User.class;
// 获取所有的成员变量
Field[] declaredFields = userClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName() + "--->" + declaredField.getType());
}
System.out.println("================================");
// 获取某个指定的成员变量
try {
Field name = userClass.getDeclaredField("name");
System.out.println(name.getName() + "--->" + name.getType());
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
2、使用成员变量
在Filed类中提供给给成员变量赋值和获取值的方法,如下图所示:
获取值的方法时Filed类的需要用Filed类的对象来调用,而且不管是设置值、还是获取值,都需要依赖于该变量所属的对象。代码如下:
@Test
public void testGetFields(){
Class<User> userClass = User.class;
try {
// 获取类构造器
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true); // 禁止访问控制权限
User user = declaredConstructor.newInstance();
// 获取指定的成员变量
Field name = userClass.getDeclaredField("name");
name.setAccessible(true); // 禁止访问控制权限
System.out.println(name.getName() + "--->" + name.getType());
// 给成员变量赋值
name.set(user,"张三");
// 打印赋值的结果
System.out.println(user);
} catch (NoSuchFieldException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
反射获取成员方法
在Java中反射包中,每一个成员方法用Method对象来表示,通过Class类提供的方法可以获取类中的成员方法对象。如下图所示:
1、获取成员方法
@Test
public void testGetMethod(){
Class<User> userClass = User.class;
// 获取类中的全部成员方法
Method[] methods = userClass.getDeclaredMethods();
// 遍历这个数组中的每一个方法对象
for (Method method : methods) {
System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());
}
}
2、执行成员方法
在Method类中提供了方法,可以将方法自己执行起来。
@Test
public void testGetMethod(){
Class<User> userClass = User.class;
// 获取类中的全部成员方法
try {
// 获取类构造器
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true); // 禁止访问控制权限
User user = declaredConstructor.newInstance();
Method run = userClass.getDeclaredMethod("run");
run.setAccessible(true); // 禁止访问控制权限
// 执行
run.invoke(user);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}