反射 初学

1. 反射引入

引用:

之前如何使用类和对象

定义类, 使用的时候是类的实例(对象)

User类 (构造方法,属性 , 方法)

User user = new User(); // 使用new关键字创建对象

使用创建的对象去调用方法或者属性

在哪里用就在哪创建该类的对象使用即可

问题:

如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法,属性等?

类名,就是类在项目中的地址,通过地址找到类,就可以动态获得类中信息

 Class.forName("com.mysql.cj.jdbc.Driver");//数据库加载驱动 


<select id="findStudents" resultType="com.ffyc.model.Student">
​
    
<servlet-class>com.practise.servlet.LoginServlet</servlet-class>

以前(在自己学习的项目中)在哪用 , 就在那new ,但是这样的话 ,代码就写死了,

但是一个框架,需要处理不同的类,都需要能够进行处理

Mybatis框架 可以写一个方法,接收一个类的地址,就可以根据地址创建该类的对象

这样,我们使用框架 就可以实现以不变应万变

2. java反射的概念和作用

2.1 概念

在程序运行的过程中,可以动态的获取任意类的信息,以及创建类的对象,以及调用对象中的属性方法

2.2 作用

动态获取类的信息(类地址)

2.3 好处

写一个方法, 可以处理任何的类

3. 反射基本用法

3.1 java 反射相关API

java反射相关的类主要包括

Class 类型 Constructor 构造方法

Method 方法 Field属性

除了Class外 , 其他类都位于 java.lang.reflect包中

java反射API将类的类型 , 方法 , 属性都封装成了类 , 其中最重要的类是Class , 可以说反射使用都是从Class开始

3.2 如何动态 获取类的信息

Class类是Java反射机制的基础 , 通过Class类 , 可以得到一个类的基本信息

User.java ----> User.class ----> 一旦程序中使用User类,就会加载字节码文件,为 改类创建一个Class类的对象, 我们可以通过该类的Class类的对象 , 获取类中的信息

3.3 如何或得类的Class对象

通过类名, 对象, 类的地址 获得类的Class对象,这样就变为统一的类型,通过Class中的方法获取信息

要使用Class类的方法 , 必须先获得类的Class类的实例, 获得类的实例的常用三种方法如下:

  1. Object类中的getClass方法:适用于通过对象获得Class实例的情况

  2. 类名.class方式 适用于通过类名获得Class实例的情况

  3. Class的静态方法 forName

package com.ffyc.test;
import com.ffyc.model.Student;
​
public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
​
        // 1. Object类中的getClass方法:适用于通过对象获得Class实例的情况
        Student student = new Student();
        Class s1 = student.getClass();
​
        // 2.类名.class方式 适用于通过类名获得Class实例的情况
        Class s2 = Student.class;
​
        // 3. Class的静态方法 forName
        String cs = "com.ffyc.model.Student";
        Class s3 = Class.forName(cs);
​
        System.out.println(s1 == s2 && s2 == s3);//true
    }
}

Class对象:一旦某个类被加载(创建对象) , 使用类中的静态成员 , 类名.class , Class.forName(类名) 都会创建类的Class对象, 只要是同一个类,获得的Class对象就是同一个

通过类的Class对象 , 获得类的信息,调用成员

3.4 获得Constructor类实例

获得类中的构造方法

Constructor类可以通过getXXX方法获得构造方法的基本信息 具体可以查找API文档

拿到类的Class对象后, 获得类中的构造方法 , 属性, 方法.....

package com.ffyc.javareflect;
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
// Constructor类实例
public class Test2 {
​
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*
             获取类的Class对象
        */
​
        // 通过Class.forName("类的地址")
        String cname = "com.ffyc.javareflect.Car"; // 类地址
        Class c = Class.forName(cname);// 通过类地址 加载类, 获得到类的Class对象
        // 获得到Class 对象后 , 如何获得类的信息,获得那些信息?
​
        /*
            获得类中的构造方法
             找无参构造方法 如果没有会报错
                Constructor<Car> constructor = c.getConstructor(); 获得公共类
                Object object = constructor.newInstance();
         */
        Constructor<Car> constructor = c.getDeclaredConstructor();//获得Class实例
        constructor.setAccessible(true); // 打开私有无参构造方法的权限,就可以访问了
        Object object = constructor.newInstance(); // 创建实例
        System.out.println(object);
​
​
        System.out.println("==================");
        Constructor constructor1 = c.getConstructor(int.class, String.class); // 找有参构造方法 如果没有会报错
​
        Object object1 = constructor1.newInstance(10, "BYD");
        System.out.println(object1);
    }
}

3.5 获得Field实例

获得类中的属性

package com.ffyc.javareflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

