Java反射机制

一、引入

回顾之前java程序如何使用类

1、分析确定类名,属性名,方法....创建类

public class Car{

        private String name;

        private String color;

        ....

        public void run(){}

}

2、创建类的对象

Car bm = new Car();

Car bc = new Car();

3、使用

bm.run();

bc.run();

一切都是已知的

在程序开发中,在哪里需要使用哪个类的对象,就在哪创建这个类的对象,去使用即可。这种写法对于业务开发是没有问题的 。

但是在一些组件、框架开发中,他们本身时不知道要处理那些类的。

例如jackson组件,我们给他什么类,他就要处理转换什么类

new ObjectMapper().writeValueAsString(result)

在web.xml中配置了那些servlet类,tomcat就要创建哪些类的对象

<servlet-class>com.wbc.dorm.web.LoginServlet</servlet-class>
<servlet-class>com.wbc.dorm.web.TestServlet</servlet-class>

在myBatis中,给了什么类型,myBatis就可以将结果封装映射到给定的类的对象中

<select id="login1" parameterType="Admin" resultType="Admin">
<update id="updateTeacher" parameterType="Teacher" >

框架只需要写一套程序,就可以处理我们给他的类。框架是如何做到写一套程序,就可以处理任意类的呢?

以前已知类名的使用方式可以看作是正向使用类,框架需要对任意类处理时,只是知道类的信息,通过类的名字,动态采取获得类中的信息。

我们把这种对类的使用方式,称为反向使用

二、java的反射机制

1、反射机制的定义

        在运行状态中,仅知道一个类名时,就可以动态获得类中信息,创建对象,调用成员的机制,称为java的反射机制

2、反射机制的实现

1、获得class类对象 java.lang.Class

Class类的对象,表示当前正在运行中的类和接口

 我们通过Class类的对象来获取类的信息,主要有三种方法

 

package com.wbc.javaReflect;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //以前创建类是已知类名,new创建对象 调用对象成员
       /* User user = new User();
        user.login();*/

        //使用反射机制时,我们只知道类的名称(包含包名+类名)
        String className = "com.wbc.javaReflect.User";

        //如何活得类的信息呢---可以通过Class类来获得类中信息

        //如何获得类的Class对象?
        //方式1
        Class clazz1=Class.forName(className);
        System.out.println(clazz1);

        //方式2
        Class clazz2=User.class;
        System.out.println(clazz1==clazz2);//true 一个类被加载进内存只能生成一个Class对象

        //方式3
            User user=new User();
            Class clazz3=user.getClass();
    }
}

 需要注意的是,一个类在内存中只会为该类创建一个class对象,通过这个对象就可以调用类的成员

2、通过class对象获得类的对象

package com.wbc.javaReflect;

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

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        /*
            Java中创建对象的方式
            1、new对象
            2、序列化--反序列化重新生成对象
            3、反射机制(反射机制有2种 一种是调用class类的对象创建对象;一种是调用.getConstructor()构造方法,通过构造方法创建对象)
            4、对象克隆
        */
        String className = "com.wbc.javaReflect.User" ;
        //1、通过类名,获得到类的Class对象
        Class aClass = Class.forName(className);

        //通过类的Class对象,创建对象
        Object object = aClass.newInstance();
        System.out.println(object);
        //获取user对象的属性
        Field[] fields = aClass.getDeclaredFields();



        //2、通过类中的构造方法,通过构造方法API中的方法创建对象
        Constructor constructor1 =  aClass.getConstructor();//获取公共的无参构造方法
            Object object1 = constructor1.newInstance();
        Constructor constructor2=aClass.getConstructor(String.class,String.class);//获取公共的有两个String参数的构造方法
            Object object2 = constructor2.newInstance("张三","111");

        System.out.println(object1);
        System.out.println(object2);

        

    }
}

3、获得对象的构造方法

//获取公共的无参构造方法
Constructor constructor1 =  aClass.getConstructor();
//获取公共的有参数的构造方法
Constructor constructor2=aClass.getConstructor(String.class,String.class);
//获得所有公共的构造方法
Constructor[] constructors = aClass.getConstructors();
//获取类中任意的构造方法(包括私有的)
Constructor constructor = aClass.getDeclaredConstructor();
//获取类中所有的构造方法(包括私有的)
Constructor[] constructors2 =   aClass.getDeclaredConstructors();

4、获取对象的属性

//拿到指定名称公共的属性(成员变量)
Field field = aClass.getField("account");
//拿到指定名称的属性(成员变量)包括属性
Field accountField = aClass.getDeclaredField("account")
//允许访问私有属性
accountField.setAccessible(true);
例:简单模拟myBatis从数据库查找后的自动封装的原理(破解权限)

