基于自定义toString格式实现对象实例化的技术咨询
实现自定义toString逆向的toObject方法
背景说明
现有自定义toString方法用于生成特定格式的对象序列化字符串:
@NotNull public static String toString(@NotNull Object object, @NotNull Object[] variables, Class<?>... parameters) throws NoSuchMethodException { StringBuilder sb = new StringBuilder(); sb.append(object.getClass().getConstructor(parameters)).append('\n'); sb.append(object.getClass()).append(":"); for (Object variable : variables){ sb.append(" | ").append(variable.toString()); } return sb.toString(); }
该方法生成的字符串示例如下:
public User(java.lang.String,int) class User: | username | 369172
需要实现逆向方法toObject,将上述格式的字符串转换为原对象的新实例,需解决三个核心问题:
- 未知参数/变量数量时,将变量转换为可用于实例化的形式
- 适配对象构造方法为私有的场景
- 处理参数为输出默认
Object.toString()结果的对象
要求:禁止使用Serialization接口
完整实现代码
import org.jetbrains.annotations.NotNull; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ObjectConverter { // 原自定义toString方法 @NotNull public static String toString(@NotNull Object object, @NotNull Object[] variables, Class<?>... parameters) throws NoSuchMethodException { StringBuilder sb = new StringBuilder(); sb.append(object.getClass().getConstructor(parameters)).append('\n'); sb.append(object.getClass()).append(":"); for (Object variable : variables){ sb.append(" | ").append(variable.toString()); } return sb.toString(); } // 实现的逆向toObject方法 @NotNull public static Object toObject(@NotNull String formattedObject, @NotNull Object object, Class<?>... parameters) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { // 拆分格式化字符串为构造方法行和变量行 String[] lines = formattedObject.split("\n"); if (lines.length != 2) { throw new IllegalArgumentException("非法的格式化字符串格式"); } String constructorLine = lines[0]; String variablesLine = lines[1]; // 解析构造方法参数类型(如果未传入parameters则从字符串提取) Constructor<?> constructor; if (parameters == null || parameters.length == 0) { Pattern constructorPattern = Pattern.compile("\\((.*?)\\)"); Matcher matcher = constructorPattern.matcher(constructorLine); if (!matcher.find()) { throw new IllegalArgumentException("无法解析构造方法行"); } String paramTypesStr = matcher.group(1); String[] paramTypeNames = paramTypesStr.isEmpty() ? new String[0] : paramTypesStr.split(","); List<Class<?>> paramTypeList = new ArrayList<>(); for (String typeName : paramTypeNames) { typeName = typeName.trim(); try { paramTypeList.add(Class.forName(typeName)); } catch (ClassNotFoundException e) { // 处理基本类型映射 switch (typeName) { case "int": paramTypeList.add(Integer.TYPE); break; case "long": paramTypeList.add(Long.TYPE); break; case "double": paramTypeList.add(Double.TYPE); break; case "float": paramTypeList.add(Float.TYPE); break; case "boolean": paramTypeList.add(Boolean.TYPE); break; case "byte": paramTypeList.add(Byte.TYPE); break; case "short": paramTypeList.add(Short.TYPE); break; case "char": paramTypeList.add(Character.TYPE); break; default: throw new IllegalArgumentException("无法识别的参数类型:" + typeName); } } } parameters = paramTypeList.toArray(new Class[0]); } // 处理私有构造方法:获取声明的构造方法并开启访问权限 constructor = object.getClass().getDeclaredConstructor(parameters); constructor.setAccessible(true); // 解析变量值并转换为对应类型 String[] variableStrs = variablesLine.split(" \\| "); List<Object> variableValues = new ArrayList<>(); for (int i = 1; i < variableStrs.length; i++) { String varStr = variableStrs[i].trim(); Class<?> paramType = parameters[i-1]; variableValues.add(convertStringToType(varStr, paramType, object.getClass())); } // 实例化并返回对象 return constructor.newInstance(variableValues.toArray()); } /** * 字符串转目标类型,处理默认Object.toString()场景 */ private static Object convertStringToType(String valueStr, Class<?> targetType, Class<?> targetClass) throws NoSuchFieldException, IllegalAccessException { // 处理基本类型及包装类 if (targetType.isPrimitive()) { if (targetType == Integer.TYPE) return Integer.parseInt(valueStr); if (targetType == Long.TYPE) return Long.parseLong(valueStr); if (targetType == Double.TYPE) return Double.parseDouble(valueStr); if (targetType == Float.TYPE) return Float.parseFloat(valueStr); if (targetType == Boolean.TYPE) return Boolean.parseBoolean(valueStr); if (targetType == Byte.TYPE) return Byte.parseByte(valueStr); if (targetType == Short.TYPE) return Short.parseShort(valueStr); if (targetType == Character.TYPE) return valueStr.charAt(0); } else if (targetType == String.class) { return valueStr; } else if (Number.class.isAssignableFrom(targetType)) { if (targetType == Integer.class) return Integer.valueOf(valueStr); if (targetType == Long.class) return Long.valueOf(valueStr); if (targetType == Double.class) return Double.valueOf(valueStr); if (targetType == Float.class) return Float.valueOf(valueStr); if (targetType == Byte.class) return Byte.valueOf(valueStr); if (targetType == Short.class) return Short.valueOf(valueStr); } else if (Boolean.class == targetType) { return Boolean.valueOf(valueStr); } else { // 匹配默认Object.toString()格式:类名@十六进制哈希码 Pattern defaultToStringPattern = Pattern.compile("(.*?)@[0-9a-fA-F]+"); Matcher matcher = defaultToStringPattern.matcher(valueStr); if (matcher.matches()) { // 优先尝试无参构造实例化 try { Constructor<?> noArgsConstructor = targetType.getDeclaredConstructor(); noArgsConstructor.setAccessible(true); return noArgsConstructor.newInstance(); } catch (NoSuchMethodException e) { // 无参构造不存在时,尝试获取静态单例字段(如INSTANCE) try { Field instanceField = targetType.getDeclaredField("INSTANCE"); instanceField.setAccessible(true); return instanceField.get(null); } catch (NoSuchFieldException | InvocationTargetException | InstantiationException ex) { throw new IllegalArgumentException("无法解析默认toString格式的对象:" + valueStr); } } catch (InvocationTargetException | InstantiationException ex) { throw new RuntimeException("实例化默认toString对象失败", ex); } } else { throw new IllegalArgumentException("不支持的参数类型转换:" + targetType.getName()); } } throw new IllegalArgumentException("无法转换字符串为指定类型:" + targetType.getName()); } }
核心问题解决细节
1. 未知参数/变量数量处理
- 通过正则表达式从构造方法行中提取参数类型全限定名,转换为
Class对象 - 拆分变量行内容,按顺序匹配参数类型,调用转换方法将字符串转为对应实例
- 使用动态列表存储参数类型和变量值,适配任意数量的输入参数
2. 私有构造方法适配
- 使用
getDeclaredConstructor获取类中所有声明的构造方法(包括私有) - 调用
constructor.setAccessible(true)开启反射访问权限,允许调用私有构造方法实例化对象
3. 默认Object.toString()结果处理
- 正则匹配
类名@十六进制哈希码格式,识别默认toString输出 - 优先尝试通过无参构造方法实例化(同样开启私有访问权限)
- 无参构造不存在时,尝试获取常见静态单例字段(如
INSTANCE) - 若以上方式均不支持,抛出明确的异常提示
内容的提问来源于stack exchange,提问作者Alexandrite2000
相关产品推荐
相关产品推荐

