【Java】反射详解
Java 反射详解
运行时动态获取类信息、创建对象、调用方法完整教程
目录
一、反射概述
1.1 什么是反射
反射(Reflection)是Java提供的一种机制,允许程序在运行时检查和操作类的结构(类、方法、字段等)。
反射的核心功能:
- 运行时获取类的信息
- 动态创建对象
- 动态调用方法
- 动态访问和修改字段
1.2 反射的应用场景
- 框架开发:Spring、Hibernate等框架大量使用反射
- 动态代理:AOP面向切面编程
- 注解处理:运行时处理注解
- 插件系统:动态加载类
- 序列化/反序列化:JSON、XML转换
1.3 反射的优缺点
优点:
- 灵活性高,可以动态操作类
- 降低代码耦合度
- 适合框架和工具开发
缺点:
- 性能开销大(比直接调用慢10-100倍)
- 破坏封装性(可访问私有成员)
- 代码可读性差
- 编译时无法检查类型安全
二、获取Class对象
2.1 三种获取方式
publicclassGetClassDemo{publicstaticvoidmain(String[] args)throwsException{// 方式1:通过类名.classClass<String> clazz1 =String.class;// 方式2:通过对象.getClass()String str ="Hello";Class<?extendsString> clazz2 = str.getClass();// 方式3:通过Class.forName()Class<?> clazz3 =Class.forName("java.lang.String");// 三种方式获取的是同一个Class对象System.out.println(clazz1 == clazz2);// trueSystem.out.println(clazz2 == clazz3);// true}}2.2 获取类信息
publicclassClassInfoDemo{publicstaticvoidmain(String[] args){Class<String> clazz =String.class;// 类名System.out.println("简单名: "+ clazz.getSimpleName());// StringSystem.out.println("全限定名: "+ clazz.getName());// java.lang.StringSystem.out.println("规范名: "+ clazz.getCanonicalName());// java.lang.String// 包信息System.out.println("包名: "+ clazz.getPackage().getName());// java.lang// 父类System.out.println("父类: "+ clazz.getSuperclass());// class java.lang.Object// 接口Class<?>[] interfaces = clazz.getInterfaces();for(Class<?> i : interfaces){System.out.println("接口: "+ i.getName());}// 修饰符int modifiers = clazz.getModifiers();System.out.println("是否public: "+java.lang.reflect.Modifier.isPublic(modifiers));System.out.println("是否final: "+java.lang.reflect.Modifier.isFinal(modifiers));System.out.println("是否abstract: "+java.lang.reflect.Modifier.isAbstract(modifiers));System.out.println("是否interface: "+java.lang.reflect.Modifier.isInterface(modifiers));}}2.3 判断类型
publicclassTypeCheckDemo{publicstaticvoidmain(String[] args){Class<String> clazz =String.class;// 判断类型System.out.println("是否接口: "+ clazz.isInterface());// falseSystem.out.println("是否数组: "+ clazz.isArray());// falseSystem.out.println("是否枚举: "+ clazz.isEnum());// falseSystem.out.println("是否注解: "+ clazz.isAnnotation());// falseSystem.out.println("是否基本类型: "+ clazz.isPrimitive());// false// 数组类型Class<?> arrayClass =int[].class;System.out.println("是否数组: "+ arrayClass.isArray());// trueSystem.out.println("数组元素类型: "+ arrayClass.getComponentType());// int// 判断继承关系System.out.println("String是否是Object的子类: "+Object.class.isAssignableFrom(String.class));// trueSystem.out.println("Object是否是String的子类: "+String.class.isAssignableFrom(Object.class));// false}}2.4 获取注解
importjava.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interfaceAuthor{Stringname();Stringdate();}@Author(name ="张三", date ="2025-01-01")classMyClass{}publicclassAnnotationDemo{publicstaticvoidmain(String[] args){Class<MyClass> clazz =MyClass.class;// 判断是否有注解if(clazz.isAnnotationPresent(Author.class)){// 获取注解Author author = clazz.getAnnotation(Author.class);System.out.println("作者: "+ author.name());System.out.println("日期: "+ author.date());}// 获取所有注解Annotation[] annotations = clazz.getAnnotations();for(Annotation ann : annotations){System.out.println("注解: "+ ann);}}}三、构造方法反射
3.1 获取构造方法
importjava.lang.reflect.Constructor;publicclassPerson{privateString name;privateint age;publicPerson(){}publicPerson(String name){this.name = name;}publicPerson(String name,int age){this.name = name;this.age = age;}privatePerson(int age){this.age = age;}}publicclassConstructorDemo{publicstaticvoidmain(String[] args)throwsException{Class<Person> clazz =Person.class;// 获取所有public构造方法Constructor<?>[] constructors = clazz.getConstructors();for(Constructor<?> c : constructors){System.out.println(c);}// 获取所有构造方法(包括私有)Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();// 获取指定参数的public构造方法Constructor<Person> constructor1 = clazz.getConstructor();// 无参Constructor<Person> constructor2 = clazz.getConstructor(String.class);// 一参Constructor<Person> constructor3 = clazz.getConstructor(String.class,int.class);// 两参// 获取私有构造方法Constructor<Person> privateConstructor = clazz.getDeclaredConstructor(int.class);}}3.2 创建对象
publicclassCreateInstanceDemo{publicstaticvoidmain(String[] args)throwsException{Class<Person> clazz =Person.class;// 方式1:使用无参构造Person p1 = clazz.getDeclaredConstructor().newInstance();// 方式2:使用有参构造Constructor<Person> constructor = clazz.getConstructor(String.class,int.class);Person p2 = constructor.newInstance("张三",25);// 方式3:访问私有构造方法Constructor<Person> privateConstructor = clazz.getDeclaredConstructor(int.class); privateConstructor.setAccessible(true);// 取消访问检查Person p3 = privateConstructor.newInstance(30);System.out.println(p2);}}四、字段反射
4.1 获取字段
importjava.lang.reflect.Field;publicclassStudent{publicString name;privateint age;protectedString school;staticString country ="China";}publicclassFieldDemo{publicstaticvoidmain(String[] args)throwsException{Class<Student> clazz =Student.class;// 获取所有public字段(包括继承的)Field[] fields = clazz.getFields();// 获取所有字段(不包括继承的)Field[] allFields = clazz.getDeclaredFields();// 获取指定字段Field nameField = clazz.getField("name");// public字段Field ageField = clazz.getDeclaredField("age");// 私有字段// 字段信息System.out.println("字段名: "+ nameField.getName());System.out.println("字段类型: "+ nameField.getType());System.out.println("修饰符: "+ nameField.getModifiers());}}4.2 访问和修改字段
publicclassFieldAccessDemo{publicstaticvoidmain(String[] args)throwsException{Student student =newStudent();Class<Student> clazz =Student.class;// 访问public字段Field nameField = clazz.getField("name"); nameField.set(student,"李四");String name =(String) nameField.get(student);System.out.println("姓名: "+ name);// 访问private字段Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true);// 取消访问检查 ageField.set(student,20);int age =(int) ageField.get(student);System.out.println("年龄: "+ age);// 访问静态字段Field countryField = clazz.getDeclaredField("country"); countryField.set(null,"USA");// 静态字段传nullString country =(String) countryField.get(null);System.out.println("国家: "+ country);}}五、方法反射
5.1 获取方法
importjava.lang.reflect.Method;publicclassCalculator{publicintadd(int a,int b){return a + b;}privateintsubtract(int a,int b){return a - b;}publicstaticintmultiply(int a,int b){return a * b;}}publicclassMethodDemo{publicstaticvoidmain(String[] args)throwsException{Class<Calculator> clazz =Calculator.class;// 获取所有public方法(包括继承的)Method[] methods = clazz.getMethods();// 获取所有方法(不包括继承的)Method[] allMethods = clazz.getDeclaredMethods();// 获取指定方法Method addMethod = clazz.getMethod("add",int.class,int.class);Method subtractMethod = clazz.getDeclaredMethod("subtract",int.class,int.class);// 方法信息System.out.println("方法名: "+ addMethod.getName());System.out.println("返回类型: "+ addMethod.getReturnType());System.out.println("参数类型: "+Arrays.toString(addMethod.getParameterTypes()));}}5.2 调用方法
publicclassMethodInvokeDemo{publicstaticvoidmain(String[] args)throwsException{Calculator calculator =newCalculator();Class<Calculator> clazz =Calculator.class;// 调用public方法Method addMethod = clazz.getMethod("add",int.class,int.class);Object result1 = addMethod.invoke(calculator,10,20);System.out.println("10 + 20 = "+ result1);// 30// 调用private方法Method subtractMethod = clazz.getDeclaredMethod("subtract",int.class,int.class); subtractMethod.setAccessible(true);Object result2 = subtractMethod.invoke(calculator,30,10);System.out.println("30 - 10 = "+ result2);// 20// 调用静态方法Method multiplyMethod = clazz.getMethod("multiply",int.class,int.class);Object result3 = multiplyMethod.invoke(null,5,6);// 静态方法传nullSystem.out.println("5 * 6 = "+ result3);// 30}}六、实战案例
6.1 通用对象打印器
importjava.lang.reflect.Field;publicclassObjectPrinter{publicstaticvoidprint(Object obj)throwsException{Class<?> clazz = obj.getClass();System.out.println("=== "+ clazz.getSimpleName()+" ===");Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);String name = field.getName();Object value = field.get(obj);System.out.println(name +" = "+ value);}}publicstaticvoidmain(String[] args)throwsException{Person person =newPerson("张三",25);print(person);}}6.2 简单依赖注入
importjava.lang.annotation.*;importjava.lang.reflect.Field;// 自定义注解@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@interfaceInject{}// 服务类classUserService{publicvoidsave(){System.out.println("保存用户");}}// 控制器类classUserController{@InjectprivateUserService userService;publicvoidregister(){ userService.save();}}// 依赖注入容器publicclassSimpleContainer{publicstaticvoidinject(Object obj)throwsException{Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){if(field.isAnnotationPresent(Inject.class)){ field.setAccessible(true);Class<?> fieldType = field.getType();Object instance = fieldType.getDeclaredConstructor().newInstance(); field.set(obj, instance);}}}publicstaticvoidmain(String[] args)throwsException{UserController controller =newUserController();inject(controller);// 注入依赖 controller.register();// 保存用户}}6.3 通用对象复制
importjava.lang.reflect.Field;publicclassObjectCopier{publicstatic<T>Tcopy(T source)throwsException{Class<?> clazz = source.getClass();// 创建新对象@SuppressWarnings("unchecked")T target =(T) clazz.getDeclaredConstructor().newInstance();// 复制所有字段Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);Object value = field.get(source); field.set(target, value);}return target;}publicstaticvoidmain(String[] args)throwsException{Person original =newPerson("张三",25);Person copied =copy(original);System.out.println("原对象: "+ original);System.out.println("复制对象: "+ copied);System.out.println("是否同一对象: "+(original == copied));// false}}6.4 动态代理
importjava.lang.reflect.*;// 接口interfaceUserDao{voidsave();voiddelete();}// 实现类classUserDaoImplimplementsUserDao{@Overridepublicvoidsave(){System.out.println("保存用户");}@Overridepublicvoiddelete(){System.out.println("删除用户");}}// 代理处理器classLogHandlerimplementsInvocationHandler{privateObject target;publicLogHandler(Object target){this.target = target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{System.out.println(">>> 方法开始: "+ method.getName());Object result = method.invoke(target, args);System.out.println("<<< 方法结束: "+ method.getName());return result;}}// 测试publicclassProxyDemo{publicstaticvoidmain(String[] args){UserDao userDao =newUserDaoImpl();// 创建代理对象UserDao proxy =(UserDao)Proxy.newProxyInstance( userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(),newLogHandler(userDao));// 调用代理方法 proxy.save();System.out.println(); proxy.delete();}}6.5 配置文件加载
importjava.io.InputStream;importjava.lang.reflect.Field;importjava.util.Properties;// 配置类classAppConfig{privateString appName;privateint port;privateString database;// getters...}// 配置加载器publicclassConfigLoader{publicstatic<T>Tload(Class<T> clazz,String configFile)throwsException{// 读取配置文件Properties props =newProperties();InputStream is = clazz.getClassLoader().getResourceAsStream(configFile); props.load(is);// 创建对象T instance = clazz.getDeclaredConstructor().newInstance();// 设置字段值Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);String key = field.getName();String value = props.getProperty(key);if(value !=null){// 类型转换if(field.getType()==int.class){ field.set(instance,Integer.parseInt(value));}elseif(field.getType()==String.class){ field.set(instance, value);}}}return instance;}}七、高级应用
7.1 泛型信息获取
importjava.lang.reflect.*;importjava.util.*;publicclassGenericDemo{// 带泛型的字段privateList<String> stringList;privateMap<String,Integer> map;// 带泛型的方法publicList<String>getList(){returnnull;}public<T>TgenericMethod(T param){return param;}publicstaticvoidmain(String[] args)throwsException{Class<GenericDemo> clazz =GenericDemo.class;// 1. 获取字段的泛型类型Field field = clazz.getDeclaredField("stringList");Type genericType = field.getGenericType();if(genericType instanceofParameterizedType){ParameterizedType pt =(ParameterizedType) genericType;System.out.println("原始类型: "+ pt.getRawType());// interface java.util.ListType[] actualTypes = pt.getActualTypeArguments();System.out.println("泛型参数: "+ actualTypes[0]);// class java.lang.String}// 2. 获取Map的泛型类型Field mapField = clazz.getDeclaredField("map");Type mapType = mapField.getGenericType();if(mapType instanceofParameterizedType){ParameterizedType pt =(ParameterizedType) mapType;Type[] types = pt.getActualTypeArguments();System.out.println("Key类型: "+ types[0]);// class java.lang.StringSystem.out.println("Value类型: "+ types[1]);// class java.lang.Integer}// 3. 获取方法返回值的泛型类型Method method = clazz.getMethod("getList");Type returnType = method.getGenericReturnType();System.out.println("返回类型: "+ returnType);// 4. 获取方法的泛型参数Method genericMethod = clazz.getMethod("genericMethod",Object.class);TypeVariable<?>[] typeParams = genericMethod.getTypeParameters();for(TypeVariable<?> tv : typeParams){System.out.println("泛型参数名: "+ tv.getName());// T}}}7.2 数组操作
importjava.lang.reflect.Array;publicclassArrayReflectionDemo{publicstaticvoidmain(String[] args){// 1. 创建数组Object intArray =Array.newInstance(int.class,5);Array.set(intArray,0,10);Array.set(intArray,1,20);System.out.println("第一个元素: "+Array.get(intArray,0));// 10System.out.println("数组长度: "+Array.getLength(intArray));// 5// 2. 创建多维数组Object multiArray =Array.newInstance(int.class,3,4);System.out.println("多维数组类型: "+ multiArray.getClass());// class [[I// 3. 动态扩展数组int[] oldArray ={1,2,3};int[] newArray =(int[])expandArray(oldArray,6);System.out.println("新数组长度: "+ newArray.length);// 6System.out.println("第一个元素: "+ newArray[0]);// 1}// 通用数组扩展方法publicstaticObjectexpandArray(Object oldArray,int newLength){Class<?> componentType = oldArray.getClass().getComponentType();int oldLength =Array.getLength(oldArray);Object newArray =Array.newInstance(componentType, newLength);// 复制旧数组内容System.arraycopy(oldArray,0, newArray,0,Math.min(oldLength, newLength));return newArray;}}7.3 性能优化
importjava.lang.reflect.*;importjava.util.concurrent.ConcurrentHashMap;publicclassReflectionCache{// 缓存Class对象privatestaticfinalConcurrentHashMap<String,Class<?>> classCache =newConcurrentHashMap<>();// 缓存Method对象privatestaticfinalConcurrentHashMap<String,Method> methodCache =newConcurrentHashMap<>();// 缓存Field对象privatestaticfinalConcurrentHashMap<String,Field> fieldCache =newConcurrentHashMap<>();// 获取Class(带缓存)publicstaticClass<?>getClass(String className)throwsClassNotFoundException{return classCache.computeIfAbsent(className, key ->{try{returnClass.forName(key);}catch(ClassNotFoundException e){thrownewRuntimeException(e);}});}// 获取Method(带缓存)publicstaticMethodgetMethod(Class<?> clazz,String methodName,Class<?>... paramTypes){String key = clazz.getName()+"#"+ methodName;return methodCache.computeIfAbsent(key, k ->{try{Method method = clazz.getMethod(methodName, paramTypes); method.setAccessible(true);// 提前设置,避免每次调用return method;}catch(NoSuchMethodException e){thrownewRuntimeException(e);}});}// 获取Field(带缓存)publicstaticFieldgetField(Class<?> clazz,String fieldName){String key = clazz.getName()+"#"+ fieldName;return fieldCache.computeIfAbsent(key, k ->{try{Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true);return field;}catch(NoSuchFieldException e){thrownewRuntimeException(e);}});}// 性能测试publicstaticvoidmain(String[] args)throwsException{Class<String> clazz =String.class;Method method = clazz.getMethod("length");// 测试1:不使用缓存long start1 =System.nanoTime();for(int i =0; i <100000; i++){Method m = clazz.getMethod("length");}long end1 =System.nanoTime();System.out.println("不使用缓存: "+(end1 - start1)/1000000+"ms");// 测试2:使用缓存long start2 =System.nanoTime();for(int i =0; i <100000; i++){Method m =getMethod(clazz,"length");}long end2 =System.nanoTime();System.out.println("使用缓存: "+(end2 - start2)/1000000+"ms");}}7.4 方法句柄(MethodHandle)
MethodHandle 是 JDK 7 引入的,性能比反射更好。
importjava.lang.invoke.*;publicclassMethodHandleDemo{publicstaticvoidmain(String[] args)throwsThrowable{// 1. 获取MethodHandles.LookupMethodHandles.Lookup lookup =MethodHandles.lookup();// 2. 查找方法MethodType mt =MethodType.methodType(int.class);MethodHandle mh = lookup.findVirtual(String.class,"length", mt);// 3. 调用方法String str ="Hello";int length =(int) mh.invoke(str);System.out.println("字符串长度: "+ length);// 5// 4. 性能对比performanceTest();}publicstaticvoidperformanceTest()throwsThrowable{String str ="Hello World";int iterations =10000000;// 直接调用long start1 =System.nanoTime();for(int i =0; i < iterations; i++){int len = str.length();}long end1 =System.nanoTime();System.out.println("直接调用: "+(end1 - start1)/1000000+"ms");// 反射调用Method method =String.class.getMethod("length");long start2 =System.nanoTime();for(int i =0; i < iterations; i++){int len =(int) method.invoke(str);}long end2 =System.nanoTime();System.out.println("反射调用: "+(end2 - start2)/1000000+"ms");// MethodHandle调用MethodHandles.Lookup lookup =MethodHandles.lookup();MethodType mt =MethodType.methodType(int.class);MethodHandle mh = lookup.findVirtual(String.class,"length", mt);long start3 =System.nanoTime();for(int i =0; i < iterations; i++){int len =(int) mh.invoke(str);}long end3 =System.nanoTime();System.out.println("MethodHandle: "+(end3 - start3)/1000000+"ms");}}性能对比结果(参考):
- 直接调用:~5ms
- MethodHandle:~50ms(比直接调用慢10倍)
- 反射调用:~500ms(比直接调用慢100倍)
7.5 简单ORM框架
importjava.lang.annotation.*;importjava.lang.reflect.*;importjava.sql.*;importjava.util.*;// 表名注解@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interfaceTable{Stringvalue();}// 列名注解@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@interfaceColumn{Stringvalue();}// 实体类@Table("users")classUser{@Column("id")privateLong id;@Column("username")privateString username;@Column("age")privateInteger age;// getters and setters...publicLonggetId(){return id;}publicvoidsetId(Long id){this.id = id;}publicStringgetUsername(){return username;}publicvoidsetUsername(String username){this.username = username;}publicIntegergetAge(){return age;}publicvoidsetAge(Integer age){this.age = age;}@OverridepublicStringtoString(){return"User{id="+ id +",+ username +"', age="+ age +"}";}}// 简单ORMpublicclassSimpleORM{// 查询所有publicstatic<T>List<T>findAll(Class<T> clazz,Connection conn)throwsException{// 获取表名Table table = clazz.getAnnotation(Table.class);String tableName = table.value();// 执行查询String sql ="SELECT * FROM "+ tableName;Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(sql);// 映射结果List<T> results =newArrayList<>();while(rs.next()){T obj =mapResultSet(clazz, rs); results.add(obj);} rs.close(); stmt.close();return results;}// 根据ID查询publicstatic<T>TfindById(Class<T> clazz,Connection conn,Object id)throwsException{Table table = clazz.getAnnotation(Table.class);String tableName = table.value();String sql ="SELECT * FROM "+ tableName +" WHERE id = ?";PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setObject(1, id);ResultSet rs = pstmt.executeQuery();T result =null;if(rs.next()){ result =mapResultSet(clazz, rs);} rs.close(); pstmt.close();return result;}// 插入publicstatic<T>voidinsert(T obj,Connection conn)throwsException{Class<?> clazz = obj.getClass();Table table = clazz.getAnnotation(Table.class);String tableName = table.value();// 构建SQLStringBuilder columns =newStringBuilder();StringBuilder values =newStringBuilder();List<Object> params =newArrayList<>();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){if(field.isAnnotationPresent(Column.class)){Column column = field.getAnnotation(Column.class); field.setAccessible(true);Object value = field.get(obj);if(value !=null){if(columns.length()>0){ columns.append(", "); values.append(", ");} columns.append(column.value()); values.append("?"); params.add(value);}}}String sql ="INSERT INTO "+ tableName +" ("+ columns +") VALUES ("+ values +")";PreparedStatement pstmt = conn.prepareStatement(sql);for(int i =0; i < params.size(); i++){ pstmt.setObject(i +1, params.get(i));} pstmt.executeUpdate(); pstmt.close();}// 映射ResultSet到对象privatestatic<T>TmapResultSet(Class<T> clazz,ResultSet rs)throwsException{T obj = clazz.getDeclaredConstructor().newInstance();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){if(field.isAnnotationPresent(Column.class)){Column column = field.getAnnotation(Column.class);String columnName = column.value(); field.setAccessible(true);Object value = rs.getObject(columnName); field.set(obj, value);}}return obj;}// 测试(需要数据库连接)publicstaticvoidmain(String[] args){// 示例代码,实际需要数据库连接System.out.println("简单ORM框架示例");System.out.println("使用反射实现对象关系映射");}}7.6 Bean拷贝工具
importjava.lang.reflect.Field;importjava.util.*;publicclassBeanUtils{// 浅拷贝publicstatic<T>TshallowCopy(T source)throwsException{if(source ==null)returnnull;@SuppressWarnings("unchecked")Class<T> clazz =(Class<T>) source.getClass();T target = clazz.getDeclaredConstructor().newInstance();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);Object value = field.get(source); field.set(target, value);}return target;}// 属性拷贝(不同类型)publicstaticvoidcopyProperties(Object source,Object target)throwsException{if(source ==null|| target ==null)return;Class<?> sourceClass = source.getClass();Class<?> targetClass = target.getClass();Field[] sourceFields = sourceClass.getDeclaredFields();Map<String,Field> targetFieldMap =newHashMap<>();for(Field field : targetClass.getDeclaredFields()){ targetFieldMap.put(field.getName(), field);}for(Field sourceField : sourceFields){String fieldName = sourceField.getName();Field targetField = targetFieldMap.get(fieldName);if(targetField !=null&& sourceField.getType()== targetField.getType()){ sourceField.setAccessible(true); targetField.setAccessible(true);Object value = sourceField.get(source); targetField.set(target, value);}}}// 对象转MappublicstaticMap<String,Object>toMap(Object obj)throwsException{if(obj ==null)returnnull;Map<String,Object> map =newHashMap<>();Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);String key = field.getName();Object value = field.get(obj); map.put(key, value);}return map;}// Map转对象publicstatic<T>TfromMap(Map<String,Object> map,Class<T> clazz)throwsException{if(map ==null)returnnull;T obj = clazz.getDeclaredConstructor().newInstance();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){ field.setAccessible(true);String key = field.getName();Object value = map.get(key);if(value !=null){ field.set(obj, value);}}return obj;}publicstaticvoidmain(String[] args)throwsException{// 测试对象转MapUser user =newUser(); user.setId(1L); user.setUsername("张三"); user.setAge(25);Map<String,Object> map =toMap(user);System.out.println("对象转Map: "+ map);// 测试Map转对象User user2 =fromMap(map,User.class);System.out.println("Map转对象: "+ user2);}}八、最佳实践与注意事项
8.1 性能优化建议
publicclassReflectionBestPractices{// ❌ 不推荐:每次都获取MethodpublicvoidbadPractice(Object obj)throwsException{for(int i =0; i <10000; i++){Method method = obj.getClass().getMethod("toString"); method.invoke(obj);}}// ✅ 推荐:缓存Method对象privatestaticfinalMap<String,Method> methodCache =newConcurrentHashMap<>();publicvoidgoodPractice(Object obj)throwsException{Method method = methodCache.computeIfAbsent( obj.getClass().getName()+"#toString", k ->{try{Method m = obj.getClass().getMethod("toString"); m.setAccessible(true);// 提前设置return m;}catch(NoSuchMethodException e){thrownewRuntimeException(e);}});for(int i =0; i <10000; i++){ method.invoke(obj);}}}性能优化要点:
- 缓存 Class、Method、Field、Constructor 对象
- 提前调用
setAccessible(true),避免每次检查 - 使用 MethodHandle 替代反射(JDK 7+)
- 避免在循环中使用反射
- 考虑使用字节码生成(如 ASM、Javassist)
8.2 异常处理
反射操作会抛出多种异常,需要妥善处理。
publicclassReflectionExceptionHandling{publicstaticObjectinvokeMethod(Object obj,String methodName,Object... args){try{Class<?> clazz = obj.getClass();Class<?>[] paramTypes =newClass[args.length];for(int i =0; i < args.length; i++){ paramTypes[i]= args[i].getClass();}Method method = clazz.getMethod(methodName, paramTypes);return method.invoke(obj, args);}catch(NoSuchMethodException e){System.err.println("方法不存在: "+ methodName);}catch(IllegalAccessException e){System.err.println("无法访问方法: "+ methodName);}catch(InvocationTargetException e){System.err.println("方法执行异常: "+ e.getCause());}catch(Exception e){System.err.println("未知异常: "+ e.getMessage());}returnnull;}}常见异常:
| 异常 | 说明 | 解决方案 |
|---|---|---|
ClassNotFoundException | 类不存在 | 检查类名和类路径 |
NoSuchMethodException | 方法不存在 | 检查方法名和参数类型 |
NoSuchFieldException | 字段不存在 | 检查字段名 |
IllegalAccessException | 无法访问 | 使用 setAccessible(true) |
InvocationTargetException | 方法执行异常 | 获取 getCause() 查看真实异常 |
InstantiationException | 无法实例化 | 确保有无参构造方法 |
8.3 安全性考虑
publicclassReflectionSecurity{publicstaticvoidmain(String[] args){// 1. 检查SecurityManagerSecurityManager sm =System.getSecurityManager();if(sm !=null){try{ sm.checkPermission(newReflectPermission("suppressAccessChecks"));}catch(SecurityException e){System.err.println("没有反射权限");return;}}// 2. 谨慎使用setAccessibletry{Field field =SomeClass.class.getDeclaredField("privateField");// ⚠️ 破坏封装性,谨慎使用 field.setAccessible(true);}catch(Exception e){ e.printStackTrace();}// 3. 验证输入String className =getUserInput();if(!isValidClassName(className)){thrownewIllegalArgumentException("非法类名");}}privatestaticbooleanisValidClassName(String className){// 白名单验证return className.matches("^[a-zA-Z0-9.]+$");}privatestaticStringgetUserInput(){return"com.example.MyClass";}staticclassSomeClass{privateString privateField;}}安全建议:
- 不要反射用户输入的类名(可能导致代码注入)
- 谨慎使用
setAccessible(true) - 在生产环境考虑禁用反射
- 使用白名单验证类名和方法名
- 注意 SecurityManager 的限制
8.4 线程安全
publicclassReflectionThreadSafety{// ✅ 线程安全:使用ConcurrentHashMapprivatestaticfinalConcurrentHashMap<String,Method> cache =newConcurrentHashMap<>();// ❌ 不安全:HashMap在多线程下可能出问题// private static final HashMap<String, Method> cache = new HashMap<>();publicstaticMethodgetMethod(Class<?> clazz,String methodName)throwsNoSuchMethodException{String key = clazz.getName()+"#"+ methodName;return cache.computeIfAbsent(key, k ->{try{return clazz.getMethod(methodName);}catch(NoSuchMethodException e){thrownewRuntimeException(e);}});}}8.5 常见陷阱
publicclassReflectionPitfalls{publicstaticvoidmain(String[] args)throwsException{// 陷阱1:基本类型 vs 包装类型Method method1 =MyClass.class.getMethod("method",int.class);// ✅ 正确// Method method2 = MyClass.class.getMethod("method", Integer.class); // ❌ 错误// 陷阱2:数组类型Method arrayMethod =MyClass.class.getMethod("arrayMethod",int[].class);// ✅ 正确// Method arrayMethod2 = MyClass.class.getMethod("arrayMethod", int.class); // ❌ 错误// 陷阱3:可变参数Method varargMethod =MyClass.class.getMethod("varargMethod",String[].class);// ✅ 正确// 陷阱4:私有构造方法Constructor<MyClass> constructor =MyClass.class.getDeclaredConstructor(); constructor.setAccessible(true);// 必须设置MyClass obj = constructor.newInstance();// 陷阱5:静态字段/方法Field staticField =MyClass.class.getDeclaredField("staticField"); staticField.set(null,"new value");// 静态字段传nullMethod staticMethod =MyClass.class.getMethod("staticMethod"); staticMethod.invoke(null);// 静态方法传null}staticclassMyClass{privatestaticString staticField ="old value";privateMyClass(){}publicvoidmethod(int param){}publicvoidarrayMethod(int[] array){}publicvoidvarargMethod(String... args){}publicstaticvoidstaticMethod(){}}}九、快速参考
反射核心类
| 类 | 说明 | 主要方法 |
|---|---|---|
Class | 类的字节码对象 | forName(), newInstance(), getMethod() |
Constructor | 构造方法 | newInstance(), setAccessible() |
Field | 字段 | get(), set(), setAccessible() |
Method | 方法 | invoke(), setAccessible() |
Modifier | 修饰符工具类 | isPublic(), isStatic(), isFinal() |
常用方法对比
| 方法 | 说明 | 访问范围 |
|---|---|---|
getXxx() | 获取public成员 | public(含继承) |
getDeclaredXxx() | 获取所有成员 | 所有(不含继承) |
获取Class对象的三种方式
// 方式1:类名.class(编译时已知)Class<String> clazz1 =String.class;// 方式2:对象.getClass()(运行时获取)String str ="Hello";Class<?extendsString> clazz2 = str.getClass();// 方式3:Class.forName()(动态加载)Class<?> clazz3 =Class.forName("java.lang.String");反射操作速查表
| 操作 | 代码示例 |
|---|---|
| 创建对象 | clazz.getDeclaredConstructor().newInstance() |
| 获取字段值 | field.get(obj) |
| 设置字段值 | field.set(obj, value) |
| 调用方法 | method.invoke(obj, args) |
| 访问私有成员 | member.setAccessible(true) |
| 判断类型 | clazz.isAssignableFrom(subClass) |
| 获取注解 | clazz.getAnnotation(Ann.class) |
| 获取泛型 | field.getGenericType() |
性能对比(参考值)
| 操作方式 | 相对性能 | 适用场景 |
|---|---|---|
| 直接调用 | 1x(基准) | 常规开发 |
| MethodHandle | 10x | 需要动态调用且关注性能 |
| 反射(有缓存) | 50x | 框架开发 |
| 反射(无缓存) | 100x | 避免使用 |
最佳实践清单
✅ 推荐做法:
- 仅在必要时使用反射(框架、工具类)
- 缓存 Class、Method、Field 对象
- 提前调用
setAccessible(true) - 使用 try-catch 处理所有可能的异常
- 考虑使用 MethodHandle 替代反射
- 在性能敏感场景避免使用反射
- 使用白名单验证类名和方法名
- 充分的代码注释和文档
❌ 避免做法:
- 不要在循环中频繁使用反射
- 不要反射用户输入的类名(安全风险)
- 不要忽略异常处理
- 不要在性能关键路径使用反射
- 不要过度使用
setAccessible(true) - 不要在多线程环境使用非线程安全的缓存
十、总结
本文档全面介绍了 Java 反射机制的核心知识:
1. 反射概述
- 反射的定义和核心功能
- 应用场景(框架开发、动态代理、注解处理)
- 优缺点分析
2. 获取Class对象
- 三种获取方式及使用场景
- 获取类信息(名称、包、父类、接口、修饰符)
- 类型判断和继承关系检查
- 注解获取
3. 构造方法反射
- 获取构造方法(public和private)
- 创建对象的多种方式
- 访问私有构造方法
4. 字段反射
- 获取字段信息
- 访问和修改字段值
- 处理静态字段
5. 方法反射
- 获取方法信息
- 调用方法(public、private、static)
- 方法参数和返回值处理
6. 实战案例
- 通用对象打印器
- 简单依赖注入
- 通用对象复制
- 动态代理
- 配置文件加载
7. 高级应用
- 泛型信息获取
- 数组操作
- 性能优化(缓存机制)
- MethodHandle(性能更好的替代方案)
- 简单ORM框架
- Bean拷贝工具
8. 最佳实践
- 性能优化建议
- 异常处理
- 安全性考虑
- 线程安全
- 常见陷阱
关键要点
| 主题 | 核心内容 |
|---|---|
| 获取Class | 三种方式:类名.class、对象.getClass()、Class.forName() |
| 访问控制 | 使用 setAccessible(true) 访问私有成员 |
| 性能优化 | 缓存反射对象,考虑使用 MethodHandle |
| 异常处理 | 处理多种反射异常,获取真实异常原因 |
| 安全性 | 验证输入,谨慎使用 setAccessible |
| 线程安全 | 使用 ConcurrentHashMap 缓存 |
使用场景
| 场景 | 是否使用反射 | 说明 |
|---|---|---|
| 框架开发 | ✅ 推荐 | Spring、Hibernate等 |
| 动态代理 | ✅ 推荐 | AOP实现 |
| 注解处理 | ✅ 推荐 | 运行时处理注解 |
| 插件系统 | ✅ 推荐 | 动态加载类 |
| 序列化 | ✅ 推荐 | JSON、XML转换 |
| 常规业务 | ❌ 不推荐 | 性能差,可读性差 |
| 性能敏感 | ❌ 不推荐 | 使用直接调用 |
学习路径
- 入门阶段:掌握Class对象获取、基本的字段和方法操作
- 进阶阶段:理解构造方法、注解、泛型信息获取
- 高级阶段:掌握性能优化、MethodHandle、动态代理
- 实战阶段:实现简单框架(依赖注入、ORM)
推荐资源
- Java官方文档 - Reflection API
- 《Java核心技术》卷1 - 第5章
- 《Effective Java》- 第65条:优先考虑接口而不是反射
- 《深入理解Java虚拟机》- 第7章
下一步
- 深入学习动态代理(JDK动态代理、CGLIB)
- 了解字节码操作(ASM、Javassist、ByteBuddy)
- 学习注解处理器(APT)
- 研究主流框架的反射应用(Spring、MyBatis)
- 实践:开发一个简单的依赖注入容器
记住:反射是一把双刃剑,强大但有代价。在追求灵活性的同时,要权衡性能、安全性和代码可维护性。只在真正需要的地方使用反射,并遵循最佳实践。