Java反射系列(1):入门基础

本文深入解析Java反射机制,介绍Class对象及其获取方式,演示如何利用反射动态实例化对象、调用方法及修改属性,探讨其在Spring框架中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于最近在看spring源码,从Ioc入手,其实现用到了java的反射,所以打算仔细学习一下。

什么是java反射?

动态获取运行时的类信息。什么意思呢?对于任意一个类,能获得它的相关属性(包括字段,注解)和方法;对于任意一个对象,能调用它的属性和方法;
一般来说,我们开发时,比如实例化一个对象
Car car = new Car();
或者设置一个属性
car.setBrand("XXX");
调用一个方法

car.introduce();

然后代码编译,运行...

但是反射提供了另外一种方式,让我们可以在运行时获取car的属性和方法,并调用,设置相关方法和属性

第一个例子

有一个Car类

public class Car {

    private Video video;

    /**
     * 车牌
     */
    private String brand;

    /**
     * 颜色
     */
    private Integer color;

    /**
     * 最大速度
     */
    private int maxSpeed;

    /**
     * 无参方法
     */
    public void introduce() {
        System.out.println("brand:" + brand + ";color:" + color + ";maxSpeed:" + maxSpeed);
    }

    public void introduceWithParameters(String brand, int maxSpeed) {
        System.out.println("brand:" + brand + ";maxSpeed:" + maxSpeed);
    }
    // 省略get,set方法

}
@Test
    public void demoTest() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 获取Class对象
        Class clazz = Class.forName("com.example.learnjdk.reflation.Car");

        // 获取类的默认构造器对象并通过它实例化Car
        Constructor<Car> constructor = clazz.getConstructor(null);
        Car car = constructor.newInstance();

        // 通过反射设置属性
        Method setBrand = clazz.getDeclaredMethod("setBrand", String.class);
        setBrand.invoke(car, "大众");

        // 通过反射调用方法
        Method introduce = clazz.getMethod("introduce", null);
        introduce.invoke(car, null);

        System.out.println(car);

    }

Class对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
获取Class对象的方式

  • 类字面值来获取指定类型(或 void)的 Class 对象
System.out.println("类字面值方式:" + Car.class.getName());
  • 对象方式
  • System.out.println("对象方式:" + car.getClass().getName());
    Class.forName(String className)
  • Class clazz = Class.forName("com.example.learnjdk.reflation.Car");
    ClassLoader加载
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    Class clazz = loader.loadClass("com.example.learnjdk.reflation.Car");

Class是反射的基础类,还有几个比较重要的反射类

  • Constructor:构造函数反射类
  • Method:类方法的反射类
  • Field:类的成员变量反射类

Constructor

可以通过这个反射类,实例化对象。newInstance是主要方法

T newInstance(Object... initargs)

看下面的例子

// 通过构造函数,实例化对象
        //获取所有构造函数
        Constructor[] constructors = clazz.getConstructors();
        for (int i = 0; i < constructors.length; i++) {
            System.out.println(constructors[i]);
        }

        // 根据指定类型获取无参构造函数
        Constructor<Car> defaultConstructor = clazz.getConstructor(null);
        System.out.println("默认构造函数:" + defaultConstructor);
        Car defaultCar = defaultConstructor.newInstance(null);
        // 相当于new Car()
        System.out.println("默认构造函数创建的对象:" + defaultCar);

        // 根据指定类型获取有参构造函数
        Constructor<Car> constructorHasParameters = clazz.getConstructor(String.class, Integer.class, int.class);
        System.out.println("有参构造:" + constructorHasParameters);
        Car carHasParameters = constructorHasParameters.newInstance("宝马", 2, 5000);
        // 相当于new Car("宝马", 2, 5000)
        System.out.println("有参构造的函数:" + carHasParameters);

Method

提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)

Method[] getDeclaredMethods()

这个方法获取所有返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

// 获取反射的所有的方法,并调用
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println("反射获取的方法:" + methods[i]);
            // 以 String 形式返回此 Method 对象表示的方法名称。
            System.out.println("方法名称:" + methods[i].getName());

            Class[] parameterTypes = methods[i].getParameterTypes();
            // 按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。
            // 如果底层方法不带参数,则返回长度为 0 的数组。
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.println("方法参数类型:" + parameterTypes[j]);
            }

            // 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
            Class<?> returnType = methods[i].getReturnType();
            System.out.println("方法返回类型:" + returnType);
        }

调用一下对应的方法,

public Object invoke(Object obj,
                     Object... args)
// 获取类的默认构造器对象并通过它实例化Car
        Constructor<Car> constructor = clazz.getConstructor(null);
        Car car = constructor.newInstance();

        // 通过反射设置属性
        Method setBrand = clazz.getDeclaredMethod("setBrand", String.class);
        setBrand.invoke(car, "大众");

        // 通过反射调用方法
        Method introduce = clazz.getMethod("introduce", null);
        introduce.invoke(car, null);

        System.out.println(car);

