
Java反射
什么是反射:
在 Java 的世界里,反射是一项强大的机制,它让我们能够在运行时动态操作类和对象,对于任意一个类或对象,我们都能够通过反射的方式获取到这个类或对象的所有属性和方法。
简而言之,给我一个Class对象,我能通过反射获取到该类的属性和方法。
反射的基础步骤:
获取Class对象
获取字段/方法/构造器
进行访问和操作
获取Class类的三种方式:
对象.getClass() 通过对象的getClass方法获取Class对象,适用于对象已经创建好
类.Class 无需创建对象直接通过类获取对应的Class对象
Class.forName() 通过一个类的全限定名即可获取该类的Class对象
// 方式一:通过类名
Class<?> clazz1 = Person.class;
// 方式二:通过对象
Person p = new Person();
Class<?> clazz2 = p.getClass();
// 方式三:通过全限定类名
Class<?> clazz3 = Class.forName("com.example.Person");
Field类:
Field
表示一个类或接口中的一个属性。它是反射 API 提供的一个类,用来描述类结构的一部分:字段。
//返回所有的public字段,包括其父类的,如果没有字段,返回空数组
public Field[] getFields()
//返回本类声明的所有字段,包括非public的,但不包括父类的
public Field[] getDeclaredFields()
//返回本类或父类中指定名称的public字段,找不到抛出异常NoSuchFieldException
public Field getField(String name)
//返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException
public Field getDeclaredField(String name)
//获取字段的名称
public String getName()
//判断当前程序是否有该字段的访问权限
public boolean isAccessible()
//flag设为true表示忽略Java的访问检查机制,以允许读写非public的字段
public void setAccessible(boolean flag)
//获取指定对象obj中该字段的值
public Object get(Object obj)
//将指定对象obj中该字段的值设为value
public void set(Object obj, Object value)
** 当字段为static修饰时,obj可以直接传入null
因为静态字段不属于任何一个实例对象,而是属于类本身,所以在调用 get() 或 set() 时,可以将对象参数设为 null
Unsafe类无法通过getUnsafe方法来获取,因此只能通过反射的方式获取,而theUnsafe属性在Unsafe类中被static修饰,因此在获取该属性时可以传入null。
public class TestUnSafe {
//获取Unsafe的实例
static final Unsafe unsafe;
//记录变量state在类TestUnSafe中的偏移值
static final long stateOffset;
private volatile long state=0;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
stateOffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));
} catch (Exception ex) {
System.out.println(ex.getLocalizedMessage());
throw new Error(ex);
}
}
public static void main(String[] args) {
TestUnSafe test = new TestUnSafe();
Boolean sucess = unsafe.compareAndSwapInt(test, stateOffset, 0, 1);
System.out.println(sucess);
}
}
Method类:
类中定义的静态和实例方法都被称为方法,用类Method表示。
//返回所有的public方法,包括其父类的,如果没有方法,返回空数组
public Method[] getMethods()
//返回本类声明的所有方法,包括非public的,但不包括父类的
public Method[] getDeclaredMethods()
//返回本类或父类中指定名称和参数类型的public方法,
//找不到抛出异常NoSuchMethodException
public Method getMethod(String name, Class<? >... parameterTypes)
//返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
public Method getDeclaredMethod(String name, Class<? >... parameterTypes)
//获取方法的名称
public String getName()
//flag设为true表示忽略Java的访问检查机制,以允许调用非public的方法
public void setAccessible(boolean flag)
//在指定对象obj上调用Method代表的方法,传递的参数列表为args
public Object invoke(Object obj, Object... args) throws
IllegalAccessException, Illegal-ArgumentException, InvocationTargetException
** 对于invoke方法,如果方法为静态方法则obj对象为null
创建对象和构造函数:
**调用默认构造方法(无参)来创建对象
Map<String, Integer> map = HashMap.class.newInstance();
map.put("hello", 123);
**获取所有构造方法:
//获取所有的public构造方法,返回值可能为长度为0的空数组
public Constructor<? >[] getConstructors()
//获取所有的构造方法,包括非public的
public Constructor<? >[] getDeclaredConstructors()
//获取指定参数类型的public构造方法,没找到抛出异常NoSuchMethodException
public Constructor<T> getConstructor(Class<? >... parameterTypes)
//获取指定参数类型的构造方法,包括非public的,没找到抛出异常NoSuchMethodException
public Constructor<T> getDeclaredConstructor(Class<? >... parameterTypes)
// 通过获取的构造方法来调用newInstance来创建对象
public T newInstance(Object ... initargs) throws InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException
// 举例:
Constructor<StringBuilder> contructor= StringBuilder.class
.getConstructor(new Class[]{int.class});
StringBuilder sb = contructor.newInstance(100);
类型检测和转换:
List<String> list = new ArrayList();
// 我们可以通过instanceof关键字来判断类与类之间的关系。
if(list instanceof ArrayList){
System.out.println("array list");
}
// Class对象中也提供了isInstance方法来用于类型的检测和判断。
Class cls = Class.forName("java.util.ArrayList");
if(cls.isInstance(list)){
System.out.println("array list");
}
// 我们可以通过()的方式来进行强制类型转换。
List list = xxx
if(list instanceof ArrayList){
ArrayList arrList = (ArrayList)list;
}
// 我们也可以通过Class对象中提供的cast方法来进行优雅的类型转换
public static <T> T toType(Object obj, Class<T> cls){
return cls.cast(obj);
}
实战演示:Json序列化
// Person实体类
public class Person {
private String name;
private int age;
private String sex;
public Person() {} // 必须有无参构造函数
public Person(String name, int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
// 序列化工具
public class JsonUtil {
// 对象转 JSON 字符串
public static String toJson(Object obj) throws IllegalAccessException {
Class<?> clazz = obj.getClass();
StringBuilder json = new StringBuilder("{");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAccessible()){
field.setAccessible(true);
}
Object value = field.get(obj);
json.append("\"").append(field.getName()).append("\"")
.append(":");
if (value instanceof String) {
json.append("\"").append(value).append("\"");
} else {
json.append(value);
}
json.append(",");
}
if (fields.length > 0) {
json.deleteCharAt(json.length() - 1);
}
json.append("}");
return json.toString();
}
// JSON 字符串转对象(只支持简单类型)
public static <T> T fromJson(String json, Class<T> clazz) throws Exception {
// 调用无参构造方法
T instance = clazz.getDeclaredConstructor().newInstance();
// 预处理 JSON 字符串:去掉 { } 和空格
json = json.trim().substring(1, json.length() - 1);
String[] pairs = json.split(",");
Map<String, String> kvMap = new HashMap<>();
for (String pair : pairs) {
String[] kv = pair.split(":", 2);
String key = kv[0].trim().replace("\"", "");
String value = kv[1].trim().replace("\"", "");
kvMap.put(key, value);
}
for (Field field : clazz.getDeclaredFields()) {
if (!field.isAccessible()){
field.setAccessible(true);
}
String fieldName = field.getName();
String value = kvMap.get(fieldName);
if (value != null) {
Class<?> type = field.getType();
if (type == int.class || type == Integer.class) {
field.set(instance, Integer.parseInt(value));
} else if (type == String.class) {
field.set(instance, value);
} else if (type == short.class) {
field.setShort(instance, Short.parseShort(value));
} else {
// 其他情况
Constructor<?> constructor = type.getConstructor(new Class[]{String.class});
field.set(instance,constructor.newInstance(value));
}
}
}
return instance;
}
}
public class TestJsonUtil {
public static void main(String[] args) throws Exception {
Person p = new Person("张三", 25,"男");
// 序列化
String json = JsonUtil.toJson(p);
System.out.println("序列化结果:" + json);
// 反序列化
String jsonStr = "{\"name\":\"李四\",\"age\":30,\"sex\":\"男\"}";
Person p2 = JsonUtil.fromJson(jsonStr, Person.class);
System.out.println("反序列化对象:" + p2);
}
}
序列化结果:{"name":"张三","age":25,"sex":"男"}
反序列化对象:Person{name='李四', age=30, sex='男'}
小结:
反射是一项比较强大的功能,在各种知名的Java相关框架中都会应用反射机制,反射在日常业务代码中可能很少会出现,但是懂得反射可以让你看别人的源码时有一种恍然大悟的感觉。
反射相关练习题(正在进行中):
字段拷贝工具类
编写一个copy(Object src, Object dest)
方法,将两个相同类型对象的字段值进行复制(类似浅拷贝)。手写 ORM 映射小工具
创建一个注解@Column(name="xxx")
,实现将一个 Java 对象转成一条 SQLINSERT
语句。手写 JSON 解析器(进阶版)
在我们刚才的例子基础上扩展:支持嵌套对象、List、布尔值等。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 小王
评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果