一面百度, 被温柔小姐姐问到了反射. 虽然大概清楚反射是什么, 但是毕竟不经常使用, 所以严重翻车. 在此提醒大家, 反射很重要, 一定要复习好, 千万不要像我一样在反射上翻车. 说多了都是泪…
目录
1. 什么是反射
Java的反射机制是在运行状态中, 对于任意一个类, 都可以知道这个类的所有方法和属性. 对于任意一个对象, 都能调用它的任意方法和属性. 既然能拿到, 我们就可以修改这部分类型信息. 这种动态获取信息以及动态调用对象方法的功能称为 Java 的反射机制.
2. 反射的用途
- 反射最重要的用途就是开发各种框架
- 在第三方应用开发过程中, 经常会遇见某个类的成员变量, 方法或者属性是私有的或是只对系统应用开放, 这个时候就可以使用 java 的反射机制来获取所需要的私有成员或方法.
3. 反射相关的类
类名 | 用途 |
---|---|
Class 类 | 代表类的实体, 在运行的 Java 应用程序中表示类和接口 |
Field 类 | 代表类的成员变量 / 属性 |
Method 类 | 代表类的方法 |
Constructior 类 | 代表类的构造方法 |
3.1 常用获得类的相关方法
getClassLoader(): 获得类的加载器
getDeclaredClasses(): 返回一个数组, 数组中包含类中所有类和接口类的对象(包括私有)
forName(String className): 根据类名返回类的对象
newInstance(): 创建类的实例
getName(): 获取类的完整路径名字
3.2 常用获得类中属性相关的方法
getField(String name): 获得某个公有的属性对象
getFields(): 获得所有公有的属性对象
getDeclaredField(String name): 获得某个属性对象
getDeclaredFields(): 获得所有属性对象
3.3 获得类中构造器相关方法
getConstructor(Class…<?> parameterTypes): 获得该类中与参数类型匹配的公有构造方法
getConstructors(): 获得该类的所有共有构造方法
getDeclaredConstructor(Class…<?> parameterTypes): 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors(): 获得该类所有构造方法
3.4 获得类中方法相关的方法
getMethod(String name, Class…<?> parameterTypes): 获得该类某个公有的方法
getMethods(): 获得该类的所有公有方法
getDeclaredMethod(String name, Class…<?> parameterTypes): 获得该类中的某个方法
getDeclaredMethods(): 获得该类中的所有方法
4. 反射的步骤
4.1 获取 Class 对象的方式
在反射之前, 我们首先要拿到当前需要反射的类的 Class 对象, 然后通过 Class 对象的核心方法, 达到反射的目的
- 使用 Class.forName(“类的全路径名”), 静态方法. 前提: 已明确类的全路径名
- 使用 .class 方法. 前提: 仅适合在编译前就已经明确要操作的 Class
- 使用 类对象的 getClass() 方法
// 私有属性 name
private String name = "guyueyue";
// 公有属性 age
public int age = 21;
// 不带参数的构造方法
public Student() {
System.out.println("Student()");
}
// 带参数的私有构造方法
private Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Student(name, age)");
}
// 提供各种方法(共有的方法 和 私有的方法)
public void eat() {
System.out.println("I am eat");
}
public void sleep() {
System.out.println("I am sleep");
}
private void function(String str) {
System.out.println(str);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Reflex {
public static void main(String[] args) {
// 1. 通过 getClass() 获取 class 对象
Student student = new Student();
Class c1 = student.getClass();
// 2. 直接通过 类名.class 的方式获取
Class c2 = Student.class;
// 3. 使用 Class.forName("类的全路径") 获取, 但是可能会抛出 ClassNotFoundException 异常
Class c3 = null;
try {
c3 = Class.forName("Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 在一个 JVM 中只会有一个 Class 实例, 所以对上述产生的 Class 文件进行一个比较
System.out.println(c1.equals(c2));
System.out.println(c2.equals(c3));
System.out.println(c1.equals(c3));
}
}
4.2 反射的使用
首先获取到 Class 对象 classStudent
- 创建对象: classStudent.newInstance();
- 反射私有构造方法: classStudent.getDeclaredConstructor(参数类型.class);
- 反射私有属性: classStudent.getDeclaredField(属性名);
- 反射私有方法: classStudent.getDeclaredMethod(“方法名”, 参数类型.class);
public class ReflectClass {
// 1. 创建对象
public static void reflectNewInstance() {
try {
Class classStudent = Class.forName("Student"); // 通过 Class.forName("类的全路径") 获得 class 对象
Object objectStudent = classStudent.newInstance(); // 通过 class 对象创建一个对象
Student student = (Student) objectStudent; // 将这个对象转成 Student 的类型
System.out.println("获取到学生对象" + student);
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. 反射私有的构造方法
public static void reflectPrivateConstructor() {
// 通过 Class.forName("类的全路径") 获得 class 对象
try {
Class classStudent = Class.forName("Student");
Constructor declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class, int.class); // 传入对应的参数
declaredConstructorStudent.setAccessible(true); // 设置为 true 后可修改访问权限
Object objectStudent = declaredConstructorStudent.newInstance("kiki", 18); // 修改姓名和年龄
Student student = (Student) objectStudent;
System.out.println("获得私有构造函数并且修改姓名和年龄" + student);
} catch (Exception e) {
e.printStackTrace();
}
}
// 3. 反射私有属性
private static void reflectPrivateField() {
// 通过 Class.forName("类的全路径") 获得 class 对象
try {
Class classStudent = Class.forName("Student");
Field field = classStudent.getDeclaredField("name");
field.setAccessible(true); // 修改为 true 后可以修改访问权限
Object objectStudent = classStudent.newInstance();
Student student = (Student) objectStudent;
field.set(student, "shaking");
String name = (String)field.get(student);
System.out.println("反射私有属性修改name为" + name);
} catch (Exception e) {
e.printStackTrace();
}
}
// 4. 反射私有方法
public static void reflectPrivateMethod() {
try {
Class classStudent = Class.forName("Student");
Method method = classStudent.getDeclaredMethod("function", String.class);
System.out.println("私有方法名为" + method.getName());
method.setAccessible(true); // 设置为 true 后可以修改私有访问权限
Object objectStudent = classStudent.newInstance();
Student student = (Student) objectStudent;
method.invoke(student, "参数");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. 反射的优缺点
- 优点:
- 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象, 都能够调用它的任意一个方法
- 增加程序的灵活性和扩展性, 降低耦合性, 提高自适应能力
- 反射应用于很多流行的框架: Spring 等
- 缺点:
- 使用反射会导致程序的效率降低
- 反射技术绕过了源代码技术, 会带来维护问题
- 反射代码比普通的代码更加复杂