精华内容
下载资源
问答
  • 动态创建对象,给对象属性赋值
    千次阅读
    2018-07-24 10:04:28

           在开发过程中经常会遇到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;
        }
        
    }

     

    更多相关内容
  • Java自定义类创建对象数组并赋值

    千次阅读 2021-12-22 15:41:03
    一、起因     采用下面的代码为对象数组里面的每个元素的字段赋值时报错。... //创建对象数组,没有显式初始化,stuArr存储地址 for(int i = 0; i < stuArr.length; ++i){

    一、起因

        采用下面的代码为对象数组里面的每个元素的字段赋值时报错。

    public class StudentArray {
        public static void main(String[] args){
            Student[] stuArr = new Student[5]; //创建对象数组,stuArr存储地址
    
            for(int i = 0; i < stuArr.length; ++i){
                System.out.println(stuArr[i]); //null
    
                stuArr[i].number = i + 1; //报错
                stuArr[i].state = (int)Math.round(Math.random() * 3);
                stuArr[i].score = (int)Math.round(Math.random() * 100);
                System.out.println(stuArr[i]);
            }
        }
    }
    
    class Student{
        int number;
        int state;
        int score;
    
        @Override
        public String toString() {
            return "Student{" +
                    "number=" + number +
                    ", state=" + state +
                    ", score=" + score +
                    '}';
        }
    }
    

        错误信息如下:自定义对象数组的元素的值为null,报空指针异常。

    null
    Exception in thread "main" java.lang.NullPointerException: Cannot assign field "number" because "stuArr[i]" is null
    	at com.zhangqu.java.method.StudentArray.main(StudentArray.java:19)
    

        原因分析:因为Student[] stuArr = new Student[5];创建对象数组时,返回系统为该对象数组在堆区分配的空间的地址,并将地址值存储到引用变量stuArrstuArr的数据类型为类数组Student[],是引用类型;而该数组的每一个元素都是类Student的对象(本质是引用,存储null或地址值),数组的每一个元素的数据类型为类Student,也是引用类型,由于没有继续使用new为这些引用类型Student类对象分配空间,所有数组的元素默认被初始化为null在这里插入图片描述

        不较真的情况下,可以把Student[] stuArr = new Student[5];看做如下形式:

    Student stu1 = null;
    Student stu2 = null;
    ......
    Student stu5 = null;
    Student[] stuArr = {stu1, stu2, ..., stu5}
    

    二、正确代码

    public class StudentArray {
        public static void main(String[] args){
            Student[] stuArr = new Student[5]; //创建对象数组,stuArr存储地址, 初始化变量stuArr
            int num = 0;
            for (Student stu : stuArr) {
                System.out.println(stu); //null
    
                stu = new Student(); //用new分配空间,显式的为数组的每一个元素赋值,stu存储地址
                System.out.println(stu);
                System.out.println(stu.getClass().toString()); //获取数据类型
    
                stu.number = ++num;
                stu.state = (int)Math.round(Math.random() * 3);
                stu.score = (int)Math.round(Math.random() * 100);
                System.out.println(stu);
            }
        }
    }
    
    class Student{
        int number;
        int state;
        int score;
    
        @Override
        public String toString() {
            return "Student{" +
                    "number=" + number +
                    ", state=" + state +
                    ", score=" + score +
                    '}';
        }
    }
    

    运行结果:

    null
    Student{number=0, state=0, score=0}
    class com.zhangqu.java.method.Student
    Student{number=1, state=1, score=10}
    null
    Student{number=0, state=0, score=0}
    class com.zhangqu.java.method.Student
    Student{number=2, state=0, score=42}
    ......
    

        内存分析概要图如下:
    正确创建对象数组
        由于是自定义的类,所以创建对象数组时,第一步是用new为数组对象本身分配空间(初始化),第二步是用new为数组对象的元素分配空间(赋值)。

    public class StudentArray {
        public static void main(String[] args){
        }
    
        @Test
        void testObjArr(){
            StuObjArr[] stuObjArrs = new StuObjArr[2]; //第一步,为对象数组分配空间,初始化stuObjArrs
            for(int i = 0; i < stuObjArrs.length; i++){
                stuObjArrs[i] = new StuObjArr(); //第二步,为数组元素分配空间,为stuObjArrs[i]赋值
                System.out.println(stuObjArrs[i]); //打印数组元素在堆区的地址
            }
        }
    }
    
    class StuObjArr{
        int id;
        String name;
    }
    

    运行结果:

    com.zhangqu.java.method.StuObjArr@2a70a3d8
    com.zhangqu.java.method.StuObjArr@289d1c02
    

    最后附上一张数据类型图,基础真的很重要。
    引用类型的变量,只能存储两类值,null或地址值(含变量的数据类型)
    Java数据类型

    展开全文
  • java对象赋值的方式

    千次阅读 2021-02-12 09:20:54
    了解对对象赋值的方式2.了解静态方法和实例方法3.了解成员变量的作用域4.了解自定义构造方法和构造方法技术:1.对对象赋值的方式:a.对于public修饰的属性使用对象直接使用b.对于private修饰的属性不能直接使用只能...

    目的:

    1.了解对对象赋值的方式

    2.了解静态方法和实例方法

    3.了解成员变量的作用域

    4.了解自定义构造方法和构造方法

    技术:

    1.对对象赋值的方式:

    a.对于public修饰的属性使用对象直接使用

    b.对于private修饰的属性不能直接使用只能间接作用

    2.setter和getter方法

    给外部一个set方法通过这个方法间接给对象赋值,可以对外部给的值进行控制

    给外部一个get方法访问某个变量的值

    注意:建议将setter/getter方法定义在类的最后面避免干扰

    3.构造方法:创建一个类的一个对象的时候就会被调用的方法,在对象创建的时候 需要给属性赋值

    4.方法重载:同一个类型里面有多个同名的时候 就会被调用的方法但是返回值或者参数不同

    5.成员变量的作用域 :从对象创建到对象被销毁

    6.使用静态方法的类型:

    a.工厂模式 factory

    b.当不需要记录数据只关心功能就可以使用静态方法

    c.注意:静态方法不能调用外部非静态属性和方法

    技术的使用:

    1.类的默认构造方法:

    bc5e449448ff

    2.类的自定义构造方法:

    bc5e449448ff

    3.对象方法和静态方法

    bc5e449448ff

    心得:

    昨晚上布置的那个程序还是没有写出来就是感觉写到后面去了好多东西都不知道该怎么使用,刚开始的时候想的是定义一个二维数组来写可是写到后面去了自己感觉又不可以,所以又来重新想,但是思路一致不够清晰,听了课之后再在看写的代码还是太难了,下来还是要慢慢的看了。可能有些也不一定看得懂。

    展开全文
  • java使用反射创建并操作对象的方法

    千次阅读 2021-02-25 18:17:27
    Class 对象可以获得该类里的方法(由 Method 对象表示)、构造器(由 Constructor 对象表示)、成员变量(由 Field 对象表示),这三个类都位于 java.lang.reflect 包下,实现了 java.lang.reflect.Member 接口。...

    Class 对象可以获得该类里的方法(由 Method 对象表示)、构造器(由 Constructor 对象表示)、成员变量(由 Field 对象表示),这三个类都位于 java.lang.reflect 包下,并实现了 java.lang.reflect.Member 接口。程序可以通过对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,能通过 Field 对象直接访问并修改对象的成员变量值。

    创建对象

    通过反射来生成对象需要先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。

    在很多 Java EE 框架中都需要根据配置文件信息来创建 Java 对象,从配置文件读取的只是某个类的字符串类名,程序需要根据该字符串来创建对应的实例,就必须使用反射。

    下面程序就实现了一个简单的对象池,该对象池会根据配置文件读取 key-value 对,然后创建这些对象,并将这些对象放入一个 HashMap 中。

    public class ObjectPoolFactory {

    // 定义一个对象池,前面是对象名,后面是实际对象

    private Map objectPool = new HashMap<>();

    // 定义一个创建对象的方法

    // 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象

    private Object createObject(String clazzName) throws Exception, IllegalAccessException, ClassNotFoundException {

    // 根据字符串来获取对应的Class对象

    Class> clazz = Class.forName(clazzName);

    // 使用clazz对应类的默认构造器创建实例

    return clazz.getConstructor().newInstance();

    }

    // 该方法根据指定文件来初始化对象池

    // 它会根据配置文件来创建对象

    public void initPool(String fileName)

    throws InstantiationException, IllegalAccessException, ClassNotFoundException {

    try (FileInputStream fis = new FileInputStream(fileName)) {

    Properties props = new Properties();

    props.load(fis);

    for (String name : props.stringPropertyNames()) {

    // 每取出一对key-value对,就根据value创建一个对象

    // 调用createObject()创建对象,并将对象添加到对象池中

    objectPool.put(name, createObject(props.getProperty(name)));

    }

    } catch (Exception ex) {

    System.out.println("读取" + fileName + "异常");

    }

    }

    public Object getObject(String name) {

    // 从objectPool中取出指定name对应的对象

    return objectPool.get(name);

    }

    public static void main(String[] args) throws Exception {

    ObjectPoolFactory pf = new ObjectPoolFactory();

    pf.initPool("obj.txt");

    System.out.println(pf.getObject("a")); // ①

    System.out.println(pf.getObject("b")); // ②

    }

    }

    上面程序中 createObject() 方法里的两行粗体字代码就是根据字符串来创建 Java 对象的关键代码,程序调用 Class 对象的 newInstance() 方法即可创建一个 Java 对象。程序中的 initPool() 方法会读取属性文件,对属性文件中每个 key-value 对创建一个 Java 对象,其中 value 是该 Java 对象的实现类,而 key 是该 Java 对象放入对象池中的名字。为该程序提供如下属性配置文件。

    a=java.util.Date

    b=javax.swing.JFrame

    编译、运行上面的 ObjectPoolFactory 程序,执行到 main 方法中的①号代码处,将看到输出系统当前时间——这表明对象池中已经有了一个名为a的对象,该对象是一个 java.util.Date 对象。执行到②号代码处,将看到输出一个 JFrame 对象。

    提示:这种使用配置文件来配置对象,然后由程序根据配置文件来创建对象的方式非常有用,大名鼎鼎的 Spring 框架就采用这种方式大大简化了 Java EE 应用的开发。当然,Spring 采用的是 XML 配置文件——毕竟属性文件能配置的信息太有限了,而 XML 配置文件能配置的信息就丰富多。

    如果不想利用默认构造器来创建 Java 对象,而想利用指定的构造器来创建 Java 对象,则需要利用 Constructor 对象,每个 Constructor 对应一个构造器。为了利用指定的构造器来创建 Java 对象,需要如下三个步骤。

    获取该类的 Class 对象。

    利用 Class 对象的 getConstructor() 方法来获取指定的构造器。

    调用 Constructor 的 newInstance() 方法来创建 Java 对象。

    下面程序利用反射来创建一个 JFrame 对象,而且使用指定的构造器。

    public class CreateJFrame {

    public static void main(String[] args) throws Exception {

    // 获取JFrame对应的Class对象

    Class> jframeClazz = Class.forName("javax.swing.JFrame");

    // 获取JFrame中带一个字符串参数的构造器

    Constructor ctor = jframeClazz.getConstructor(String.class);

    // 调用Constructor的newInstance方法创建对象

    Object obj = ctor.newInstance("测试窗口");

    // 输出JFrame对象

    System.out.println(obj);

    }

    }

    上面程序中第一行粗休字代码用于获取 JFrame 类的指定构造器,前面已经提到:如果要唯一地确定某类中的构造器,只要指定构造器的形参列表即可。第一行粗体字代码获取构造器时传入了一个 String 类型,即表明想获取只有一个字符串参数的构造器。

    程序中第二行粗体字代码使用指定构造器的 newInstance() 方法来创建一个 Java 对象,当调用 Constructor 对象的 newInstance() 方法时通常需要传入参数,因为调用 Constructor 的 newInstance() 方法实际上等于调用它对应的构造器,传给 newInstance() 方法的参数将作为对应构造器的参数。

    对于上面的 CreateFrame.java 中已知 java.swing.JFrame 类的情形,通常没有必要使用反射来创建该对象,毕竟通过反射创建对象时性能要稍低一些。实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射,通常在开发通用性比较广的框架、基础平台时可能会大量使用反射。

    调用方法

    当获得某个类对应的 Class 对象后,就可以通过该 Class 对象的 getMethods() 方法或者 getMethod()方法来获取全部方法或指定方法——这两个方法的返回值是 Method 数组,或者 Method 对象。

    每个 Method 对象对应一个方法,获得 Method 对象后,程序就可通过该 Method 来调用它对应的方法。在 Method 里包含一个 Invoke() 方法,该方法的签名如下。

    Object invoke(Object obj, Object...args):该方法中的 obj 是执行该方法的主调,后面的 args 是执行该方法时传入该方法的实参。

    下面程序对前面的对象池工厂进行加强,允许在配置文件中增加配置对象的成员变量的值,对象池工厂会读取为该对象配置的成员变量值,并利用该对象对应的 setter 方法设置成员变量的值。

    public class ExtendedObjectPoolFactory {

    // 定义一个对象池,前面是对象名,后面是实际对象

    private Map objectPool = new HashMap<>();

    private Properties config = new Properties();

    // 从指定属性文件中初始化Properties对象

    public void init(String fileName) {

    try (FileInputStream fis = new FileInputStream(fileName)) {

    config.load(fis);

    } catch (IOException ex) {

    System.out.println("读取" + fileName + "异常");

    }

    }

    // 定义一个创建对象的方法

    // 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象

    private Object createObject(String clazzName) throws Exception {

    // 根据字符串来获取对应的Class对象

    Class> clazz = Class.forName(clazzName);

    // 使用clazz对应类的默认构造器创建实例

    return clazz.getConstructor().newInstance();

    }

    // 该方法根据指定文件来初始化对象池

    // 它会根据配置文件来创建对象

    public void initPool() throws Exception {

    for (String name : config.stringPropertyNames()) {

    // 每取出一个key-value对,如果key中不包含百分号(%)

    // 这就表明是根据value来创建一个对象

    // 调用createObject创建对象,并将对象添加到对象池中

    if (!name.contains("%")) {

    objectPool.put(name, createObject(config.getProperty(name)));

    }

    }

    }

    // 该方法将会根据属性文件来调用指定对象的setter方法

    public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

    for (String name : config.stringPropertyNames()) {

    // 每取出一对key-value对,如果key中包含百分号(%)

    // 即可认为该key用于控制调用对象的setter方法设置值

    // %前半为对象名字,后半控制setter方法名

    if (name.contains("%")) {

    // 将配置文件中的key按%分割

    String[] objAndProp = name.split("%");

    // 取出调用setter方法的参数值

    Object target = getObject(objAndProp[0]);

    // 获取setter方法名:set + "首字母大写" + 剩下部分

    String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1);

    // 通过target的getClass()获取它的实现类所对应的Class对象

    Class> targetClass = target.getClass();

    // 获取希望调用的setter方法

    Method mtd = targetClass.getMethod(mtdName, String.class);

    // 通过Method的invoke方法执行setter方法

    // 将config.getProperty(name)的值作为调用setter方法的参数

    mtd.invoke(target, config.getProperty(name));

    }

    }

    }

    public Object getObject(String name) {

    // 从objectPool中取出指定name对应的对象

    return objectPool.get(name);

    }

    public static void main(String[] args) throws Exception {

    ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();

    epf.init("extObj.txt");

    epf.initPool();

    epf.initProperty();

    System.out.println(epf.getObject("a"));

    }

    }

    上面程序中 initProperty() 方法里的第一行粗体字代码获取目标类中包含一个 String 参数的 setter 方法,第二行粗体字代码通过调用 Method 的 invoke() 方法来执行该 setter 方法,该方法执行完成后,就相当于执行了目标对象的 setter 方法。为上面程序提供如下配置文件。

    a=javax.swing.JFrame

    b=javax.swing.JLabel

    #set the title of a

    a%title=Test Title

    上面配置文件中的 a%title 行表明希望调用a对象的 setTitle() 方法,调用该方法的参数值为 Test Title。编译、运行上面的 ExtendedObjectPoolFactory.java 程序,可以看到输出一个 JFrame 窗口,该窗口的标题为 Test Title。

    提示:Spring 框架就是通过这种方式将成员变量值以及依赖对象等都放在配置文件中进行管理的,从而实现了较好的解耦。这也是 Spring 框架的 IOC 的秘密。

    当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的 private 方法,则可以先调用 Method 对象的如下方法。

    setAccessible(boolean flag):将 Method 对象的 accessible 设置为指定的布尔值。值为true,指示该 Method 在使用时应该取消 Java 语言的访问权限检查:值为false,则指示该 Method 在使用时要实施 Java 语言的访问权限检查。

    注意:实际上,setAccessible() 方法并不属于 Method,而是属于它的父类 AccessibleObject。因此 Method、Constructor、Field 都可调用该方法,从而实现通过反射来调用 private 方法、private 构造器和成员变量,下一节将会让读者看到这种示例。也就是说,它们可以通过调用该方法来取消访问权限检查,通过反射即可访问 private 成员。

    访问成员变量值

    通过 Class 对象的 getFields() 或 getField() 方法可以获取该类所包括的全部成员变量或指定成员变量。Field 提供了如下两组方法来读取或设置成员变量值。

    getXxx(Object obj):获取 obj 对象的该成员变量的值。此处的 Xxx 对应8种基本类型,如果该成员变量的类型是引用类型,则取消 get 后面的Xxx。

    setXxx(Object obj, Xxx val):将 obj 对象的该成员变量设置成值。此处的 Xxx 对应8种基本类型,如果该成员变量的类型是引用类型,则取消 set 后面的Xxx。

    使用这两个方法可以随意地访问指定对象的所有成员变量,包括 private 修饰的成员变量。

    class Person {

    private String name;

    private int age;

    public String toString() {

    return "Person[name:" + name + " , age:" + age + " ]";

    }

    }

    public class FieldTest {

    public static void main(String[] args) throws Exception {

    // 创建一个Person对象

    Person p = new Person();

    // 获取Person类对应的Class对象

    Class personClazz = Person.class;

    // 获取Person的名为name的成员变量

    // 使用getDeclaredField()方法表明可获取各种访问控制符的成员变量

    Field nameField = personClazz.getDeclaredField("name");

    // 设置通过反射访问该成员变量时取消访问权限检查

    nameField.setAccessible(true);

    // 调用set()方法为p对象的name成员变量设置值

    nameField.set(p, "Yeeku.H.Lee");

    // 获取Person类名为age的成员变量

    Field ageField = personClazz.getDeclaredField("age");

    // 设置通过反射访问该成员变量时取消访问权限检查

    ageField.setAccessible(true);

    // 调用setInt()方法为p对象的age成员变量设置值

    ageField.setInt(p, 30);

    System.out.println(p);

    }

    }

    上面程序中先定义了一个 Person 类,该类里包含两个 private 成员变量:name 和 age,在通常情况下,这两个成员变量只能在 Person 类里访问。但本程序 FieldTest 的 main() 方法中6行粗体字代码通过反射修改了 Person 对象的 name、age 两个成员变量的值。

    第一行粗体字代码使用 getDeclaredField() 方法获取了名为 name 的成员变量,注意此处不是使用 getField()方法,因为 getField() 方法只能获取 public 访问控制的成员变量,而 getDeclaredField() 方法则可以获取所有的成员变量;第二行粗体字代码则通过反射访问该成员变量时不受访问权限的控制;第三行粗体字代码修改了 Person 对象的 name 成员变量的值。修改 Person 对象的 age 成员变量的值的方式与此完全相同。

    编译、运行上面程序,会看到如下输出:

    Person[name:Yeeku.H.Lee , age:30 ]

    操作数组

    在 java.lang.reflect 包下还提供了一个 Array 类,Array 对象可以代表所有的数组。程序可以通过使用 Array 来动态地创建数组,操作数组元素等。

    Array 提供了如下几类方法。

    static Object newInstance(Class> componentType,int...length):创建一个具有指定的元素类型、指定维度的新数组。

    static xxx getXxx(Object array, int index):返回 array 数组中第 index 个元素。其中是各种基本数据类型,如果数组元素是引用类型,则该方法变为 get(Object array, int index)。

    static void setXxx(Object array, int index, xxx val):将 array 数组中第 index 个元素的值设为 val。其中 xxx 是各种基本数据类型,如果数组元素是引用类型,则该方法变成 set(Object array, int index, Object val)。

    下面程序示范了如何使用 Array 来生成数组,为指定数组元素赋值,并获取指定数组元素的方式。

    public class ArrayTest1 {

    public static void main(String args[]) {

    try {

    // 创建一个元素类型为String ,长度为10的数组

    Object arr = Array.newInstance(String.class, 10);

    // 依次为arr数组中index为5、6的元素赋值

    Array.set(arr, 5, "疯狂Java讲义");

    Array.set(arr, 6, "轻量级Java EE企业应用实战");

    // 依次取出arr数组中index为5、6的元素的值

    Object book1 = Array.get(arr, 5);

    Object book2 = Array.get(arr, 6);

    // 输出arr数组中index为5、6的元素

    System.out.println(book1);

    System.out.println(book2);

    } catch (Throwable e) {

    System.err.println(e);

    }

    }

    }

    上面程序中三行粗体字代码分别是通过 Array 创建数组,为数组元素设置值,访问数组元素的值的示例代码,程序通过使用 Array 就可以动态地创建并操作数组。

    下面程序比上面程序稍微复杂一点,下面程序使用 Array 类创建了一个三维数组。

    public class ArrayTest2 {

    public static void main(String args[]) {

    /*

    * 创建一个三维数组。 根据前面介绍数组时讲的:三维数组也是一维数组, 是数组元素是二维数组的一维数组,

    * 因此可以认为arr是长度为3的一维数组

    */

    Object arr = Array.newInstance(String.class, 3, 4, 10);

    // 获取arr数组中index为2的元素,该元素应该是二维数组

    Object arrObj = Array.get(arr, 2);

    // 使用Array为二维数组的数组元素赋值。二维数组的数组元素是一维数组,

    // 所以传入Array的set()方法的第三个参数是一维数组。

    Array.set(arrObj, 2, new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战" });

    // 获取arrObj数组中index为3的元素,该元素应该是一维数组。

    Object anArr = Array.get(arrObj, 3);

    Array.set(anArr, 8, "疯狂Android讲义");

    // 将arr强制类型转换为三维数组

    String[][][] cast = (String[][][]) arr;

    // 获取cast三维数组中指定元素的值

    System.out.println(cast[2][3][8]);

    System.out.println(cast[2][2][0]);

    System.out.println(cast[2][2][1]);

    }

    }

    上面程序的第一行粗体字代码使用 Array 创建了一个三维数组,程序中较难理解的地方是第二段粗体字代码部分,使用 Array 为 arrObj 的指定元素赋值,相当于为二维数组的元素赋值。由于二维数组的元素是一维数组,所以程序传入的参数是一个一维数组对象。

    运行上面程序,将看到 cast[2][3][8]、cast[2][2][0]、cast[2][2][1] 元素都有值,这些值就是刚才程序通过反射传入的数组元素值。

    以上就是java使用反射创建并操作对象的方法的详细内容,更多关于JAVA 反射创建并操作对象的资料请关注脚本之家其它相关文章!

    展开全文
  • import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectPO { private String h1; private String h2; public String getH1() { return h1; } public...
  • java中对象给对象赋值

    千次阅读 2021-03-16 20:02:44
    java中对象给对象赋值 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行...
  • 最近对将子类对象赋值给父类对象有点心得,想和大家分享一下.但本人水平有限,请各位指正和批评. 言归正传,下面是几个小例子,请大家看一看. 测试一 父类:  public class Supclass { public void print() { ...
  • Java当中,若希望在创建数组的同时给数组赋值很简单,可以想下面这样: int[] num = {1,2,3}; String strs = {"a", "b", "c"} 但是创建一个List、Set、Map的同时该如何进行赋值操作呢? 2、解决方法: 1...
  • 问题简介在Java当中,若希望在创建数组的同时给数组赋值很简单,可以想下面这样:int[] num = {1,2,3};String strs = {"a", "b", "c"}但是,如果我们创建List集合,或者Map集合时,也想快速地为它赋初始值,应当如何...
  • java对象赋值给另一个对象

    千次阅读 2021-03-05 19:56:29
    对基本数据类型的赋值很简单的。基本类型存储了实际的数值,而并非指向一个对象的引用,所以在赋值的时候,是...所以倘若“将一个对象赋值给另一个对象“的时候,实际上是将“引用”从一个地方复制到另一个地方。这...
  • java 子类对象赋值给父类对象的使用,包括代码及详解,个人笔记
  • 下面小编就为大家带来一篇Java多态和实现接口的类的对象赋值给接口引用的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Java对象赋值与引用

    万次阅读 多人点赞 2017-07-28 19:52:34
    Java对象赋值与引用详解
  • java根据属性名动态生成对象并赋值

    千次阅读 2020-08-11 08:48:31
    /** * 实体工具类 */ public class GenerateObjectUtil { /** * 根据属性动态生成对象并赋值 * * 注意:返回生成的对象属性,均在设置属性前加入前缀$cglib_prop_,例如$cglib_prop_userId * * @param property...
  • Java创建数组的方法大致有三种说明:这里以int为数据类型以arr为数组名来演示一、声明并赋值int[] arr = {1,2,4, …};注意这里的花括号不是语句块,而且而且花括号后的分号也不能省二、声明数组名开辟空间并且赋值...
  • 经常在java程序员面试的过程中,面试官经常会问到下面这个问题:String strA = "abc"; String StrB = new String("... 创建了一个对象StrA并且把“abc”在内存中的地址赋值给了对象StrA,所以这个...
  • Java 创建 List 的时候直接赋值

    千次阅读 2020-08-18 16:23:47
    构造代码块:类中{}直接括起来的语句,每次创建对象都会被调用,先于构造函数执行。 静态代码块:类中static{}括起来的语句,只执行一次,先于构造代码块块执行。 同步代码块:类中synchronized(){}括起来的语句...
  • 其实这个问题可以当成声明变量要不要赋初始值的问题,不管这个变量是基础类型还是引用类型,只是基础类型不能赋值NULL。 这里要分两种清况,成员变量或者局部变量。给一个代码: class People{ private String ...
  • Java中怎样新建一个list给其赋值

    千次阅读 2020-08-19 08:12:45
    Java中新建对象并赋值的方法一般是 int a = 1; String b="公众号:霸道的程序猿"; 即使是数组,在新建时并赋值 int[] x={1,2,3,4}; 如果想在新建一个list并赋值要怎么办。 注: 博客:...
  • JAVA对象赋值与引用

    万次阅读 多人点赞 2016-08-23 16:41:48
    前言最近在学习红黑树,当我尝试不使用递归来实现时,发现自己的...@Intopass的知乎回答 java到底是值传递还是引用传递 @focusChen的博客 JAVA 对象引用,以及对象赋值 一番折腾先找个对象过年class Node { int value;
  • Java中声明一个对象并赋值NULL或者只声明不赋值 其实这个问题可以当成声明变量要不要赋初始值的问题,不管这个变量是基础类型还是引用类型,只是基础类型不能赋值NULL。 这里要分两种清况,成员变量或者局部变量。...
  • 总结一下今天所学,我们在学习java的时候,总会多多少少的听到过反射机制,但是我们好像听过之后就过去了,从来没去了解过它,然后平时做东西,也没有用到过。久而久之就慢慢给淡忘了。有时候面试的时候会被问道,你...
  • 关于Java对象数组赋值的问题

    千次阅读 2019-12-18 12:18:49
    在写Java大作业时需要对对象数组赋值,而我的程序在赋值完后,我发现对象数组中的每个对象都相同。 定义对象数组 读取文件中的数据,设置一个临时变量stu储存每一个对象的信息并赋值 上诉代码每次赋玩值后都输出第...
  • java动态给对象添加属性并赋值

    千次阅读 2019-12-10 22:57:28
    Maven引用: <!... <groupId>cglib <artifactId>cglib ...java动态给对象添加属性并赋值 扩展: 深入学习java源码之Introspector.getBeanInfo()与Introspector.getPropertyDescriptors()
  • java声明对象数组

    千次阅读 2021-03-15 03:45:06
    java如何对象创建数组初始化急夜影驱动编程小编今天和大家分享答案/** * 功能:声明一个学生类 * @author qqliweng * 时间:2009-10-22 * 版本:V1.0 */ public class Student { private String name; //学员姓名 ...
  • 在实例化一个对象时,JVM首先会检查相关类型是否已经加载初始化,如果没有,则JVM立即进行加载调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM...
  • 一、在maven工程中引入fastjson.jar包com.alibabafastjson1.1.41二、创建实体对象packagejson;publicclassUser{publicStringname;publicStringage;publicStringsex;publicStringgetName(){returnname;}...
  • java -新建list链表并赋值的方法总结

    千次阅读 2020-12-15 10:55:00
    java -新建list链表并赋值的方法总结1、直接初始化并赋值1.1 初始化的同时赋值1.2 先初始化后赋值2、将一个list赋值给另一个list2.1 对象引用的方式赋值2.2 非对象引用的方式赋值 1、直接初始化并赋值 1.1 初始化的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 424,305
精华内容 169,722
关键字:

java创建对象并赋值

java 订阅
友情链接: CPP.rar