什么是反射:

  • 在 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 对象转成一条 SQL INSERT 语句。

    • 手写 JSON 解析器(进阶版)
      在我们刚才的例子基础上扩展:支持嵌套对象、List、布尔值等。