反射机制(java.lang.reflect.*)框架的底层原理
Java_Java零基础进阶必备视频教程(适合Java基础,Java入门)_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
重点5.4和6.3
1. 简单介绍
### 1.1 作用?
- 可以直接读和修改字节码文件
- 反射机制可以直接操作代码片段
- 让程序更加灵活
1.2 相关的类在哪个包下
java.lang.reflect.*
1.3 相关的重要的类
类 | 作用 |
---|---|
java.lang.Class | 代表整个字节码 |
java.lang.reflect.Method | 代表字节码中的方法字节码 |
java.lang.reflect.Constructor | 代表字节码中的构造方法字节码 |
java.lang.reflect.Field | 代表字节码中的属性字节码 |
2 获取Class的三种方法
2.1 Class.forName()
(较为常用)
- 静态方法
- 方法的参数是一个字符串。
- 字符需要的是一个完整类名,完整类名必须带有包名。java.lang包也不能略。
其他:
- 这个方法的执行会导致类加载。(第一次加载时会执行类中的静态代码块)
Class c1 = Class.forName("java.lang.String");
Class c2 = Class.forName("java.lang.Date");
Class c3 = Class.forName("java.lang.Integer");
Class c4 = Class.forName("java.lang.System");
2.2 对象.getClass()
方法
String s = "abc";
Class x = s.getClass();
2.3 类.class
属性
Class y = String.class;
Class z = int.class; // 基本数据类型也有 .class
3.通过Class来实例化对象
// c代表的就是Date类型
Class c = Class.forName("java.util.Date");
// 实例化一个Date对象
Object obj = c.newInstance();
newInstance()内部调用了User的无参构造方法。
当User没有无参构造方法时会抛出异常 java.lang.InstantiationException
所以,开发中,model必须有无参构造方法
4.类加载器(ClassLoader)
4.1 什么是类加载器
专门负责加载类的命令/工具
4.2 3个类加载器
- 启动类加载器
- 扩展类加载器
- 应用类加载器
4.3 加载顺序
- 启动类加载器,加载
jre/lib/rt.jar
。若找不到,下一步- 扩展类加载器,加载
jre/lib/ext/*.jar
。若找不到,下一步- 应用类加载器,加载
classpath
中的类。classpath在环境变量配置。
4.4 双亲委派机制
为了安全考虑,类加载器先从jre/lib/rt.jar
和jre/lib/ext/*.jar
中加载。对应启动类加载器(公)和扩展类加载器(母)。
5 Field
5.1 获取Field
Class<?> studentClass = Class.forName("com.bjpowernode.java.bean.Student");
Field[] fields = studentClass.getDeclaredFields();
getDeclaredFields()
可以获取所有的属性。public, protected, default (package) access, and private fields.不能获取继承的属性。
getFields()
只能获取public属性。
5.2 通过Field获取属性的细节(了解一下)
for (Field field : fields) {
System.out.print(Modifier.toString(field.getModifiers()) + " ");
System.out.print(field.getType().getSimpleName() + " ");
System.out.println(field.getName());
}
getModifiers()
获取属性的修饰符,返回一个int
,通过Modifier.toString()
转换成对应的字符串。
getType()
获取属性的类型。返回Class。
getName()
获取属性名。
5.3 反编译Field(实际开发没用)
根据以上的方法,可以通过拼接字符串,反编译出一个.class
文件的源码。
public static void main(String[] args) throws Exception {
StringBuilder builder = new StringBuilder();
Class<?> studentClass = Class.forName("com.bjpowernode.java.bean.Student");
builder.append("public class ").append(studentClass.getSimpleName()).append(" {\n");
Field[] fields = studentClass.getDeclaredFields();
for (Field field : fields) {
builder.append("\t");
builder.append(Modifier.toString(field.getModifiers())).append(" ");
builder.append(field.getType().getSimpleName()).append(" ");
builder.append(field.getName());
builder.append(";\n");
}
builder.append("}");
System.out.println(builder);
}
5.4 通过反射机制,set/get一个Java对象的属性(重点掌握)
Class<?> studentClass = Class.forName("com.bjpowernode.java.bean.Student");
Object obj = studentClass.newInstance();
Field no = studentClass.getDeclaredField("no");
no.set(obj, 2222); // 给obj的no属性赋值2222
Object o = no.get(obj); // 获取obj的no属性
- 以上方法不能访问private属性,需要打破封装
name.setAccessible(true)
。
Field name = studentClass.getDeclaredField("name");
name.setAccessible(true);
name.set(obj, "2222");
System.out.println(obj);
6 Method(超级重点)
6.1 反射Method(了解)
Class<?> userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
Method[] declaredMethods = userServiceClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// 修饰符
System.out.println(Modifier.toString(declaredMethod.getModifiers()));
// 返回类型
System.out.println(declaredMethod.getReturnType());
// 方法名
System.out.println(declaredMethod.getName());
// 参数类型
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
System.out.println();
}
方法 | 作用 |
---|---|
.getDeclaredMethods() | 获取Method |
.getModifiers() | 获取修饰符 |
.getReturnType() | 获取返回值类型 |
.getName() | 获取方法名 |
.getParameterTypes() | 获取参数类型列表 |
6.2 Method反编译(了解)
根据以上的方法,拼接字符串。类似Field反编译。
6.3 通过反射机制获取并调用方法(重点)
Class<?> userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
Object obj = userServiceClass.newInstance();
// 获取方法
Method login = userServiceClass.getDeclaredMethod("login", String.class, String.class);
// 反射机制中最最最最重要的一个方法,调用方法
Object retValue = login.invoke(obj, "1212", "1212");
System.out.println(retValue);
.getDeclaredMethod()
需要传入函数名和参数类型
.invoke()
调用方法
7 Constructer
7.1反编译
public static void main(String[] args) throws Exception {
StringBuilder s = new StringBuilder();
Class<?> studentClass = Class.forName("com.bjpowernode.java.bean.Student");
// 类修饰符
s.append(Modifier.toString(studentClass.getModifiers())).append(" class ");
// 类名
s.append(studentClass.getSimpleName()).append("{\n");
// 获取构造方法
Constructor<?>[] constructors = studentClass.getConstructors();
for (Constructor<?> constructor : constructors) {
s.append("\t");
// 构造方法修饰符
s.append(Modifier.toString(constructor.getModifiers())).append(" ");
// 构造方法名与类名相同
s.append(studentClass.getSimpleName());
s.append("(");
// 获取参数类型
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
s.append(parameterType.getSimpleName()).append(",");
}
if (parameterTypes.length != 0) {
s.deleteCharAt(s.length() - 1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
.getConstructors()
获取构造方法
7.2 通过反射机制构造对象
// 1.获取构造方法
Constructor<?> constructor = studentClass.getDeclaredConstructor(String.class, int.class);
// 2.调用构造方法new对象
Object he = constructor.newInstance("he", 1);
无参构造直接用
.newInstance()
不加参数,不用先获取构造方法。
8 获取父类和实现的接口
Class<?> stringClass = Class.forName("java.lang.String");
// 获取父类
Class<?> superclass = stringClass.getSuperclass();
System.out.println(superclass.getName());
// 获取接口
Class<?>[] interfaces = stringClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
}
方法 | 作用 |
---|---|
.getSuperclass() | 获取父类 |
.getInterfaces() | 获取实现的接口 |