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;

    }


}

  

原文地址:https://www.cnblogs.com/x54256/p/8486146.html