反射Reflect

通过名字(类名)获得字节码信息,获得对象,获得方法,甚至调用方法,什么玄学
通过全路径限定获得类对象,通过类对象获得字节码信息,字节码信息里存储的属性,方法都可以通过类对象获取到,这一切的动作都发生在程序运行时,所以反射使java语言拥有了准动态的特性
案例:美团外卖
美团外卖提供接口,支付合作商家实现接口

public interface Mtwm {
    void payonline();
}

合作商家支付宝

public class AliPay implements  Mtwm{
    @Override
    public void payonline() {
        System.out.println("正在使用支付宝支付");
    }
}

合作商家微信

public class WechatPay implements Mtwm {
    @Override
    public void payonline() {
        System.out.println("正在使用微信支付");
    }
}

测试

public class Test02 {
    public static void main(String[] args) throws Exception {
        String path = "com.pb.test01.AliPay"; //通过名字掉方法
        Class cla = Class.forName(path);
        Object o = cla.newInstance();//通过类对象创建类
        //获得类中的方法(需要知道方法名(重写应用))
        Method m = cla.getMethod("payonline");
        //调用方法
        m.invoke(o);
    }

    private static void pay(Mtwm m) {
        m.payonline();
    }

通过反射不需要多次判断前端传进来的支付商家是哪位,直接根据名字调用对应的方法(反射),提高了代码的拓展性
在这里插入图片描述

获取字节码信息(类对象)的四种方式

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.通过对象调getclass()方法
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);
        //2.通过类名调用class()静态方法
        Class c2 = Person.class;
        System.out.println(c2);
        //3.(常用)调用class类提供的静态方法forname()
        Class c3 = Class.forName("com.pb.test02.Person");
        System.out.println(c3);
        //4.(了解)利用类的加载器
        //获取当前类对象,然后通过类对象调用当前类加载器
        ClassLoader loder = Test.class.getClassLoader();
        Class c4 = loder.loadClass("com.pb.test02.Person");
        System.out.println(c4);
    }
}

不管用什么方式获得类对象,都是同一个对象,因为字节码信息只加载一次

作用

构造器

getconstructor() 获得public修饰的构造器
getdeclarestructor 获得所有构造器
如果构造器带参,那么方法里也需要传相同数量的参数,通过这种方式可以获得指定构造器

对象

类对象.getconstructor().newInstance()

属性

getFields():获取运行时类和父类中被public修饰的属性
getDeclaredFields():获取运行时类中的所有属性
如果想要获取指定属性,需要向方法中传入属性名

属性的具体结构

//获取修饰符
        /*int modifiers = sno.getModifiers();
        System.out.println(modifiers);
        System.out.println(Modifier.toString(modifiers));*/
        System.out.println(Modifier.toString(sno.getModifiers()));
        //获取属性的数据类型:
        Class clazz = sno.getType();
        System.out.println(clazz.getName());
        //获取属性的名字:
        String name = sno.getName();
        System.out.println(name);
        System.out.println("-------------------------------");
        //给属性赋值:(给属性设置值,必须要有对象)
        Field sco = cls.getField("score");
        Object obj = cls.newInstance();
        sco.set(obj,98);//给obj这个对象的score属性设置具体的值,这个值为98
        System.out.println(obj);
    }
}

方法

getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰)
getDeclaredMethods:获取运行时类中的所有方法
获取指定方法:把方法名当作参数传进去
//获取指定的方法:
Method showInfo1 = cls.getMethod(“showInfo”);

反射给基类写查询方法

//通过反射获取类名,需要先获得类对象,当前的类对象是<T>是一个泛型,
// 但是BaseDaoImpl的子类会有具体的类型,所以创建类对象的任务交给子类
// ,在这里通过有参构造器接受泛型类对象
public class BaseDaoImpl<T> implements BaseDao<T> {
    private Class<T> tClass;
    private String className; //类名
    public BaseDaoImpl(Class<T> tclass) {
        this.tClass = tclass; //获得类对象
        className = tClass.getSimpleName();
    }

    public BaseDaoImpl() {
    }

    @Override
    public T selectOnePeron(int id) {
        Connection conn = Connjdbc.conn();
        String sql = "select * from "+className+" where id = ?";
        T t = null;
        try {
            PreparedStatement ps = conn.prepareStatement(sql);
            //设置字段对应数据
            ps.setInt(1,id);
            ResultSet rs = ps.executeQuery();
            if(rs.next())
            {
                //通过反射创建对象,将结果集的内容一一赋值
                t = tClass.getConstructor().newInstance(); //不能直接调get/set方法
                //由于此时不知道rs结果集里存的字段名(靠字段名取数据),也不知道有多少个,所以用另一个方法获得个数和数据
                //获得结果集中关于字段的属性(数量,名字,数据类型,数据)
                ResultSetMetaData rsm = rs.getMetaData();
                int columnCount = rsm.getColumnCount();//字段数量
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = rsm.getColumnName(i);//列名
                    Object value = rs.getObject(columnName);//列中对应的数据
                    //obj.set方法(value)——>method.invoke(obj, value);
                    //要知道方法名和对象才能执行方法,但泛型不能直接调,所以要拼接方法名
                    StringBuilder s = new StringBuilder("set");
                    String[] nameArr = columnName.split("_");
                    for (String name : nameArr) {
                        String s1 = String.valueOf(name.charAt(0)); //获得字符串数组中每个元素的第一个字符
                        //将每个元素的第一个字符替换为大写
                        String s2 = name.replaceFirst(s1, s1.toUpperCase());
                        //将变形后的每个字符串拼接起来
                        s.append(s2); //方法名
                    }
                    String type = rsm.getColumnClassName(i); //列在表里面的类型 映射在  程序里面的数据类型
                    System.out.println(type);
                    //知道方法名后用反射获取方法
                    Method method = tClass.getMethod(s.toString(), Class.forName(type));
                    method.invoke(t,value);
                }
            }
        } catch (SQLException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException | ClassNotFoundException throwables) {
            throwables.printStackTrace();
        }

        return t;
    }
}

通过反射优化servlet代码

通过当前输入的路径调用相应方法,这样把不同的servlet都封装成方法放进一个继承了BaseServlet的类中,减少了代码的冗余

        //1.获取当次请求的路径名
        String requestURI = req.getRequestURI();
        //2.通过路径名获得方法名(要求路径名与方法名一致)
        String path = requestURI.substring(requestURI.lastIndexOf("/")+1);
        //开始反射
        //1.获取当前类对象,通过类对象和方法名获得方法
        try {
            Method method = this.getClass().getMethod(path); //(有参传参)
            //2.调用方法(传入对象和参数)
            method.invoke(this);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值