java的反射机制是java当中十分主流的技术,java的核心底层思想之一IOC就是基于反射技术实现的,正因如此,在面试当中反射常常作为一个常考的问题来考察面试者的素养,今天小编就来和大家分享这一方面的知识,希望大家都能有所收获。
1.反射的概念
反射是指在程序的运行期间,可以实时的获取到类的信息,并将类的成分当作对象来操作,而这种动态获取并调用信息的能力就叫做反射。
2.反射的应用场景
-
运行时获取任意对象的类信息
-
动态创建对象
-
读写私有字段
-
调用私有方法
-
绕过泛型擦除
-
实现通用框架
3.反射的使用
Class类的常用方法:
-
拿字节码:String.class、obj.getClass()、Class.forName("全限定名")
-
看名字:getName() 得全限定名,getSimpleName() 得类简称,getPackageName() 得包名
-
看血统:getSuperclass() 拿父类,getInterfaces() 拿直接实现的接口数组
-
看字段:getFields() 只给 public(含继承),getDeclaredFields() 本类全权限(含 private)
-
拿单个字段:getField("age") 仅 public,getDeclaredField("age") 可破私有,之后 setAccessible(true) 即可读写
-
看方法:getMethods() 给 public(含继承),getDeclaredMethods() 本类全权限(含 private)
-
拿单个方法:getMethod("foo", int.class) 仅 public,getDeclaredMethod("foo", int.class) 可破私有,invoke 调用
-
看构造:getConstructors() 仅 public,getDeclaredConstructors() 含私有;getDeclaredConstructor(param...) 拿指定构造,newInstance(args) 建对象
-
创实例:clazz.getDeclaredConstructor().newInstance() 替代已过时的 clazz.newInstance()
-
看注解:getAnnotation(Anno.class) 取注解,isAnnotationPresent(Anno.class) 判断有无
-
类型判断:isInterface()、isEnum()、isArray()、isPrimitive()、isSynthetic() 快速识别
-
数组:Array.newInstance(int.class, 5) 动态建数组,Array.set/get 读写元素
-
泛型残留:getGenericSuperclass() 再强转 ParameterizedType,可读到父类泛型实参
-
类加载器:getClassLoader() 拿定义加载器,Bootstrap 返回 null
-
模块:getModule() 拿 Module 对象,JDK 9+ 可用
实现代码:
java
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
/**
* 反射机制一次性演示
*/
public class ReflectionProof {
/* ============ 1. 任意对象都能拿到 Class ============ */
public static void obtainClass() throws ClassNotFoundException {
// 三种常见写法
Class<?> c1 = User.class; // 类字面量
Class<?> c2 = new User().getClass(); // 对象.getClass()
Class<?> c3 = Class.forName("User"); // 全限定名
System.out.println("c1==c2? " + (c1 == c2));
System.out.println("c2==c3? " + (c2 == c3));
}
/* ============ 2. 动态创建对象 ============ */
public static void createObject() throws Exception {
Class<User> clz = User.class;
// 公有无参构造
User u1 = clz.getDeclaredConstructor().newInstance();
// 私有有参构造
Constructor<User> c = clz.getDeclaredConstructor(int.class, String.class);
c.setAccessible(true); // 暴力破门
User u2 = c.newInstance(18, "Tom");
System.out.println("u2 = " + u2);
}
/* ============ 3. 读写私有字段 ============ */
public static void manipulateField() throws Exception {
User u = new User(20, "Alice");
Field ageField = User.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(u, 30); // 把年龄改成 30
System.out.println("修改后 age = " + ageField.get(u));
}
/* ============ 4. 调用私有方法 ============ */
public static void callPrivateMethod() throws Exception {
User u = new User();
Method m = User.class.getDeclaredMethod("secret", String.class);
m.setAccessible(true);
String result = (String) m.invoke(u, "hello");
System.out.println("私有方法返回: " + result);
}
/* ============ 5. 绕过泛型擦除 ============ */
@SuppressWarnings("unchecked")
public static void breakGeneric() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
// 拿到原始字节码后,泛型信息被擦除
Method add = list.getClass().getMethod("add", Object.class);
add.invoke(list, "我不是 Integer"); // 成功塞进去
System.out.println("list 内容 = " + list);
for (Object o : list) {
System.out.println("元素类型: " + o.getClass() + ", 值: " + o);
}
}
public static void main(String[] args) throws Exception {
obtainClass();
createObject();
manipulateField();
callPrivateMethod();
breakGeneric();
}
}
/* ==================== 被操作的小可怜 ==================== */
class User {
private int age;
private String name;
public User() {}
private User(int age, String name) {
this.age = age;
this.name = name;
}
private String secret(String msg) {
return "私有方法收到: " + msg;
}
@Override
public String toString() {
return "User{age=" + age + ", name='" + name + "'}";
}
}
今天的分享就到这里了,希望这篇博客能给你一些帮助,让你对关于java反射机制的问题得到进一步的提升,在面试的时候能从容面对面试官。