反射案例

反射案例

1. 目标

1. 字符串数据解析 ==> JavaBean 规范对象 ==> 存储到 ArrayList 集合中
2. 存储 JavaBean 规范类对象中的 ArrayList 数据 ==> 字符串

2. 字符串内容

className=com.qfedu.entity.Student;[id=1,name=王嚣张,age=99,gender=男,score=0.5];[id=2,name=王周董,age=16,gender=男,score=5.5];[id=3,name=王乾,age=9,gender=男,score=9.5];[id=4,name=王强哥,age=25,gender=男,score=99.5];
1. 根据字符串信息内容分析 JavaBean 规范对象成员变量数据类型选择。
package com.qfedu.entity;

class Student {
    private int id;
    private String name;
    private int age;
    private char gender;
    private float score;
    
    // 根据所需完成对应的 Setter 和 Getter 以及 Constructor 方法
}
2. [id=1,name=王嚣张,age=99,gender=男,score=0.5] ==> JavaBean 规范 Student 对象

得到哪些东西???需要哪些技术???
	1. String 字符串切割 substring
	2. 类型转换. String ==> int char float
		例如:	
			"id=1" ==> split("=") ==> String[] {"id", "1"}
			"id" ==> Student 类内成员变量名称为 id 的 Field 对象 【反射】
			"1" ==> id Field 对象所需的【int】类型
		String ==> 转换为其他数据类型 基本数据类型包装类

3. 字符串转基本类型方法

package com.qfedu.demo;

/*
 * 字符串转其他类型相关方法
 */
public class Demo1 {
	public static void main(String[] args) {
		/*
		 String => byte short int long
		 String => float
		 String => double
		 String => char
		 String => boolean
		 
		 包装类:
		 	Java 把基本数据类型设定了一个符合 Java 万物皆对象的思想 完成对应基本数据类型的包装类。
		 	byte   ==> Byte
		 	short  ==> Short
		 	int    ==> Integer
		 	long   ==> Long
		 	float  ==> Float
		 	double ==> Double
		 	char   ==> Character
		 	boolean ==> Boolean
		 	
		 	操作使用和基本数据类型操作一致,多辅助了一些相关的方法 
		 	String 解析对应数据方法
		 */
		String str1 = "100";
		int i = Integer.parseInt(str1);
		System.out.println(i);
		/*
		 * Integer 
		 * static int parseInt(String)
		 * 
		 * Short Byte Long 都有类似的方法
		 * static short parseShort(String) 
		 * static byte parseByte(String) 
		 * static long parseLong(String) 
		 */
		
		String str2 = "99.5";
		float f = Float.parseFloat(str2);
		double d = Double.parseDouble(str2);
		System.out.println(f);
		System.out.println(d);
		/*
		 * Float Double 
		 * static float parseFloat(String) 
		 * static double parseDouble(String)
		 */
		
		/*
		 * Character 
		 * String 类方法
		 * 		char charAt(0);
		 */
		String str3 = "true";
		boolean b = Boolean.parseBoolean(str3);
		
		System.out.println(b);
		/*
		 * Boolean
		 * static boolean parseBoolean(String) 
		 */
		
		/*
		 * String 转基本数据类型操作方法
		 * Byte Short Long Float Double Boolean
		 * static xxx parseXXXX(String)
		 * 
		 * Integer
		 * static int parseInt(String);
		 * 
		 * Character
		 * 		char charAt(0);
		 */
		
	}
}

4. 反射反思

1. Class 对象获取 ==> 
	.class字节码文件在内存【方法区】对应内存空间。==> 
	.class文件对应 Java文件 ==> 
	对应 Java程序或者对应数据类型。

2. 通过 Class 对象 获取成员变量 Field 对象。
	a. Field 对象是否可以获取【成员变量名称】???
		获取【成员变量名称】 方法 是什么?
			Field 类内方法 String getName(); 获取当前成员变量名称。
			
	b. Field 对象是否可以获取【成员变量数据类型】???
		获取【成员变量数据类型】 方法 是什么?
			Field 类内方法 Class getType();  获取当前成员变量数据类型 Class

3. 通过 Class 对象 获取成员变量 Method 对象。
	String getName();
		Method 类内方法 获取当前成员方法名称
	Class[] getParameterTypes();
		Method 类内方法 获取当前成员方法形式参数列表数据类型 Class 数组
	Class getReturnType();
		Method 类内方法 获取当前成员方法返回值数据类型 Class 对象
	例如:
		public int test(int n1, int n2);
		Class cls;
		Method m1 = cls.getMethod("test", int.class, int.class);
		m1.getName(); ==> "test"
		m1.getParameterTypes() ==> Class[] types = {int.class, int.class}
		m1.getReturnType(); ==> Class returnType = int.class;

5. 字符串转JavaBean规范对象

5.1 Class 对象获取
className=com.qfedu.entity.Student;[id=1,name=a,age=99,gender=男,score=0.5];[id=2,name=c,age=16,gender=男,score=5.5];[id=3,name=d,age=9,gender=男,score=9.5];[id=4,name=e,age=25,gender=男,score=99.5];
className=com.qfedu.entity.Student
包含对应数据类型的完整的包名.类名 ==> 对应的 Class 对象 【为所欲为】
className=com.qfedu.entity.Student;[id=1,name=王嚣张,age=99,gender=男,score=0.5];[id=2,name=周董,age=16,gender=男,score=5.5];[id=3,name=王乾,age=9,gender=男,score=9.5];[id=4,name=强哥,age=25,gender=男,score=99.5];