// Field类实例
public class Test3 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        /*
             获取类的Class对象
        */
        String cname = "com.ffyc.javareflect.Car"; // 类地址
        Class c = Class.forName(cname);// 通过类地址 加载类, 获得到类的Class对象

        Constructor constructor = c.getDeclaredConstructor(); // 获得无参构造方法创造对象
        constructor.setAccessible(true);//打开私有权限
        Object object = constructor.newInstance();//创建实例
        System.out.println(object);
        
        System.out.println("================");
        /*
            获得类中的属性  Field类
                 c.getField(name); 获得指定名称的公共权限属性
                 getFields() 获得所有公共属性
                 c.getDeclaredField("name");  获得指定名称私有属性
                 getDeclaredFields() 获得所有私有属性
         */

        // map 模拟查询出来的数据结果
        Map<String, Object> map = new HashMap<>();
        map.put("no", 100);
        map.put("color", "blue");
        map.put("name", "BYD");

        // 获得类中所有属性 fields
        Field[] fields = c.getDeclaredFields();
        System.out.println(fields.length);
        for (Field f : fields) {
            f.setAccessible(true);// 打开权限
            // 将Map中的每个键 传入 每个属性f 中
            f.set(object, map.get(f.getName()));
        }
        System.out.println(object);

    }
}

3.6 获得Method实例

获得类中的所有方法

package com.ffyc.javareflect;

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

//  获得所有方法
public class Test4 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        /*
             获取类的Class对象
        */
        String cname = "com.ffyc.javareflect.Car"; // 类地址
        Class c = Class.forName(cname);// 通过类地址 加载类, 获得到类的Class对象

        Constructor constructor = c.getDeclaredConstructor(); // 获得无参构造方法 并创造对象
        constructor.setAccessible(true);//打开私有权限
        Object object = constructor.newInstance();//创建实例

        /*
           c.getMethods() 拿出所有公共方法
           c.getMethod("getNo"); 获得公共的指定名称参数的成员方法
           c.getMethod("setNo", int.class); 要有type
            c.getDeclaredFields();获得所有方法(包含私有)
            c.getDeclaredField(name,type);获得所有方法(包含私有)
         */
        //c.getMethod("getNo");
        Method method = c.getMethod("setNo", int.class);
        method.invoke(object, 100);//传入100
        System.out.println(object);
    }
}

4. Java对象转json(反射案例)

package com.ffyc.test;

import com.ffyc.model.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

// 自测field类
public class ObjectToJson {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Student student = new Student(1001, "小明", "男");

        String json = ObjectToJson(student);

        System.out.println(student);
        System.out.println("转JSON~~~");
        System.out.println(json);
    }

    /*
        Student{id=1001, name='小明', gender='男'}
            转JSON~~~
        {id:1001,name:小明,gender:男}
        invoke() 方法获取方法的返回值
        思路分析
            1.定义待返回字符串 String s = "{"
            2.将传入对象实例化
            3.拿到Class实例化对象的所有属性
            4.增强for循环遍历
                4.0 获得每个属性的名称
                4.1 将每个属性名拼接获得相应get方法名 例如 id ---> getId
                4.2 通过get方法名查找Class实例对象中的相应方法
                4.3 将Object对象中相应get方法的返回值拿到 method.invoke(object)
                4.4. 拼接 每次遍历得到 属性名称 和 :和 相应的值 和 ,
            5.删除字符串尾部多余的,
            6.拼接右大括号
            7.返回结果
     */

    private static String ObjectToJson(Object object) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String s = "{";
        Class c = object.getClass();// 通过对象获取Class实例

        Field[] fields = c.getDeclaredFields();// 拿到所有属性
        for (Field f : fields) {
            String filed = f.getName();// 键 id  属性名
            String getMethodName = check(filed); // 通过属性名拼接成方法名
            Method method = c.getMethod(getMethodName);// 通过方法名找到Class实例中的方法
            Object val = method.invoke(object);// 将Object 对象中的值赋给他 改属性名所对应的值
            s += filed + ":" + val + ",";
        }
        s = s.substring(0, s.length() - 1);
        s += "}";
        return s;
    }

    /*
        将属性名转为相应的get方法 id ---> getId
        思路分析:
            1.先拼创建字符串 "get"
            2.将该属性名第一个字母大写
                2.1 拿到传入值的第一个字符 charAt(0)
                2.2 将一个字符转为String型 String.valueOf()
                2.3 将第一个字符转为大写字母
            3.截取属性名第一个字母以后substring(0)
            4.将以上三部分拼接在一起并返回
     */
    private static String check(String fieldName) {
        String s1 = "get";
        String s2 = String.valueOf(fieldName.charAt(0)).toUpperCase();
        String s3 = fieldName.substring(1);
        String getMethodName = s1 + s2 + s3;
        return getMethodName;
    }
}

5. 反射优缺点

以前在项目中使用类 , 是正向使用 , 知道类名 , new类的对象,对象

在一些框架/组件中 为了对任意的类进行处理, 所以就需要获得类的Class对象, 通过类的Class对象获取类信息,创建对象, 调用对象中的方法 , 是一种反向使用类

优点:

在运行时 可以动态获取类的任意信息, 创建对象 , 调用对象方法属性, 提高代码的灵活性, 复用性

缺点:

需要对类信息进行解析判断处理 , 效率相对较低

使用反射机制可以操作私有的成员 , 会打破封装性

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值