类加载器
反射
获取字节码对象的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;
}
}