这里要注意一点,对私有方法的访问,在Car类里面再加一个私有方法

private void innerMethod() {
        System.out.println("This is a private method");
    }
// 私有方法
        Method innerMethod = clazz.getDeclaredMethod("innerMethod", null);
//        innerMethod.setAccessible(true);
        innerMethod.invoke(car, null);

调用试试,会抛出异常,这时需要设置innerMethod.setAccessible(true);才可以访问,下面的Field也是类似

Field

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

Field类最主要的方法是set(Object obj, Object value)

public void set(Object obj,
                Object value)
// 设置属性值brand
            Field brand = clazz.getDeclaredField("brand");
            // 设置访问private
            brand.setAccessible(true);
            brand.set(car, "奔驰");
            // 设置maxSpeed
            Field maxSpeed = clazz.getDeclaredField("maxSpeed");
            maxSpeed.setAccessible(true);
            maxSpeed.set(car, 500);
            // 对于基本类型,有对应的setType方法;比如int,setInt;boolean,setBoolean
            maxSpeed.setInt(car, 600);
            System.out.println(car);

输出 

 Java 语言访问控制检查

在上面例子中,是不能直接访问私有方法,属性的,必须要设置setAccessible(true).

那具体是什么意思呢?

将对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

并且,提供了相应的方法,返回对象的修饰符,来确定到底是private,protect,public...

public int getModifiers()

以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码。

// 获取语言修饰符
            int modifier = brand.getModifiers();
            System.out.println(brand.getName() + "的修饰符是" + modifier);

            // 获取方法修饰符
            int methodModifier = setBrand.getModifiers();
            System.out.println(setBrand.getName() + "的修饰符是" + methodModifier);

根据对照可以发现是private属性,public方法

最后提供一个反射的方法类,有需要的朋友可以看看

package com.example.learnjdk.reflation;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 反射工具类.
 * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
 */
@Slf4j
public class Reflections {

    /**
     * set方法前缀
     */
    private static final String SETTER_PREFIX = "set";

    /**
     * get方法前缀
     */
    private static final String GETTER_PREFIX = "get";

    /**
     * 内部类路径
     */
    private static final String CGLIB_CLASS_SEPARATOR = "$$";

    /**
     * 字段属性分隔符
     */
    private static final String PROPERTY_SAPERATOR = ".";

    /**
     * 默认参数
     */
    private static final Class<?>[] PARAMETER_TYPES = new Class[]{};

    /**
     * 默认参数
     */
    private static final Object[] PARAMETER_ARGS = new Object[]{};

    private static final Pattern LIST_PARAM_PAT = Pattern.compile("\\[[0-9]+\\]");

    /**
     * 调用Getter方法.
     * 支持多级,如:对象名.对象名.方法
     * 支持list元素,如: addressInfo.selfAddress[1].name
     */
    public static Object invokeGetter(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException {

        if (null == obj || StringUtils.isBlank(propertyName)) {
            return null;
        }
        Object object = obj;
        // 多级属性
        for (String name : StringUtils.split(propertyName, PROPERTY_SAPERATOR)) {

            // 兼容List格式:name[1]
            Matcher matcher = LIST_PARAM_PAT.matcher(name);
            if (matcher.find()) {
                name = StringUtils.replace(name, matcher.group(), "");
            }

            object = invokeMethod(object, GETTER_PREFIX + StringUtils.capitalize(name), PARAMETER_TYPES, PARAMETER_ARGS);
            // 如果上级对象为空则返回null
            if (null == object) {
                return null;
            }
            if (object instanceof List) {
                String matchStr = matcher.group();
                Integer ind = Integer.valueOf(matchStr.substring(1, matchStr.length() - 1));
                List list = (List) object;
                if (list.size() <= ind) {
                    return null;
                }
                object = list.get(ind);
            }

        }
        return object;
    }

    /**
     * 调用Setter方法, 仅匹配方法名。
     * 支持多级,如:对象名.对象名.方法
     */
    public static void invokeSetter(Object obj, String propertyName, Object value) throws InvocationTargetException, IllegalAccessException {

        if (null == obj || StringUtils.isBlank(propertyName)) {
            return;
        }
        Object object = obj;
        String[] names = StringUtils.split(propertyName, PROPERTY_SAPERATOR);
        for (int i = 0; i < names.length; i++) {
            // 获取上级对象
            if (i < names.length - 1) {
                object = invokeMethod(object, GETTER_PREFIX + StringUtils.capitalize(names[i]), PARAMETER_TYPES, PARAMETER_ARGS);
                // 如果上级对象不存在则不设置
                if (null == object) {
                    return;
                }
                continue;
            }
            invokeMethodByName(object, SETTER_PREFIX + StringUtils.capitalize(names[i]), new Object[]{value});
        }
    }

    /**
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
     */
    public static Object getFieldValue(final Object obj, final String fieldName) throws IllegalAccessException {

        // 参数为空
        if (null == obj || StringUtils.isBlank(fieldName)) {
            return null;
        }
        Field field = getAccessibleField(obj, fieldName);
        // 字段为空
        if (field == null) {
            return null;
        }
        try {
            return field.get(obj);
        } catch (IllegalAccessException e) {
            throw e;
        }
    }

    /**
     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
     */
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws IllegalAccessException {

        if (null == obj || StringUtils.isBlank(fieldName) || null == value) {
            return;
        }
        Field field = getAccessibleField(obj, fieldName);

        if (field == null) {
            return;
        }
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            throw e;
        }
    }

