Java 反射API的简介及使用

Java提供了反射技术,使我们可以通过类的全名创建对象,利用反射机制,我们可以在运行时再确定对象类型,从而对不同对象进行分别处理,多少无意,看代码

Class对象的获取
通常jvm在程序运行时会为每个类自动加载一个Class对象,通常我们创建对象和对对象里的函数进行使用或操作时都是JVM通过这个Class对象来完成我们对一个类或对象的相应操作,而一般有三种方式获取该Class对象

//Emp为一个员工数据表封装类
public class CreateClassObject {
    public static void main(String[] args) throws Exception {
        //1、通过class.forname()传入完整类名来获取Class对象
//        Class clazz = Class.forName("com.mypackage.entity.Emp");
        //2、通过类名.class来获取
//        Class<Emp> clazz = Emp.class;
        //3、通过对象的getClass()来获取
        Class clazz = new Emp().getClass();
        System.out.println(clazz.getPackage());//获取所在的包
        System.out.println(clazz.getName());//获取完整类名
        System.out.println(clazz.getSimpleName());//获取简单类名
        System.out.println(clazz.getCanonicalName());//获取符合java规范的类名

        //4、如果是一个基本数据类型,那么可以通过Type的方式来获取Class对象
        Class type = Integer[].TYPE;
        //getName和getCanonicalName
        System.out.println(type.getName());
        System.out.println(type.getCanonicalName());
    }
}

Class类相应API
首先创建两个简单的类以便于查看Class的API相应效果

Person类

package com.mypackage.reflect;

//Person类
public class Person {
    public String name;
    public int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String show(){
        return "name:"+name+", age :"+age;
    }
}

Student类 继承了Person,和实现了一堆getter和setter

package com.mypackage.reflect;

public class Student extends Person{
    public String className;
    private String address;

    public Student(){
        super();
    }

    private Student(String name,int age,String className){
        super(name,age);
        this.className= className;
    }
    public Student(String name,int age,String className,String address){
        super(name,age);
        this.className = className;
        this.address = address;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    private void add(int a,int b){
        System.out.println(a+b);
    }

    @Override
    public String toString() {
        return "Student{" +
                "className='" + className + '\'' +
                ", address='" + address + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Class类API

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

public class ClassAPI {
    public static void main(String[] args) throws Exception{
        Class<?> clazz = Class.forName("com.mypackage.reflect.Student");
        //获取成员变量,包括子类及父类,同时只包含public修饰的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
            System.out.println(field.getName());//获取字段名
            System.out.println(field.getType());//获取字段类型
            System.out.println(field.getModifiers());//获取访问修饰符类型,1为public
            System.out.println("-----");
        }
        System.out.println("=======================");
        
        //此方法返回的是当前类的所有属性,不仅仅局限于公共访问修饰符,所有的访问修饰符都可以拿到
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
        }
        System.out.println("========================");
        
        //反射在一定程度上破坏了封装性,需要合理使用
        Field address = clazz.getDeclaredField("address");
        //私有属性需设置该属性是否能被访问,true表示能被访问,破坏了封装性
        address.setAccessible(true);
        System.out.println(address.getName());
        //通过Class对象clazz的newInstance()方法创建对象
        Object o = clazz.newInstance();
        //更改对象o的成员变量address的值
        address.set(o,"北京市");
        System.out.println(((Student)o).getAddress());
        System.out.println("===============================");
        
        //获取该对象的普通方法,包含的方法范围是当前对象及父类对象的所有公共方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        System.out.println("===============================");
        
        //获取当前类中所有的方法,无论什么访问修饰符
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName());
        }
        System.out.println("===============================");
        //通过传入函数名及参数类型的class获取对应的method
        Method add = clazz.getDeclaredMethod("add", int.class, int.class);
        add.setAccessible(true);
        Object o1 = clazz.newInstance();
		//通过invoke调用add方法本身,但第一个参数需要传入newInstance()创建的对象
        add.invoke(o1,123,123);
        System.out.println("===============================");
        
        //获取对象的所有构造方法,只能获取公有的改造方法
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor.getName());
        }
        System.out.println("===============================");
        
        //获取所有的构造方法,无论是私有还是公有
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor.getName());
        }
        //如何调用私有的构造方法呢?
        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        declaredConstructor.setAccessible(true);
        Student o2 = (Student)declaredConstructor.newInstance("lisi", 23, "java");
        System.out.println(o2);
    }
}

反射的一个应用实例
反射有上面作用呢?
java JDBC使用简易教程中如果要对一个对象进行持久化,往数据库插入一条数据时(一般会把每条数据封装成一个对象,不同的数据表封装成不同对象),不同对象还需要另外实现各自的DAO(Data Access Object),即需要为每个封装的数据对象分别写不同的数据持久化类,实现对数据的增删改查操作,而利用反射技术,我们可以简化DAO的编写,实现一个类能为所有表的数据进行增删改查操作

