zoukankan      html  css  js  c++  java
  • Java 反射 分析类和对象

    Java 反射 分析类和对象

    @author ixenos

    摘要:优化程序启动策略、在运行时使用反射分析类的结构和对象

    优化程序启动策略


    在启动时,包含main方法的类被加载。它会加载所有它需要的类。这些被加载的类又要加载它们需要的类,以此类推,这是JVM的动态加载机制。

    对于一个大型的应用程序,这将消耗很多时间,用户体验不好。此时可以通过反射来优化程序启动策略,要确保包含main方法的类没有显示地引用其他的类。

    首先,显式一个启动动画,然后通过调用Class.forName手动加载其他的类,预加载。

    在运行时使用反射分析类的结构


    在java.lang.reflect包中有三个类Field、Method和Constructor,分别用于描述类的域、方法和构造器。

    这三个类都有getName方法返回项目的名称

    Field类的getType方法可返回描述域所属类型的Class对象

    Method和Constructor类都有报告参数的方法,Method类还有报告返回类型的方法

    这三个类还有getModifiers方法返回整数数值,用不同的位开关描述public和static这样的修饰符的使用状况。还可以使用java.lang.reflect.Modifier类的静态方法分析getModifiers返回的整数数值,如isPublic、isFinal、isPrivate

    Class类中的getFields、getMethods和getConstructors方法返回类提供的public域、方法和构造器数组,其中包括超类的公有成员

    Class类中的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将返回类中声明的所有域、方法和构造器,不包括超类的成员

    在运行时使用反射分析对象


     从上一节知道如何查看任意对象的数据域名称和类型:获得对应的Class对象,通过Class对象调用getDeclareFields

    本节进一步查看数据域的实际内容(对对象进行分析)

    1.查看数据域的关键方法是Field类中的get方法

      如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj域的当前值

    Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989);
    //反射分析类
    Class c1 = harry..getClass();
    Field f = c1.getDeclaredField("name");
    //反射分析对象
    Object v = f.get(harry);
    View Code

      *反射机制的默认行为受限于Java的访问控,然而,如果一个Java程序没有收到安全管理器的控制,就可以覆盖访问控制

      *这需要调用Field、Method和Constructor对象的setAccessible方法,如f.setAccessible(true);

      *setAccessible是AccessibleObject类中的一个方法,是Field、Method和Constructor类的公共超类

    //以下是一个利用反射实现的对任意类型对象的toString方法

     1 Class ObjectAnalyzer
     2 {
     3     public String toString(Object obj)
     4     {
     5         Class c1 = obj.getClass();
     6         ...
     7         String r = c1.getName();
     8         //检查这个类及其所有超类的域
     9         do
    10         {
    11             r += "[";
    12             Fields[] fields = c1.getDeclaredFields();
    13             //AccessibleObject.setAccessible也有静态方法哟
    14             //使所有的域可以访问
    15             AccessibleObject.setAccessible(fields, true);
    16             //获得所有域的变量名和值
    17             for(Field f : fields)
    18             {
    19                 //静态域在运行时不变,没有分析的必要
    20                 if(!Modifier.isStatic(f.getModifiers()))
    21                 {
    22                     //表明当前类是上一次分析的类
    23                     if(!r.endsWith("[")) { r +=" ,"; }
    24                     //变量名 = ...
    25                     r += f.getName() + "=";
    26                     try
    27                     {
    28                         //get当前obj对象的f域的值
    29                         Object val = f.get(obj);
    30                         r += toString(val);
    31                     }
    32                     catch(Exception e) { e.printStackTrace(); }
    33                 }
    34             }
    35             //完成当前类,前进到基类
    36             r += "]";
    37             c1 = c1.getSuperClass();
    38         }
    39         //当没有基类时结束循环
    40         while (c1 != null);
    41         return r;
    42     }
    43     ...
    44 }
    View Code

    //使用该toSting方法示例

    public String toString(){ return new ObjectAnalyzer().toString(this); } //使用this指向当前对象

     1 import java.util.ArrayList;
     2 
     3 /**
     4  *这个程序使用反射的toString方法来查看任意对象的内部信息
     5  */
     6 
     7 public class ObjectAnalyzerTest
     8 {
     9     public class void main(String[] args)
    10     {
    11         ArrayList<Integer> squares = new ArrayList<>();
    12         for(int i = 1; i <= 5; i++)
    13         {
    14             square.add(i * i);
    15         }
    16         System.out.println(new ObjectAnalyzer().toString(squares));
    17     }
    18 }
    View Code

    ***This和super都不能在main()方法中使用,main()方法是静态的,this是本类对象的引用,静态先于对象,所以是不能使用的。

      为什么?因为静态方法是在类加载的时候执行.类刚加载可能还没有创建对象,所以就不能用this

      动态加载请看另一篇文章http://www.cnblogs.com/ixenos/p/5682088.html

    //对toString改进使其能查看数组内部,以及记录被访问过的对象来阻止循环引用

     1 package objectAnalyzer;
     2 
     3 import java.lang.reflect.AccessibleObject;
     4 import java.lang.reflect.Array;
     5 import java.lang.reflect.Field;
     6 import java.lang.reflect.Modifier;
     7 import java.util.ArrayList;
     8 
     9 public class ObjectAnalyzer
    10 {
    11    private ArrayList<Object> visited = new ArrayList<>();
    12 
    13    /**
    14     * Converts an object to a string representation that lists all fields.
    15     * @param obj an object
    16     * @return a string with the object's class name and all field names and
    17     * values
    18     */
    19    public String toString(Object obj)
    20    {
    21       if (obj == null) return "null";
    22       if (visited.contains(obj)) return "...";
    23       visited.add(obj);
    24       Class cl = obj.getClass();
    25       if (cl == String.class) return (String) obj;
    26       if (cl.isArray())
    27       {
    28          String r = cl.getComponentType() + "[]{";
    29          for (int i = 0; i < Array.getLength(obj); i++)
    30          {
    31             if (i > 0) r += ",";
    32             Object val = Array.get(obj, i);
    33             if (cl.getComponentType().isPrimitive()) r += val;
    34             else r += toString(val);
    35          }
    36          return r + "}";
    37       }
    38 
    39       String r = cl.getName();
    40       // inspect the fields of this class and all superclasses
    41       do
    42       {
    43          r += "[";
    44          Field[] fields = cl.getDeclaredFields();
    45          AccessibleObject.setAccessible(fields, true);
    46          // get the names and values of all fields
    47          for (Field f : fields)
    48          {
    49             if (!Modifier.isStatic(f.getModifiers()))
    50             {
    51                if (!r.endsWith("[")) r += ",";
    52                r += f.getName() + "=";
    53                try
    54                {
    55                   Class t = f.getType();
    56                   Object val = f.get(obj);
    57                   if (t.isPrimitive()) r += val;
    58                   else r += toString(val);
    59                }
    60                catch (Exception e)
    61                {
    62                   e.printStackTrace();
    63                }
    64             }
    65          }
    66          r += "]";
    67          cl = cl.getSuperclass();
    68       }
    69       while (cl != null);
    70 
    71       return r;
    72    }
    73 }
    View Code

     虚拟机为每个类型管理一个Class对象,因此可以用==实现两个Class对象的比较:if(e.getClass()==Employee.class)

     

    2.设置数据域的关键方法是Field类中的set方法

      调用f.set(obj, value)可以将obj对象的f域设置成新值

  • 相关阅读:
    .charAt()方法
    CustomerBiz方法运用
    面向对象_方法 判断
    方法
    查找无序数组索引
    面向对象_购票
    创建类 方法 构建类对象
    StringBuffer 方法
    docker创建redis mysql 等服务
    docker常用的命令
  • 原文地址:https://www.cnblogs.com/ixenos/p/5691314.html
Copyright © 2011-2022 走看看