动态创建对象,并给对象属性赋值

java 专栏收录该内容
22 篇文章 0 订阅

       在开发过程中经常会遇到java对象的属性特征不确定的情况,比如属性的名称,属性的类型,属性的取值等不确定的情况,如何在java运行时获取这些信息?动态的设置java对象的属性值?借助java反射机制以及javassist能够轻松解决这些问题。

简单介绍Java的反射原理

      Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。Java程序要能够运行,java虚拟机需要事先加载java类,目前我们的程序在编译期就已经确定哪些java类需要被加载。

      Java的反射机制是在编译时并不确定哪个类需要被加载,而是在程序运行时才加载、探知、自审。这样的特点就是反射。

      何为自审:通过Java的反射机制能够探知到java类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。

       Java的反射原理最典型的应用就是各种java IDE:比如Jcreator,eclipse,idea等,当我们构建出一个对象时,去调用该对象的方法和属性的时候。一按点,IDE工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供我们进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审的过程。

Java的反射API(Reflection API)

       Class类:要正确使用Java反射机制就得使用java.lang.Class这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。

反射API用于反应在当前Java虚拟机中的类、接口或者对象信息
功能:(Object object = new Object(),下面以对象object进行说明)

  1. 获取类的Class对象

    • 如果在运行时一个类的实例已经得到,你可以使用 Class c = 对象名.getClass();
      例: Class c = object.getClass();Class s = object.getSuperclass();
    • 如果你在编译期知道类的名字,你可以使用如下的方法Class c =java. awt. Button.class; 或者Class c = Integer.TYPE;
    • 如果类名在编译期不知道, 但是在运行期可以获得, 你可以使用下面的方法Class c = Class.forName(“类的全路径”);
  2. 获取类的Fields ,对Field进行赋值

    • Field[] fus = object.getClass().getDeclaredField();
    • Field fu = object.getClass().getDeclaredField(fieldName);//获取对象object的名称为fieldName的属性域。
    • fu.setAccessible(true) ;//设置属性域的访问属性
    • fu.set(object,val); //设置object对象的属性值
  3. 获取类的Method

    • Method[] ms= object.getClass().getDeclaredMethods()
  4. 获取类的Constructor

  5. 新建类的实例

    • 通过Class的函数newInstance
    • 通过Constructor对象的方法newInstance.

    利用Java反射机制我们可以很灵活的对已经加载到Java虚拟机当中的类信息进行检测。当然这种检测在对运行的性能上会有些减弱,所以什么时候使用反射,就要靠业务的需求、大小,以及经验的积累来决定。

Javassist简要介绍

        Javassist是一个开源的分析、编辑和创建Java字节码的类库。

       关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。简而言之:Javassist 能够转换现有类的基本内容,或创建一个新类。

      Javassist 可以检查、编辑以及创建 Java 二进制类。检查方面基本上与通过 Reflection API 直接在 Java 中进行的一样。Javassist 使用类池 javassist.ClassPool 类跟踪和控制所操作的类。其工作方式与 JVM 类装载器非常相似,但是有一个重要的区别是它不是将装载的、要执行的类作为应用程序的一部分链接,类池使所装载的类可以通过 Javassist API 作为数据使用。可以使用默认的类池,它是从 JVM 搜索路径中装载的,也可以定义一个搜索自定义路径列表的类池。甚至可以直接从字节数组或者流中装载二进制类,以及从头开始创建新类。

       装载到类池中的类由 javassist.CtClass 实例表示。与标准的 Java java.lang.Class 类一样, CtClass 提供了检查类数据(如字段和方法)的方法。不过,这只是 CtClass 的部分内容,它还定义了在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。奇怪的是,Javassist 没有提供删除一个类中字段、方法或者构造函数的任何方法。

       字段、方法和构造函数分别由 javassist.CtField、javassist.CtMethod 和 javassist.CtConstructor 的实例表示。这些类定义了修改由它们所表示的对象的所有方法的方法,包括方法或者构造函数中的实际字节码内容。

 示例代码

/**
 * 动态创建对象,动态添加属性
 * 必须先添加属性,再实例化,然后设置属性值
 * @author meng
 *
 */

public class DynamicCreateObject {

    public static Object getDynamicObject(Map<String, String> map) throws Exception{
        //创建一个名称为Template的类
        Object template = addField("Template"+UUID.randomUUID(),map);    
        return template;
    }
    
    public static Object addField(String className,Map<String,String> fieldMap) throws Exception {
        // 获取javassist类池
        ClassPool pool = ClassPool.getDefault();
        // 创建javassist类
        CtClass ctClass = pool.makeClass(className,pool.get(Object.class.getName()));
        // 为创建的类ctClass添加属性
        Iterator<Entry<String, String>> iterator = fieldMap.entrySet().iterator();
        
        // 遍历所有的属性
        while (iterator.hasNext()) {
            Map.Entry<String,String> entry = (Map.Entry<String,String>) iterator.next();
            String fieldName = (String)entry.getKey();
            String fieldValue = (String)entry.getValue();
            // 增加属性,这里仅仅是增加属性字段
            String fieldType = fieldValue.getClass().getName();
            CtField ctField = new CtField(pool.get(fieldType),fieldName, ctClass);
            ctField.setModifiers(Modifier.PUBLIC);
            ctClass.addField(ctField);
        }
        // 为创建的javassist类转换为java类
        Class<?> c = ctClass.toClass();
        // 为创建java对象
        Object newObject = c.newInstance();
        iterator = fieldMap.entrySet().iterator();
        // 遍历所有的属性
        while (iterator.hasNext()) {
            Map.Entry<String,String> entry = (Map.Entry<String,String>) iterator.next();
            String fieldName = (String)entry.getKey();
            String fieldValue = (String)entry.getValue();
            // 为属性赋值
            setFieldValue(newObject,fieldName,fieldValue);
        }
        return newObject;
    }
    
    public Object getFieldValue(Object object, String fieldName) throws Exception {
        Object result = null;
        // 获取对象的属性域
        Field fu = object.getClass().getDeclaredField(fieldName);
        // 设置对象属性域的访问属性
        fu.setAccessible(true);
        // 获取对象属性域的属性值
        result = fu.get(object);
        return result;
    }
    
    private static Object setFieldValue(Object object, String fieldName, String val) throws Exception {
        Object result = null;
        Field fu = object.getClass().getDeclaredField(fieldName); // 获取对象的属性域
        // 设置对象属性域的访问属性
        fu.setAccessible(true);
        // 设置对象属性域的属性值
        fu.set(object,val);
        // 获取对象属性域的属性值
        result = fu.get(object);
        return result;
    }
    
}

 

  • 1
    点赞
  • 1
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值