==> 执行 split(";") ==> String[]

String[] arr = {
	"className=com.qfedu.entity.Student", // 想要获取的数据信息
	"[id=1,name=a,age=99,gender=男,score=0.5]",
	"[id=2,name=b,age=16,gender=男,score=5.5]",
	"[id=3,name=c,age=9,gender=男,score=9.5]", 
	"[id=4,name=d,age=25,gender=男,score=99.5]"
}

String className = arr[0].substring(arr[0].indexOf("=") + 1);

Class Class.forName(className); ==> 得到对应的 Class 对象
5.2 解析信息字符串
String[] arr = {
	"className=com.qfedu.entity.Student", // 包名.类名
	"[id=1,name=a,age=99,gender=男,score=0.5]",
	"[id=2,name=b,age=16,gender=男,score=5.5]",
	"[id=3,name=c,age=9,gender=男,score=9.5]", 
	"[id=4,name=d,age=25,gender=男,score=99.5]"
}

arr[1] = "[id=1,name=王嚣张,age=99,gender=男,score=0.5]"

String infoStr = arr[1].substring(arr[1].indexOf('[') + 1 , arr[1].lastIndexOf(']'));
infoStr ==> "id=1,name=王嚣张,age=99,gender=男,score=0.5";
String[] infoArr = infoStr.split(",");
infoArr = { // 键值对模型数组 成员变量=数据
	"id=1",
	"name=a",
	"age=99",
	"gender=男",
	"score=0.5"
};

重点信息:
	1. 成员名称
	2. 成员变量对应数据字符串信息
int index = infoArr[0].indexOf('=');
String filedName = infoArr[0].substring(0, index);
String value = infoArr[0].substring(index + 1);
5.3 BeanUtils工具类 setProperty 方法实现
package com.qfedu.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 符合 JavaBean 规范类对象操作工具类
 */
public class BeanUtils {
	/**
	 * 给予符合 JavaBean 规范类对象,根据成员变量名 fieldName,给予对应成员变量数据赋值操作, 数据对应类型为String
	 * 类型,要求在方法中,转换其类型为成员变量对应类型
	 * 
	 * @param bean      符合 JavaBean 规范类对象
	 * @param fieldName 指定成员变量名称
	 * @param value     对应当前成员变量赋值使用数据,String 类型
	 * @throws SecurityException         安全异常
	 * @throws NoSuchFieldException      没有对应成员变量异常
	 * @throws NoSuchMethodException     没有对应成员方法异常
	 * @throws InvocationTargetException 执行目标异常
	 * @throws IllegalArgumentException  非法参数异常
	 * @throws IllegalAccessException    非法权限异常
	 */
	public static void setProperty(Object bean, String fieldName, String value)
			throws NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		// 1. 通过 bean 获取对应 Class 对象
		Class<?> cls = bean.getClass();

		// 2. 获取成员变量 Field 类对象,因为 JavaBean 规范成员变量都是 private 私有化修饰,需要【暴力反射】
		Field field = cls.getDeclaredField(fieldName);
		// 3. JavaBean 规范成员变量都是 private 修饰,如果不给予操作权限,无法执行其他任何方法。操作之前,给予对应使用权限
		field.setAccessible(true);

		// 4. 获取成员变量数据类型,才可以指定 value 字符串数据对应的目标数据类型是什么
		Class<?> type = field.getType();

		/*
		 * 1. Byte Short Long Double Float Boolean ==> parse数据类型名(String)
		 * 
		 * 2. Integer ==> parseInt(String)
		 * 
		 * 3. Character ==> charAt(0);
		 * 
		 * 4. 字符串 直接用
		 * 
		 * 最终执行的方法: field.set(bean, parseValue); tips: parseValue 是 value
		 * 根据当前成员变量数据类型转换之后的结果。
		 */
		// 分支结构走起!!!
		// parseValue 用于最终赋值成员变量使用的数据,采用 Object 类型,方便后期临时存储转换之后的数据结果
		Object parseValue = null;

		if (type == String.class) {
			// 成员变量数据类型为 String 类型
			parseValue = value;
		} else if (type == char.class || type == Character.class) {
			// 成员变量数据类型为 char or Character 类型。一定要判断 char or Character
			parseValue = value.charAt(0);
		} else if (type == int.class || type == Integer.class) {
			// 成员变量数据类型为 int or Integer 类型。
			parseValue = Integer.parseInt(value);
		} else {
			/*
			 * 剩下以下类型需要转换 1. Byte Short Long Double Float Boolean ==> parse数据类型名(String)
			 */
			/*
			 * 获取数据类型名称 type ==> Float 类型 name = java.lang.Float
			 */
			String name = type.getName();
			/*
			 * substring 操作之后 name = Float
			 */
			name = name.substring(name.lastIndexOf('.') + 1);

			/*
			 * 拼接 parseXXX 方法名称 parseFloat 方法名
			 */
			String parseMethodName = "parse" + name;

			/*
			 * 通过当前成员变量数据类型获取对应的 parseXXX 解析字符串方法 得到 parseFloat 方法 public static float
			 * java.lang.Float.parseFloat(java.lang.String)
			 * 
			 */
			Method parseMethod = type.getMethod(parseMethodName, String.class);

			/*
			 * 通过该 Method 对象执行 invoke 方法 因为方法为 static 方法,第一个执行方法对象可以 null
			 */
			parseValue = parseMethod.invoke(null, value);
		}

		field.set(bean, parseValue);
	}
}

posted @ 2022-05-15 23:58  qtyanan  阅读(28)  评论(0编辑  收藏  举报