• JAVA基础知识之JVM-——使用反射生成并操作对象


    Class对象可以获取类里的方法,由Method对象表示,调用Method的invoke可以执行对应的方法;可以获取构造器,由Constructor对象表示,调用Constructor对象的newInstance方法可以执行类对应的构造方法;可以获取成员变量,由Field对象表示,通过Field对象可以直接修改类的成员变量的访问权限和值。

    创建对象

    通过反射有两种方式创建对象

    使用Class对象的newInstance(),这是最常用的方式,根据配置文件信息创建对象。

    使用Class对象获取指定的Constructor对象,再由Constructor对象的newInstance创建类的对象。

    调用方法

    首先要获取类在JVM中对应的Class对象,通过类的Class对象的getMethod方法来获取类的方法,返回Method类型的对象,最后通过Method对象的invoke来执行类的方法。

    下面的例子将演示从配置文件中读取数据来动态地创建对象,还通过配置文件信息来执行对象的setter来初始化对象。

     1 package jvmTest;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.IOException;
     5 import java.lang.reflect.Method;
     6 import java.util.HashMap;
     7 import java.util.Map;
     8 import java.util.Properties;
     9 
    10 public class ExtendedObjectPoolFactory {
    11     private Map<String, Object> objectPool = new HashMap<>();
    12     private Properties config = new Properties();
    13     public void init(String fileName) {
    14         try {
    15             FileInputStream fis = new FileInputStream(fileName);
    16             config.load(fis);
    17         } catch (IOException ex) {
    18             System.out.println("读取 " + fileName + " 异常");
    19             ex.printStackTrace();
    20         }
    21     }
    22     private Object createObject(String clazzName) throws Exception {
    23         Class<?> clazz = Class.forName(clazzName);
    24         //使用clazz默认构造器创建对象
    25         return clazz.newInstance();
    26     }
    27     //根据配置文件创建对象
    28     public void initPool() throws Exception {
    29         for (String name : config.stringPropertyNames()) {
    30             if (!name.contains("%")) {
    31                 objectPool.put(name, createObject(config.getProperty(name)));
    32             }
    33         }
    34     }
    35     
    36     //根据配置文件来执行对象对应的set方法
    37     public void initProperty() throws Exception {
    38         for (String name : config.stringPropertyNames()) {
    39             if (name.contains("%")) {
    40                 String[] objAndPro = name.split("%");
    41                 //获取对象
    42                 Object target = getObject(objAndPro[0]);
    43                 //构造setter方法名
    44                 String mtdName = "set"+ objAndPro[1].substring(0,1).toUpperCase() + objAndPro[1].substring(1);
    45                 Class<?> targetClass = target.getClass();
    46                 Method mtd = targetClass.getMethod(mtdName, String.class);
    47                 mtd.invoke(target, config.getProperty(name));
    48             }
    49         }
    50     }
    51     
    52     public Object getObject(String name) {
    53         return objectPool.get(name);
    54     }
    55     
    56     public static void main(String[] args) throws Exception {
    57         ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
    58         epf.init("extObj.txt");
    59         epf.initPool();
    60         epf.initProperty();
    61         System.out.println(epf.getObject("a"));
    62     }
    63 }

    上面程序第23行获取Class对象后在第25行使用默认的构造器动态创建出了对象, 

    如果要使用指定构造器,需要先获取Constructor对象,再使用Cronstructor对象的newInstance创建对象,像下面这样,

    1 Constructor ctor = clazz.getConstructor(String.class);
    2 return ctor.newInstance("here is parameter for specific constructor");

    第45行获取Class对象后,在46行通过Class对象获取指定要执行的方法(带一个参数),用Method对象表示,最后在47行调用Method对象的invoke可以执行类的指定方法

    上面的例子使用下面的测试文件进行测试,

    extObj.txt

    1 a=javax.swing.JFrame
    2 b=javax.swing.JLabel
    3 a%title=Test title

    输入结果,

    javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=Test title,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
    View Code

     访问成员变量

    通过Class对象的getField方法可以访问指定的成员变量,调用继承自AccessableObject的setAccessable方法可以获得private成员变量的访问权限。

    以下方法可以访问成员变量,

    get(Object obj), 获取成员变量值。  如果是基本类型,直接使用类似 getInt(Object obj),  getChar(Object obj)的方式

    set(Object obj), 设置成员变量值。 如果是基本类型,直接使用类似 setInt(Object, int val), setChar(Object obj, char c)的方式

    下面演示访问成员变量,

     1 package jvmTest;
     2 
     3 import java.lang.reflect.Field;
     4 
     5 class Person {
     6     private String name;
     7     private int age;
     8     public String toString() {
     9         return "Person[name: "+name+", age: "+age+"]";
    10     }
    11 }
    12 public class FieldTest {
    13     public static void main(String[] args) throws Exception {
    14         Person p = new Person();
    15         Class<Person> clazz = Person.class;
    16         //getDeclaredFiled可以获取所有访问权限的成员变量
    17         Field nameField = clazz.getDeclaredField("name");
    18         //true表示取消权限限制
    19         nameField.setAccessible(true);
    20         nameField.set(p, "Tom");
    21         Field ageField = clazz.getDeclaredField("age");
    22         ageField.setAccessible(true);
    23         ageField.setInt(p, 18);
    24         System.out.println(p);
    25     }
    26 }

    上面程序第20行使用的是set(), 而23行使用的是setInt(), 输出结果,

    Person[name: Tom, age: 18]

    操作数组

    java.lang.reflect还包含一个Array类,可以动态地创建数组,设置元素的值,比较强大,主要方法有,

    newInstance(..),创建数组,可以指定数组元素类型, 数组维度, 数组长度

    get(...), 获取索引为index的元素,对于基本数据类型的数组,方法为getInt(...),  getChar(...) ...

    set(...), 设置索引为index的元素,对于基本数据类型的数组,方法为setInt(...),  setChar(...) ...

    下面演示用法,

     1 package jvmTest;
     2 
     3 import java.lang.reflect.Array;
     4 
     5 public class ArrayTest2 {
     6     public static void main(String[] args) {
     7         // 创建三维数组
     8         Object arr = Array.newInstance(String.class, 3, 4, 10);
     9         // 获取index为2的元素,该元素是一个二维数组
    10         Object arrObj = Array.get(arr, 2);
    11         // 给二维数组index为2的元素赋值
    12         // 二维数组的元素是一维数组,所以赋值也要用数组来赋值
    13         Array.set(arrObj, 2, new String[] { "天王盖地虎", "宝塔镇河妖" });
    14         //获取二维数组指定index的元素(结果是一维数组)
    15         Object anArr = Array.get(arrObj, 3);
    16         Array.set(anArr, 8, "野鸡闷头钻,哪能上天王山");
    17         //将arr强制转换为三维数组
    18         String[][][] cast = (String[][][])arr;
    19         
    20         System.out.println(cast[2][2][0]);
    21         System.out.println(cast[2][2][1]);
    22         System.out.println(cast[2][3][8]);
    23     }
    24 }

    输出如下,

    1 天王盖地虎
    2 宝塔镇河妖
    3 野鸡闷头钻,哪能上天王山
  • 相关阅读:
    第一节 Spring的环境搭建
    002. 配置环境变量
    001. 巧妇难为无米之炊之安装环境
    第七节 认识SpringMVC中的表单标签
    [六字真言]6.吽.SpringMVC中上传大小异常填坑
    [六字真言]4.叭.SpringMVC异常痛苦
    [六字真言]5.咪.功力不足,学习前端JavaScript异常
    [六字真言]3.呢.异常的谎言,你要相信多少次?
    [六字真言]2.嘛.异常定制和通用.md
    vim基本技巧
  • 原文地址:https://www.cnblogs.com/fysola/p/6107712.html
Copyright © 2020-2023  润新知