Java反射

1. 反射概述

1.1 反射是什么

反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

Oracle 官方对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

简而言之,通过反射,我们可以在运行时获得程序或程序中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

1.2 反射的作用

  • 运行时判断任意一个对象所属的类;

  • 运行时创建任意一个类的对象;

  • 运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);

  • 运行时调用任意一个对象的方法。

  • Class.forName("com.mysql.jdbc.Driver");注册驱动

2. 反射的使用

2.1 获取Class

Class是什么:

Book. Book book = new Book()

  • 当我们在程序中要表达这个实体时,往往会定义一个Book类,当我们在程序中要表达这个实体时,往往会定义一个Car类,总之不同的实体都会定义不同用来表示相关信息,

  • 其实JVM里面所有的本身也是一个实体。每个类都有类名,都有方法名,都有属性名。

  • 那么我们用什么来表示这些信息呢?就是Class类.

  • 为什么需要Class类。如果想在运行时或者编译时拿到类或者对象的信息;那么必须借助Class类。运行时获取类型信息可以让我们写出更灵活更强大的程序,比如实现动态代理,反射等等。

Java的对象模型中:

  1. 所有的类都是Class类的实例,Object是类,那么Object也是Class类的一个实例。

  2. 所有的类都最终继承自Object类,Class是类,那么Class也继承自Object。

首先创建Person类:

package entity;

public class Person {
    private String name;
    public int age;

    public Person() {
    }

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

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    private void playGame(){
        System.out.println("playing……");
    }

}

获取Class的方法有三种:

  1. 通过类,直接获取某个类的class

  2. Class<Person> personClass = Person.class;

  3. 调用某个对象的getClass()方法,getClass( )是Object的方法

  4. Person person = new Person();
    Class<? extends Person> personClass = person.getClass();// 泛型表示Person 或者Person的子类

  5. 调用Class类的forName()静态方法(类的全限定名)

  6. Class<?> personClass = Class.forName("com.codingfuture.entity.Person");

2.2 生成实例对象

  1. 调用Class对象的newInstance()方法

  2. Person person = personClass.newInstance();
    // 编译异常原因,newInstance()是本质上还是去调用类的默认无参构造器,如果类不存在构造器,创建对象会失败

  3. 通过获取类的构造方法进行实例化

  4. 无参构造

  5. Constructor<Person> constructor = personClass.getConstructor();
    Person person = constructor.newInstance();
    // 使用无参构造创建实例,可以不传参数

  6. 有参构造

  7. Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
     Person person = constructor.newInstance("tom", 18);
     // 使用有参构造创建实例,必须传入与构造器类型相同的参数

2.3 访问属性

  1. 访问公有属性

  2. // 获取age属性
    Field ageField = personClass.getField("age");
    // 获取person对象的age属性值
    Object age = ageField.get(person);
    System.out.println(age);
    
    //将person对象的age属性值设置为20
    ageField.set(person,20);
    System.out.println(person.age);

    访问私有属性

  3. // 获取私有的name属性
    Field nameField = personClass.getDeclaredField("name");
    // 将name属性的可访问性设置为true
    nameField.setAccessible(true);
    
    // 将person对象的name属性值设置为jerry
    // 正常写法:person.setName("jerry);
    nameField.set(person,"jerry");
    System.out.println(person.getName());
    
    // 获取person对象的name属性值
    Object name = nameField.get(person);
    System.out.println(name);

2.4 调用方法

  1. 访问公有方法

  2. // 获取getName()方法
    Method getName = personClass.getMethod("getName");//getMethod(方法名,参数列表)
    // 通过person对象调用getName()方法,并获取执行结果
    Object name = getName.invoke(person);
    System.out.println(name);
    
    
    // 获取setName()方法
    Method setName = personClass.getMethod("setName", String.class);
    // 通过person对象调用setName()方法,并传递参数
    setName.invoke(person,"trump");
    System.out.println(person.getName());

    访问私有方法

  3. // 获取私有的playGame()方法
    Method playGame = personClass.getDeclaredMethod("playGame");
    // 将playGame()方法的可访问性设置为true
    playGame.setAccessible(true);
    // 通过person对象调用playGame()方法
    playGame.invoke(person);

3. 反射的应用

3.1 反射封装JDBC框架

3.1.1传统写法:
/**
     * 传统jdbc 写法
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");// 这句话可以省略 驱动自动加载

        Connection connection =
                DriverManager.getConnection("jdbc:mysql:///jdbcDemo", "", "");
        // 如果换一张表  student代码就需要改变,下面的赋值 都需要改变 代码写死了 需要灵活
        //虽然我不知道 未来是那个实体类,可以利用反射 在运行时拿到book表对应实体类的信息
        PreparedStatement ps = connection.prepareStatement("insert into student(id,name) values(?,?)");
        ps.setInt(1, 1);
        ps.setString(2, "张三");
        int i = ps.executeUpdate();
        System.out.println(i > 0 ? "成功" : "失败");
        connection.close();
        //把可变的东西 改为灵活的
    }
//问题:每次需要写sql语句 而且sql语句的表名 每次都要换,参数数量不确定。封装一个insert方法
3.1.2封装AddOne方法

编写BaseDao代码

package com.codingfuture.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author Petrel
 */