Uesr类

package com.wbc.javaReflect;

public class User {
    private String account;
    private String password;

    public User(String account, String password) {
        this.account = account;
        this.password = password;
        System.out.println("User的有参构造");
    }

    public User() {
        System.out.println("User无参构造");
    }
    public void login(){
        System.out.println("用户登录");
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "account='" + account + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

 模拟赋值

package com.wbc.javaReflect;

import java.lang.reflect.Field;
import java.util.HashMap;

public class Test3 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        String className = "com.wbc.javaReflect.User" ;
        //1、通过类名,获得到类的Class对象
        Class aClass = Class.forName(className);

        //通过类的Class对象,创建对象
        Object object = aClass.newInstance();

        //获得类中的成员变量
        /*Field field = aClass.getField("account");//拿到指定名称公共的属性(成员变量)
        Field accountField = aClass.getDeclaredField("account");//拿到指定名称的属性(成员变量)包括属性
            accountField.setAccessible(true);//允许访问
            accountField.set(object,"admin");//赋值
*/
    //模拟myBatis自动封装到对象
        //模拟从数据库中拿到的数据
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("account","admin");
        hashMap.put("password","123456");
        Field[] fields = aClass.getDeclaredFields();
        //封装
        for (Field field : fields) {
            field.setAccessible(true);//允许访问私有属性
            field.set(object,hashMap.get(field.getName()));
        }
        System.out.println(object);
    }
}

5、获得对象方法

Method method1 =aClass.getMethod("login");//无参的方法
Method method2 = aClass.getMethod("login", String.class, String.class);//有参的方法
Method method = aClass.getDeclaredMethod("方法名", "类型.class");//私有方法
Method[] methods = aClass.getMethods();//方法集合
Method[] methods1 = aClass.getDeclaredMethods();//私有方法集合
//调用方法对象的invoke方法调用方法
method1.invoke(object);
method2.invoke(object,"admin","111");
 例1:简单模拟myBatis从数据库查找后的自动封装的原理(通过set方法)
package com.wbc.javaReflect;

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

public class Test5 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String className = "com.wbc.javaReflect.User" ;
        //1、通过类名,获得到类的Class对象
        Class aClass = Class.forName(className);

        //通过类的Class对象,创建对象
        Object object = aClass.newInstance();
        //通过属性的get和set方法,对类中的私有属性进行赋值,取值操作
            //模拟的数据
            HashMap<String,String> hashMap = new HashMap<>();
            hashMap.put("account","admin");
            hashMap.put("password","123456");
            //先拿到类中所有的私有属性
            Field[] fields = aClass.getDeclaredFields();
                //循环拿到其set方法名字
                for (Field field : fields) {
                    field.setAccessible(true);
                    //根据属性名生成set方法名称 需要注意的是,属性名和方法名称需要规范命名才可以动态生成
                    String setMethodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                    //通过Class对象获得相对应的set方法对象
                    Method setMethodObj = aClass.getMethod(setMethodName,field.getType());
                    //调用set方法
                    setMethodObj.invoke(object, hashMap.get(field.getName()));
                }
        //测试是否封装成功        
        System.out.println(object);
    }
}

封装成功 

例2:简单模拟生成json环节
package com.wbc.javaReflect;

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

public class JsonUtil {
    public  static String objectToJson(Object obj) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class clazz = obj.getClass();
        String json="{";
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //生成属性get方法名字
            field.setAccessible(true);
            String methodsName = "get"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);
            //获得方法对象
            Method method = clazz.getMethod(methodsName);
            Object value=method.invoke(obj);
            //把属性名字和值拼接成键值
            json+=field.getName()+":"+value+",";
        }
        //去掉最后一位多余的逗号
        json=json.substring(0,json.length()-1)+"}";
        return json;
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        User user = new User();
            user.setAccount("admin");
            user.setPassword("123456");
        Car car = new Car();
            car.setName("宝马");
            car.setColor("红的");
        System.out.println(objectToJson(user));
        System.out.println(objectToJson(car));
    }
}

 完成

三、总结 

反射机制的优缺点

优点:

        ● 1.增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作

         ● 2.提高代码的复用率,比如动态代理

         ● 3.可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态 调用

缺点:

         ● 1.反射会涉及到动态类型的解析,导致性能要比非反射调用更低

        ● 2.使用反射技术通常要在一个没有安全限制的程序运行.使用时需要一些限制

        ● 3.反射可以绕过一些限制访问的属性或者方法,可能会导致破坏代码本身的封装性

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北京最后的深情

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

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

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

打赏作者

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

抵扣说明:

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

余额充值