Java——类加载器,反射
类加载器
反射
获取字节码对象的3种方式中的第一个就是传说中的反射。
1、基本使用
public void func() throws Exception{ // 1.获取字节码对象 Class clazz = Class.forName("cn.x5456.domain.Person"); // 2.获取类中的构造方法 // a.返回一个构造方法数组 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); // public cn.x5456.domain.Person(...) } // b.获取指定的构造方法 Constructor constructor = clazz.getConstructor(String.class,int.class); // public cn.x5456.domain.Person(java.lang.String,int) System.out.println(constructor); // 3.运行构造方法,创建Person类的对象 Object obj = constructor.newInstance("计震宇", 22); System.out.println(obj); // cn.x5456.domain.Person [name=计震宇, age=22] }
2、直接使用空参构造,直接建立对象
/* 有前提: 被反射的类,必须具有空参数构造方法 构造方法权限必须public */ public void func1() throws Exception{ // 1.获取字节码对象 Class clazz = Class.forName("cn.x5456.domain.Person"); // 2.直接获取空参构造,执行创建Person类对象 Object obj = clazz.newInstance(); System.out.println(obj); // cn.x5456.domain.Person [name=null, age=0] }
3、获取私有(加上Declared字段)构造
/* 反射获取私有的构造方法运行 不推荐,破坏了程序的封装性,安全性 暴力反射 */ public void func2() throws Exception{ // 1.获取字节码对象 Class clazz = Class.forName("cn.x5456.domain.Person"); // 2.获取私有构造 // 获取所有的构造方法(包括私有) Constructor[] allCons = clazz.getDeclaredConstructors(); // 获取指定(私有)构造 Constructor constructor = clazz.getDeclaredConstructor(int.class,String.class); System.out.println(constructor); // 3.设置忽略Java的 字段 检查 constructor.setAccessible(true); // 4.执行私有方法 Object obj = constructor.newInstance(18, "计震宇"); System.out.println(obj); }
4、获取/设置类内字段的值
/* * 反射获取成员变量,并修改值 * Person类中的成员String name */ public void func() throws Exception { // 1.获取字节码对象 Class clazz = Class.forName("cn.x5456.demo.Person"); // 2.获取字节码对象内的(公有)成员变量 // a.获取所有 Field[] fields = clazz.getFields(); System.out.println(Arrays.toString(fields)); //[public java.lang.String cn.x5456.demo.Person.name] // b.获取指定字段的值 Field field = clazz.getField("name"); System.out.println(field); // public java.lang.String cn.x5456.demo.Person.name // 3.设置字节码对象内字段的值 // Object obj 必须有对象的支持, Object value 修改后的值 Object obj = clazz.newInstance(); field.set(obj,"计宝宝"); }
5、获取类内方法并执行
public void func1() throws Exception { // 1.获取字节码对象 Class clazz = Class.forName("cn.x5456.demo.Person"); // 2.获取字节码对象内方法 // 获取所有class文件中的所有公共成员方法,包括继承的 Method[] methods = clazz.getMethods(); System.out.println(Arrays.toString(methods)); // 获取指定的方法eat,Method getMethod(String methodName,Class...c) Method method = clazz.getMethod("sleep", String.class, int.class, double.class); // 3.执行方法,需要obj Object obj = clazz.newInstance(); method.invoke(obj,"计宝宝",1,123.456); }
6、跳过集合的泛型
/* * 定义集合类,泛型String * 要求向集合中添加Integer类型 * * 反射方式,获取出集合ArrayList类的class文件对象 * 通过class文件对象,调用add方法 * * 反射是修改字节码对象,和编译过程没有关系 * Java中泛型是伪泛型,只是编译器在编译过程中会检查 * 编译成.class文件后,就没有泛型这个概念了 */ public void func2() throws Exception{ List<String> list = new ArrayList<>(); list.add("计宝宝"); // 1.获取当前类的字节码(类)对象 Class clazz = list.getClass(); // 2.获取字节码(类)对象的add方法 Method method = clazz.getMethod("add", Object.class); // 3.执行add方法 method.invoke(list,1); method.invoke(list,2); method.invoke(list,3); System.out.println(list); //[计宝宝, 1, 2, 3] }
反射的作用——注册JDBC驱动
面向接口编程
最大的作用:解耦(少修改Java代码,多修改配置文件)
配置文件+反射+接口 来实现
使用反射实现excel2entity的工具类
import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * excel导入数据库工作类 * * 参数:类.class,input流 * 返回值:一个列表,中是类.class类型,需要自己调用dao层的save方法 * @author x5456 */ public class ExcelUtils { public static <T> List<T> Xls2Entity(Class<T> c, String filePath) throws Exception { return parseExcel(c,filePath); } private static <T> List<T> parseExcel(Class<T> c,String filePath) throws Exception{ ArrayList<T> ts = new ArrayList<>(); // 获取文件后缀名 String ext = filePath.substring(filePath.lastIndexOf(".") + 1); Sheet sheet; if (ext.equals("xlsx")){ XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(new File(filePath))); //读取文件中第一个Sheet标签页 sheet = workbook.getSheetAt(0); }else{ HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(new File(filePath))); //读取文件中第一个Sheet标签页 sheet = workbook.getSheetAt(0); } // 获取实体类中的所有字段 Field[] fields = c.getDeclaredFields(); List<String> fieldNameList = new ArrayList<>(); for (Field field : fields) { String name = field.getName(); fieldNameList.add(name); } //遍历标签页中所有的行 for (Row row : sheet) { // 第一行(标题),不导入数据库 int rowNum = row.getRowNum(); if (rowNum == 0) { continue; } // 创建一个实体对象 T t = c.newInstance(); // 一定要重新获取这个对象的字节码对象 // revise:上面的注释是错的,我们需要的是new一个新的对象,但是这个类的class对象都是相同的 Class clazz = t.getClass(); // 循环当前行的每列 for (int i = 0; i < row.getPhysicalNumberOfCells(); i++) { // // 第一列是id,选择主键自增,可以注释掉 // if (i == 0){ // continue; // } String fieldName = fieldNameList.get(i); // 通过字段名获取字段 Field declaredField = clazz.getDeclaredField(fieldName); // 获取字段类型的字节码 Class parameterType = declaredField.getType(); Cell cell = row.getCell(i); cell.setCellType(Cell.CELL_TYPE_STRING); // 将cell设置为string类型 Object value = cell.getStringCellValue(); // 对字段类型进行判断,进行转换 if(StringUtils.isNotBlank((String)value)){ if (parameterType.equals(Double.class)){ value = Double.valueOf((String) value); }else if(parameterType.equals(Integer.class)){ // 基线导出的excel的坑 double v = Double.valueOf((String) value); value = (int) v; } } else { value = null; } // 忽略java的private字段检查 declaredField.setAccessible(true); // 调用相应字段的set方法 declaredField.set(t,value); } ts.add(t); } return ts; } }
优化版:建议把上面的看懂,虽然上面写复杂(错:请看revise部分注释)了,但是使用到的知识还是挺重要的。
import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * excel导入数据库工作类 * * 参数:类.class,input流 * 返回值:一个列表,中是类.class类型,需要自己调用dao层的save方法 * @author x5456 */ public class ExcelUtils222 { public static <T> List<T> Xls2Entity(Class<T> c, String filePath) throws Exception { return parseExcel(c,filePath); } private static <T> List<T> parseExcel(Class<T> c,String filePath) throws Exception{ ArrayList<T> ts = new ArrayList<>(); // 获取文件后缀名 String ext = filePath.substring(filePath.lastIndexOf(".") + 1); Sheet sheet; if (ext.equals("xlsx")){ XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(new File(filePath))); //读取文件中第一个Sheet标签页 sheet = workbook.getSheetAt(0); }else{ HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(new File(filePath))); //读取文件中第一个Sheet标签页 sheet = workbook.getSheetAt(0); } // 获取实体类中的所有字段 Field[] fields = c.getDeclaredFields(); //遍历标签页中所有的行 for (Row row : sheet) { // 第一行(标题),不导入数据库 int rowNum = row.getRowNum(); if (rowNum == 0) { continue; } // 创建一个实体对象 T t = c.newInstance(); // 循环当前行的每列 for (int i = 0; i < fields.length; i++) { Field declaredField = fields[i]; // 获取字段类型的字节码 Class parameterType = declaredField.getType(); Cell cell = row.getCell(i); cell.setCellType(Cell.CELL_TYPE_STRING); // 将cell设置为string类型 Object value = cell.getStringCellValue(); // 对字段类型进行判断,进行转换 if(StringUtils.isNotBlank((String)value)){ if (parameterType.equals(Double.class)){ value = Double.valueOf((String) value); }else if(parameterType.equals(Integer.class)){ // 基线导出的excel的坑 double v = Double.valueOf((String) value); value = (int) v; } } else { value = null; } // 忽略java的private字段检查 declaredField.setAccessible(true); // 调用相应字段的set方法 declaredField.set(t,value); } ts.add(t); } return ts; } }