public abstract class BaseDao<T> {
    public int add(T t) throws ClassNotFoundException, SQLException, IllegalAccessException {
        // 1。注册驱动,这句话可以省略 驱动自动加载
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection =
                DriverManager.getConnection("jdbc:mysql:///jdbcDemo", "", "");
        // 2。通过泛型类 获取Class 对象
        Class<?> aClass = t.getClass();
        // 3。获取mysql表名
        //todo 表名转换不严谨
        String tableName = aClass.getSimpleName().toLowerCase();
        // System.out.println(tableName);
        // 4。定义sql语句
        // 如果换一张表  student代码就需要改变,下面的赋值 都需要改变 代码写死了 需要灵活
        // 虽然我不知道 未来是那个实体类,可以利用反射 在运行时拿到表对应实体类的信息
        // insert into student(id,name,age) values (?,?,?)
        Field[] fields = aClass.getDeclaredFields();
        StringBuilder columns = new StringBuilder();
        StringBuilder placeHolders = new StringBuilder();
        // 5。利用反射 在运行时拿到表对应实体类的信息
        for (Field field : fields) {
//            System.out.println(field.getName());
            columns.append(field.getName());
            columns.append(",");
            placeHolders.append("?,");
        }
        // 去掉最后一位逗号
        columns.deleteCharAt(columns.length() - 1);
        placeHolders.deleteCharAt(placeHolders.length() - 1);
//        System.out.println(columns);
//        System.out.println(placeHolders);
        // 6。拼接sql语句
        String sql = String.format("insert into %s (%s) values( %s)", tableName, columns, placeHolders);
//        System.out.println(sql);
        PreparedStatement ps = connection.prepareStatement(sql);
//       正常写法 ps.setInt(1, student.getId);
        // 7。?赋值
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            ps.setObject(i + 1, fields[i].get(t));
        }
        // 8。执行sql.
        return ps.executeUpdate();
    }

}
/*
	创建StudentDao 去实现BaseDao
*/
public class StudentDao extends BaseDao<Student> {
}

    /**
     * 测试类
     */
package com.codingfuture;

import com.codingfuture.dao.StudentDao;
import com.codingfuture.entity.Student;

import java.sql.SQLException;

/**
 * @author Petrel
 */
public class TestDemo {
    public static void main(String[] args) throws SQLException, ClassNotFoundException, IllegalAccessException {
//      BaseDao<Student> baseDao = new BaseDao<>();
        StudentDao studentDao = new StudentDao();
        Student student = new Student();
        student.setId(null);
        student.setName("小张");
        student.setAge(20);
//        int i = baseDao.add(student);
        int i = studentDao.add(student);
        System.out.println(i);
    }
}

 表名处理

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>21.0</version>
</dependency>

代码参考