package com.mypackage.reflect;

import com.mypackage.entity.Dept;
import com.mypackage.entity.Emp;
import com.mypackage.util.DBUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*
* 要查询N张表的数据,但是不想写N多的方法,能否写一个方法完成所有表的查询工作
*
* */
public class BaseDaoImpl {
    /**
     * 统一的查询表的方法
     * @param sql       不同的sql语句
     * @param params    sql语句的参数
     * @param clazz        sql语句查询返回的对象
     * @return
     */
    public List getRows(String sql,Object[] params,Class clazz){
        List list = new ArrayList();
        Connection connection = null;
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;

        try {
            //建立连接,DBUtil封装了创建连接的操作
            connection = DBUtil.getConnection();
            //创建pstmt对象
            pstmt = connection.prepareStatement(sql);
            //给sql语句填充参数
            if(params!=null){
                for(int i = 0;i<params.length;i++){
                    pstmt.setObject(i+1,params[i]);
                }
            }
            //开始执行查询操作,resultset中有返回的结果,需要讲返回的结果放置到不同的对象中
            resultSet = pstmt.executeQuery();
            //获取结果集合的元数据对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //判断查询到的每一行记录中包含多少个列
            int columnCount = metaData.getColumnCount();
            //循环遍历resultset
            while(resultSet.next()){
                //通过clazz反射创建放置具体结果属性的对象
                Object obj = clazz.newInstance();
                for(int i= 0;i<columnCount;i++){
                    //从结果集合中获取单一列的值,从1开始
                    Object objValue = resultSet.getObject(i+1);
                    //获取列的名称,注意数据封装类的成员变量名需要与数据库表中的列名一致
                    //以便于用字符串获取Method对象进而调用相应属性的set方法
                    String columnName = metaData.getColumnName(i+1).toLowerCase();
                    //获取类中的属性
                    Field declaredField = clazz.getDeclaredField(columnName);
                    //获取类中属性对应的set方法
                    Method method = clazz.getMethod(getSetName(columnName),declaredField.getType());
                    if(objValue instanceof Number){//java.sql.Number类型需要对数字类型分别处理
                        Number number = (Number) objValue;
                        //获取成员变量 的类型 的类名
                        String fname = declaredField.getType().getName();
                        //根据成员变量 的类型 的类名 区分是哪种数据类型,以获取相应数值类型的数据并传入
                        if("int".equals(fname)||"java.lang.Integer".equals(fname)){
                            method.invoke(obj,number.intValue());
                        }else if("byte".equals(fname)||"java.lang.Byte".equals(fname)){
                            method.invoke(obj,number.byteValue());
                        }else if("short".equals(fname)||"java.lang.Short".equals(fname)){
                            method.invoke(obj,number.shortValue());
                        }else if("long".equals(fname)||"java.lang.Long".equals(fname)){
                            method.invoke(obj,number.longValue());
                        }else if("float".equals(fname)||"java.lang.Float".equals(fname)){
                            method.invoke(obj,number.floatValue());
                        }else if("double".equals(fname)||"java.lang.Double".equals(fname)){
                            method.invoke(obj,number.doubleValue());
                        }
                    }else{//其他类型直接传入对象本身调用set方法即可
                        method.invoke(obj,objValue);
                    }
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DBUtil.closeConnection(connection,pstmt,resultSet);
        }

        return list;
    }

    public String getSetName(String name){//获取字段名相应的setter函数名
        return "set"+name.substring(0,1).toUpperCase()+name.substring(1);
    }

    public static void main(String[] args) {//测试用main函数
        BaseDaoImpl baseDao = new BaseDaoImpl();
        //注意传入的是Emp数据表封装类的class
        List rows1 = baseDao.getRows("select empno,ename,sal,deptno from emp where deptno =?",
         new Object[]{10}, Emp.class);
        for(Iterator it = rows1.iterator();it.hasNext();){
            Emp emp = (Emp) it.next();
            System.out.println(emp);
        }
        //传入的是Dept数据表封装类的class
        List rows2 = baseDao.getRows("select deptno,dname,loc from dept",
         new Object[]{}, Dept.class);
        for(Iterator it = rows2.iterator();it.hasNext();){
            Dept dept = (Dept) it.next();
            System.out.println(dept);
        }
    }
}

上面的main函数测试了一个类对不同数据表的查询操作,可以看到用反射简化了DAO的编写工作,不再需要为不同的数据表封装不同的DAO了,很多高级框架这中都使用的了反射技术,但根据其他资料了解到,反射技术影响了程序的性能,因此引入了运行时注解,但此篇幅中不再展开

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值