Java反射的学习

Java反射的学习

参考链接:https://blog.csdn.net/weixin_60475929/article/details/122653101

1.实例化Class类对象的四种方法

image-20200408112038746

Java获得类对象的三种方式

在这里插入图片描述

@Test
public void testString() throws ClassNotFoundException, 
NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    // 1.通过类名.class获取类对象
    Class<com.bilibili.juc.Student> studentClass = com.bilibili.juc.Student.class;
    // 输出:studentClass:class com.bilibili.juc.Student
    System.out.println("studentClass:" + studentClass);

    // 2.通过实例对象.getClass()获取类对象
    com.bilibili.juc.Student student = new com.bilibili.juc.Student();
    Class<? extends com.bilibili.juc.Student> aClass = student.getClass();
    // 输出:aClass:class com.bilibili.juc.Student
    System.out.println("aClass:" + aClass);

    // 3.通过Class.forName("类的全限定类名")获取类对象
    Class<?> classForName = Class.forName("com.bilibili.juc.Student");
    // 输出:classForName:class com.bilibili.juc.Student
    System.out.println("classForName:" + classForName);
}

// 反射的第一步就是先得到编译后的Class对象,然后就可以得到Class的全部成分
// 获取到类对象后,就可以获取到类对应的构造方法、属性以及方法,用于创建对象或方法调用

哪些类型可以有Class类对象

image-20200408112114089

image-20200408112132517

2.实体类Student

package com.bilibili.juc;


public class Student implements Cloneable{

    private Double weight;

    private Integer pkid;

    private String name;


    public Student() {
    }

    public Student(Double weight){
        this.weight = weight;
    }

    private Student(Double weight, Integer pkid, String name) {
        this.weight = weight;
        this.pkid = pkid;
        this.name = name;
    }

    public void study(){
        System.out.println("我是学生,我要学习");
    }

    private void eat(){
        System.out.println("我是学生,我要吃饭");
    }