3.1.3封装findAll方法
public List findAll() {
        Connection connection = null;
        ResultSet resultSet = null;
        Statement st = null;
        // 1。通过泛型类 获取Class 对象
        Class<T> aClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        // System.out.println(tClass);
        // 2。注册驱动,这句话可以省略 驱动自动加载
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql:///jdbcDemo", "", "");
            // 3。获取mysql表名
            // todo 表名转换不严谨
            String tableName = aClass.getSimpleName().toLowerCase();
            // System.out.println(tableName);
//      // 4。定义sql语句 select id, name,age from student
            Field[] fields = aClass.getDeclaredFields();
            StringBuilder columns = new StringBuilder();
            // 5。利用反射 在运行时拿到表对应实体类的信息
            for (Field field : fields) {
//            System.out.println(field.getName());
                columns.append(field.getName());
                columns.append(",");
            }
//        // 去掉最后一位逗号
            columns.deleteCharAt(columns.length() - 1);
            // 6。拼接sql语句
            String sql = String.format("select %s from %s", columns, tableName);
            // System.out.println(sql);
            st = connection.createStatement();
            resultSet = st.executeQuery(sql);
//            原始写法;
//            List<Student> list = new ArrayList<Student>();
//            while (resultSet.next()) {
//                Student student = new Student();
//                int id = resultSet.getInt("id");
//                String name = resultSet.getString("name");
//                int age = resultSet.getInt("age");
//                student.setId(id);
//                student.setName(name);
//                student.setAge(age);
//                list.add(student);
//            }
            // 反射写法
            List<T> list = new ArrayList<T>();
            Constructor<T> constructor = aClass.getConstructor();
            while (resultSet.next()) {
                T t = constructor.newInstance();
                for (Field field : fields) {
                    field.setAccessible(true);
                    Object value = resultSet.getObject(field.getName());
                    field.set(t, value);
                }
                list.add(t);
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return null;
    }
3.1.4封装findById方法

自定义注解

package com.codingfuture.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @AUTHOR: Petrel
 * @DESCRIPTION:
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
// 1.定义findById 方法,参数可以是string 或者int 类型
    public T findById(Serializable id) {
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        // 2。 获取泛型对象
        Class<T> aClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        try {
            //3。 获取表名
            String tableName = aClass.getSimpleName().toLowerCase();
            connection = DriverManager.getConnection("jdbc:mysql:///jdbcDemo", "", "");
            // 4. 获取所有字段
            Field[] fields = aClass.getDeclaredFields();
            StringBuilder columns = new StringBuilder();
            // 5。通过注解方式获取主键
            String keyField = null;
            for (Field field : fields) {
                if (field.isAnnotationPresent(Id.class)) {
                    keyField = field.getName();
                }
                columns.append(field.getName());
                columns.append(",");
            }
            columns.deleteCharAt(columns.length() - 1);
            System.out.println(columns);
            // 6。定义sql
            String sql = String.format("select %s from %s where %s = ? ", columns, tableName, keyField);
            System.out.println(sql);
            ps = connection.prepareStatement(sql);
            // 7. 给?赋值
            ps.setObject(1, id);
            // 8。 执行sql
            resultSet = ps.executeQuery();
            // 9.封装对象
            T t = aClass.newInstance();
            if (resultSet.next()) {
                for (Field field : fields) {
                    field.setAccessible(true);
                    Object value = resultSet.getObject(field.getName());
                    field.set(t, value);
                }
            }
            return t;

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {

        }

        return null;
    }
3.1.5封装updateById方法
public void updateById(T t) {
    Class<?> aClass = t.getClass();
    // update student set name=? where id=?
    StringBuilder sql = new StringBuilder("update ");
    String tableName = aClass.getSimpleName().toLowerCase();
    sql.append(tableName);
    sql.append(" set ");

    Field[] fields = aClass.getDeclaredFields();
    for (Field field : fields) {
        String fieldName = field.getName();
        sql.append(fieldName);
        sql.append("=?,");
    }
    sql.deleteCharAt(sql.length() - 1);
    sql.append(" where id =");
    sql.append(" ? ");

    System.out.println(sql);//update student set id= ?,name= ? where id = ?
}
//问题:我们要要修改的是 name。并不是id。 id是作为where的条件出现的。所以代码需要使用 注解修改
public void updateById(interger id,T t) {
        Class<?> aClass = t.getClass();
        //update student set name=? where id=?
        StringBuilder sql = new StringBuilder("update ");
        String tableName = tClass.getSimpleName().toLowerCase();
        sql.append(tableName);
        sql.append(" set ");

        Field[] fields = aClass.getDeclaredFields();
        Field keyField = null;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Id.class)) {
                keyField = field;
                continue;
            }
            String fieldName = field.getName();//
            sql.append(fieldName);
            sql.append("= ?,");
        }
        sql.deleteCharAt(sql.length() - 1);
        sql.append(" where ");
        sql.append(keyField.getName());
        sql.append("= ?");

        System.out.println(sql);//update student set name=? where id=?

        // 为? 赋值
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            PreparedStatement ps = connection.prepareStatement(sql.toString());
            Field keyField1 = null;

            for (int i = 0; i < fields.length; i++) {
                if (fields[i].isAnnotationPresent(Id.class)) {//白话:Id注解是否注解在 成员变量类上。
                    keyField1 = fields[i];
                    continue;
                }
                Field field = fields[i];
                field.setAccessible(true);
                Object value = field.get(t);//zss
                ps.setObject(i, value);
            }

            keyField1.setAccessible(true);
            Object value1 = keyField1.get(t);
            ps.setObject(fields.length, value1);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
3.1.6封装deleteById方法
 public boolean deleteById(T t) {
        //delete from student where id = ?
        Class<?> aClass = t.getClass();
        StringBuilder sb = new StringBuilder("delete from ");
        sb.append(this.aClass.getSimpleName().toLowerCase()).append(" where ");

        Field fieldName = null;
        //获取对象属性数组
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Id.class)) {
                fieldName = field;
                break;
            }
        }
        sb.append(fieldName.getName()).append("=?");
        String sql = sb.toString();
        System.out.println(sql);

        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql:///db01", "", "");
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < fields.length; i++) {
                if (fields[i].isAnnotationPresent(Id.class)) {

                    Field fields1 = fields[i];
                    fields1.setAccessible(true);
                    Object value = fields1.get(t);
                    ps.setObject(1, value);
                    break;
                }
            }
            int res = ps.executeUpdate();
            return res != 0 ? true : false;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gao_xu_sheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值