    /**
     * 直接调用对象方法, 无视private/protected修饰符.
     * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
     * 同时匹配方法名+参数类型,
     */
    public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
                                      final Object[] args) throws InvocationTargetException, IllegalAccessException {

        if (null == obj || StringUtils.isBlank(methodName)) {
            return null;
        }
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null) {
            return null;
        }
        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 直接调用对象方法, 无视private/protected修饰符,
     * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
     * 只匹配函数名,如果有多个同名函数调用第一个。
     */
    public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) throws InvocationTargetException, IllegalAccessException {

        if (null == obj || StringUtils.isBlank(methodName)) {
            return null;
        }
        Method method = getAccessibleMethodByName(obj, methodName);
        if (method == null) {
            return null;
        }
        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw e;
        }
    }


    /**
     * 根据调用静态方法
     */
    public static Object invokeStaticMethodByName(final String classPath, final String methodName, final Object[] args) throws Exception {

        if (StringUtils.isBlank(classPath) || StringUtils.isBlank(methodName)) {
            return null;
        }
        Class<?> clzz = getClass(classPath);
        if (null == clzz) {
            return null;
        }
        try {
            Method method = clzz.getMethod(methodName, String.class);
            if (method == null) {
                return null;
            }
            return method.invoke(null, args);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
     * <p>
     * 如向上转型到Object仍无法找到, 返回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName) {

        if (null == obj || StringUtils.isBlank(fieldName)) {
            return null;
        }
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            } catch (NoSuchFieldException e) {//NOSONAR
                // Field不在当前类定义,继续向上转型
                continue;// new add
            }
        }
        return null;
    }

    /**
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     * 匹配函数名+参数类型。
     * <p>
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethod(final Object obj, final String methodName,
                                             final Class<?>... parameterTypes) {

        if (null == obj || StringUtils.isBlank(methodName)) {
            return null;
        }
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
            try {
                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
                makeAccessible(method);
                return method;
            } catch (NoSuchMethodException e) {
                // Method不在当前类定义,继续向上转型
                continue;// new add
            }
        }
        return null;
    }

    /**
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     * 只匹配函数名。
     * <p>
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethodByName(final Object obj, final String methodName) {

        if (null == obj || StringUtils.isBlank(methodName)) {
            return null;
        }
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
            Method[] methods = searchType.getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    makeAccessible(method);
                    return method;
                }
            }
        }
        return null;
    }

    /**
     * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Method method) {

        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
                && !method.isAccessible()) {
            method.setAccessible(true);
        }
    }


    /**
     * 通过发射查询对象的方法
     *
     * @param clazz          对象的class
     * @param methodName     方法名称
     * @param parameterTypes 方法参数
     * @return 方法对象
     */
    public static Method getMethod(final Class clazz, final String methodName,
                                   final Class<?>... parameterTypes) {
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Field field) {

        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
                .isFinal(field.getModifiers())) && !field.isAccessible()) {
            field.setAccessible(true);
        }
    }

    /**
     * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
     * 如无法找到, 返回Object.class.
     * eg.
     * public UserDao extends HibernateDao<User>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or Object.class if cannot be determined
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> getClassGenricType(final Class clazz) {
        return getClassGenricType(clazz, 0);
    }

    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
     * 如无法找到, 返回Object.class.
     * <p>
     * 如public UserDao extends HibernateDao<User,Long>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     * @return the index generic declaration, or Object.class if cannot be determined
     */
    public static Class getClassGenricType(final Class clazz, final int index) {

        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {

            log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {

            log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {

            log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class) params[index];
    }

    public static Class<?> getUserClass(Object instance) {

        if (null == instance) {
            return null;
        }
        Class clazz = instance.getClass();
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {

            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass)) {
                return superClass;
            }
        }
        return clazz;
    }

    /**
     * 获取Class对象
     */
    public static Class<?> getClass(String calssPath) {

        if (StringUtils.isBlank(calssPath)) {
            return null;
        }
        try {
            return Class.forName(calssPath);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    /**
     * 创建对象
     */
    public static Object newInstance(Constructor constructor, Object... initargs) {

        if (null == constructor) {
            return null;
        }
        try {
            return constructor.newInstance(initargs);
        } catch (Exception e) {
            return null;
        }
    }
}

demo代码github:https://github.com/kobet/threa-learn

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值