    private String happy(String style){
        return "我是学生,我要玩" + style;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    public Integer getPkid() {
        return pkid;
    }

    public void setPkid(Integer pkid) {
        this.pkid = pkid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    @Override
    public String toString() {
        return "Student{" +
                "weight=" + weight +
                ", pkid=" + pkid +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

3.反射的学习

反射可以取得类的哪些东西

image-20200408113122926

image-20200408113147490

image-20200408113202204

image-20200408113216323

image-20200408113230862

/**
  * 反射是指对于任何一个Class类,在”运行的时候“都可以直接得到这个类全部成员
  * https://blog.csdn.net/weixin_60475929/article/details/122653101
*/
@Test
public void testString() throws ClassNotFoundException, NoSuchMethodException, 
IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    // 1.通过类名.class获取类对象
    Class<com.bilibili.juc.Student> studentClass = com.bilibili.juc.Student.class;
    // 输出:studentClass:class com.bilibili.juc.Student
    System.out.println("studentClass:" + studentClass);

    // 2.通过实例对象.getClass()获取类对象
    com.bilibili.juc.Student student = new com.bilibili.juc.Student();
    Class<? extends com.bilibili.juc.Student> aClass = student.getClass();
    // 输出:aClass:class com.bilibili.juc.Student
    System.out.println("aClass:" + aClass);

    // 3.通过Class.forName("类的全限定类名")获取类对象
    Class<?> classForName = Class.forName("com.bilibili.juc.Student");
    // 输出:classForName:class com.bilibili.juc.Student
    System.out.println("classForName:" + classForName);
    
    // 反射的第一步就是先得到编译后的Class对象,然后就可以得到Class的全部成分
    // 获取到类对象后,就可以获取到类对应的构造方法、属性以及方法,用于创建对象或方法调用
    
    /*
        Class类中获取构造器的方法,获取构造器的作用:依旧是初始或一个对象返回
               方法                                                            说明
        Constructor<?>[ ] getconstructors()            返回所有构造器对象的数组(只能拿public的)
        Constructor<?>[] getDeclaredConstructors()     返回所有构造器对象的数组,存在就能拿到
        constructor<T> getconstructor(class<?>... parameterTypes) 返回单个构造器对象(只能拿public的)
        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造器对象,存在就能拿到
    * */
    // 提取类中的全部构造器对象(public)
    Constructor<?>[] constructors = classForName.getConstructors();
    // 遍历构造器
    for (Constructor constructor : constructors) {
        // 名称 + 参数
        System.out.println("getConstructors:" + constructor.getName()+"=====>"
        + constructor.getParameterCount());
        /**
             * 输出:
             * getConstructors:com.bilibili.juc.Student=====>0
             * getConstructors:com.bilibili.juc.Student=====>1
        */
    }

    // 获取全部的构造器,无所谓权限是否可及
    Constructor[] construct1 = classForName.getDeclaredConstructors();
    for (Constructor constructor : construct1) {
        System.out.println("getDeclaredConstructors:" + constructor.getName()
        + "====>" + constructor.getParameterCount());
        /**
             * 输出:
             * getDeclaredConstructors:com.bilibili.juc.Student====>0
             * getDeclaredConstructors:com.bilibili.juc.Student====>3
             * getDeclaredConstructors:com.bilibili.juc.Student====>1
        */
    }

    // 获取某个构造器对象(按照参数定位无参构造器  只能拿public修饰某个构造器)
    Constructor cons = classForName.getConstructor(Double.class);
    // 名称 + 参数,输出:Double.class:com.bilibili.juc.Student=====>1
    System.out.println("Double.class:" + cons.getName()
    + "=====>"+cons.getParameterCount());

    // 获取某个构造器,无所谓权限是否可及
    Constructor conss = classForName.getDeclaredConstructor(Double.class, 
    Integer.class, String.class);
    // 名称 + 参数,输出:getDeclaredConstructor:com.bilibili.juc.Student=====>3
    System.out.println("getDeclaredConstructor:" + conss.getName()
    + "=====>"+conss.getParameterCount());

    System.out.println("-----------------------------------------");

    /* Constructor类中用于创建对象的方法:
        *           符号                                    说明
            T newlnstance(Object... initargs)        根据指定的构造器创建对象
            public void setAccessible(boolean flag)  设置为true,表示取消访问检查,进行暴力反射
    * */
    Constructor<?> declaredConstructor = classForName.getDeclaredConstructor(Double.class, 
    Integer.class, String.class);
    // 暴力反射,因为这个有三个参数的构造是私有的
    declaredConstructor.setAccessible(true);
    com.bilibili.juc.Student newInstance =
    (com.bilibili.juc.Student)declaredConstructor.newInstance(50.00, 25, "张三");
    // 输出:newInstance:Student{weight=50.0, pkid=25, name='张三'}
    System.out.println("newInstance:" + newInstance);

    System.out.println("-------------------------------------");
    
    /*
        * class类中用于获取成员变量的方法
        *           方法                                    说明
            Field[] getFields()       返回所有成员变量对象的数组(只能拿public的)
            Field[]getDeclaredFields()   返回所有成员变量对象的数组,存在就能拿到
            Field getField(String name)    返回单个成员变量对象(只能拿public的)
            Field getDeclaredField(String name) 返回单个成员变量对象,存在就能拿到
    * */
    Field[] declaredFields = classForName.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        // getAnnotatedType:返回一个 AnnotatedType 对象,该对象表示使用类型来指定由该字段对象表示的字段的类型,
        // 通过其 getType() 方法,我们可以获取到对应的字段类型
        System.out.println("getDeclaredFields:" + declaredField.getName()+"====>"
         + declaredField.getAnnotatedType().getType());
        /**
             * 输出:
             * getDeclaredFields:weight====>class java.lang.Double
             * getDeclaredFields:pkid====>class java.lang.Integer
             * getDeclaredFields:name====>class java.lang.String
        */
    }

    // 根据名称定位某个成员变量
    Field name = classForName.getDeclaredField("name");
    // 输出:getDeclaredField:name====>class java.lang.String
    System.out.println("getDeclaredField:" + name.getName()+"====>"
     + name.getAnnotatedType().getType());

    /*
        * 获取成员变量的作用依然是在某个对象中取值、赋值,Field类中用于取值、赋值的方法
                符号                                  说明
            void set(Object obj, Object value):     赋值
            Object get(Objectiobj)                  获取值
    * */
    // 暴力打开权限,因为name属性是private的
    name.setAccessible(true);
    // 赋值
    com.bilibili.juc.Student s = new com.bilibili.juc.Student();
    name.set(s, "zhangshan");// s.setName("zhangsan")
    // 输出:s:Student{weight=null, pkid=null, name='zhangshan'}
    System.out.println("s:" + s);

    String getName = (String)name.get(s);
    // 输出:getName:zhangshan
    System.out.println("getName:" + getName);

    System.out.println("----------------------------------");

    /*
     * Class类中用于获取成员方法的方法
                方法                                  说明
     Method[ ] getMethods()   返回所有成员方法对象的数组(只能拿public的)
     Method[ ] getDeclaredMethods()  返回所有成员方法对象的数组,存在就能拿到
     Method getMethod(String name,class<?>... parameterTypes) 
                      返回单个成员方法对象((只能拿public的), parameterTypes:参数类型
     Method getDeclaredMethod(String name,Class<?>... parameterTypes)
                      返回单个成员方法对象,存在就能拿到, parameterTypes:参数类型
    * */

    // 提取所有方法,包括私有的
    Method[] declaredMethods = classForName.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
        System.out.println("方法名:" + declaredMethod.getName() + ",返回值类型:" 
        + declaredMethod.getReturnType() 
        + ",参数个数:" + declaredMethod.getParameterCount());
        /**
             * 输出:
             * 方法名:study,返回值类型:void,参数个数:0
             * 方法名:eat,返回值类型:void,参数个数:0
             * 方法名:happy,返回值类型:class java.lang.String,参数个数:1
             * 方法名:getWeight,返回值类型:double,参数个数:0
             * 方法名:setWeight,返回值类型:void,参数个数:1
             * 方法名:setPkid,返回值类型:void,参数个数:1
             * 方法名:getPkid,返回值类型:class java.lang.Integer,参数个数:0
             * 方法名:toString,返回值类型:class java.lang.String,参数个数:0
             * 方法名:clone,返回值类型:class java.lang.Object,参数个数:0
             * 方法名:getName,返回值类型:class java.lang.String,参数个数:0
             * 方法名:setName,返回值类型:void,参数个数:1
             */
    }

    /*
        * 获取成员方法的作用依然是在某个对象中进行执行此方法,Method类中用于触发执行的方法
                符号                                      说明
          Object invoke(0bject obj,object.. . args)  运行方法
                                                      参数一:用obj对象调用该方法
                                                      参数二:调用方法的传递的参数(如果没有就不写)
                                                      返回值:方法的返回值(如果没有就不写)
    * */
    Method eat = classForName.getDeclaredMethod("eat");
    Method happy = classForName.getDeclaredMethod("happy", String.class);
    eat.setAccessible(true); // 打开暴力反射
    happy.setAccessible(true); // 打开暴力反射
    com.bilibili.juc.Student student1 = new com.bilibili.juc.Student();
    Object invoke_eat = eat.invoke(student1);
    // 如果方法是没有返回结果的,那么返回的为null
    /**
         * 输出:
         * 我是学生,我要吃饭
         * invoke_eat:null
         */
    System.out.println("invoke_eat:" + invoke_eat);

    Object invoke_happy = happy.invoke(student1, "游戏");
    // 输出:invoke_happy:我是学生,我要玩游戏
    System.out.println("invoke_happy:" + invoke_happy);

    /*
        * 反射的作用:绕过编程阶段为集合添加元素,
        * 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的
        * 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,
        * 其真实类型都是ArrayList了,泛型相当于被擦除了。
    * */
    ArrayList<Integer> list1 = new ArrayList<>();
    list1.add(120);
    // list1.add("加入数据"); // 报错
    Class c = list1.getClass();
    Method add = c.getDeclaredMethod("add", Object.class);
    boolean rs = (boolean)add.invoke(list1, "加入数据");
    // 输出:list1:[120, 加入数据]
    System.out.println("list1:" + list1);
    // 输出:rs:true
    System.out.println("rs:" + rs);
    /*
        * 1.反射为何可以给约定了泛型的集合存入其他类型的元素?
            编译成Class文件进入运行阶段的时候,泛型会自动擦除。
            反射是作用在运行时的技术,此时已经不存在泛型了。
    * */


    /*
        * 结论:反射的作用?
            可以在运行时得到一个类的全部成分然后操作。
            可以破坏封装性。(很突出)
            也可以破坏泛型的约束性。(很突出)
            更重要的用途是适合:做Java高级框架
    * */
}

4.案例:利用反射存储任意对象

在这里插入图片描述

/*
        分析
            ①定义一个方法,可以接收任意类的对象。
            ②每次收到一个对象后,需要解析这个对象的全部成员变量名称。
            ③这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
            ④使用反射获取对象的Class类对象,然后获取全部成员变量信息。
            ⑤遍历成员变量信息,然后提取本成员变量在对象中的具体值
            ⑥存入成员变量名称和值到文件中去即可。
* */
@Test
public void fansheTest(){
    com.bilibili.juc.Student student = new com.bilibili.juc.Student(50.00);
    save(student);
}

// 保存任意类型的对象
public static void save(Object obj){
    try {
        // 使用FileOutputStream时,如果文件不存在,会自动创建文件。但是,如果文件夹不存在,就会报错"系统找不到指定的路径"
        PrintStream ps = new PrintStream(new FileOutputStream("D:\\test\\fanshe.txt",true));
        // 1.提取对象的全部成员变量:只有反射可以解决
        Class c = obj.getClass();
        // c.getSimpleName()获取当前类名,c.getName获取全限名:包名+类名
        ps.println("========>"+c.getSimpleName()+"======<");
        // 2.提取全部成员变量
        Field[] f = c.getDeclaredFields();
        // 3.获取成员变量的信息
        for (Field field : f) {
            String name = field.getName();
            field.setAccessible(true);
            // 提取本成员变量在obj对象中的值(取值)
            String value = field.get(obj) + "";
            ps.println(name+"="+value);
        }
        ps.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

喜欢请关注我

至此,我们的Java反射的学习就讲解完成了。喜欢我的话可以关注我的微信公众号我爱学习呀嘻嘻 ,不定期分享各类资源哦。

image-20211108230322493

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值