zoukankan      html  css  js  c++  java
  • 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;
    
        }
    
    
    }
    

      

  • 相关阅读:
    参考阿里规范,优秀的 Java 项目代码该如何分层?
    SpringBoot 中实现跨域的5种方式
    美团一面:你既然写过Mybatis插件,说说它底层是怎么加载一个自定义插件的
    陌陌面试官:说说Spring AOP 的原理、SpringMVC 的处理过程?
    这16条规范代码,同事,拍桌子 大喊 “666”
    微服务很简单,用一张架构图了解一下
    K8S部署Metrics-Server服务
    cookie
    html标签默认样式整理
    html 语义化标签
  • 原文地址:https://www.cnblogs.com/x54256/p/8486146.html
Copyright © 2011-2022 走看看