• java的反射机制


    一、分析

    比如:Person有什么那?姓名,身高,睡觉行为等;

    Class用于描述java的类,那有什么那?类名,所属包名,成员变量,成员方法等;

    Class类没有构造方法,它对应的是个各类的字节码文件,也就是.class文件,

    同理就是说,每一个类的字节码,都是Class类的实例对象;

    Person的字节码是Class类的实例对象;Person.class就表示Person在内存中的字节码

    Date的字节码同样也是Class类的实例对象;Date.class

    一》得到各个字节码对应的实例对象(Class类型)三种方法

    1.类名.class;如:System.class

    2.对象.getClass();如:new Date().getClass();前提必须由对象名;

    3.Class.forName(“类名”);如;Class.forName(“java.util.Date”);

    Class的静态方法forName();指定类的完整名称;

    返回字节码文件。

    反射用的通常是这种方式:因为写源程序的时候还不知道类的名字;

    通常用一个变量表示;

    二》有九个预定义的class对象,8个基本数据类型,1void.class

    三》

    String s = "abc";

    Class cls1 = s.getClass();

    Class cls2 = String.class;

    Class cls3 = Class.forName("java.lang.String");

    //因为内存中只有一份字节码,所以不管用那种方式创建,都相等;

    System.out.println(cls1 == cls2);

    System.out.println(cls1 == cls3);

    二、构造方法的反射应用(Constructor类):getConstructor()

    一、.得到某个类的所有构造函数:所有的构造方法会装到一个数组里面;

    Constructor[]  constructors = Class.forName(“java.lang.String”).getConstructors();

    二、getConstructor()得到某一个构造方法,构造方法那么多,得到的具体是哪一个那?

    就要根据参数类型,参数类型用class对象表示

    Constructor cons1 = 

    String.class.getConstructor(StringBuffer.class ,int.class);

    就是表示得到String类中,参数为StringBufferint的构造方法;

    三、Constructor类中有一个newInstance()方法,根据构造方法创建实例对象;

    String str = (String)cons1.newInstance(new StringBuffer("abc"));

    四、上面是利用String有参数的构造函数创建对象,其实Class类给我们提供了一个创建对象的方法newInstance(),但是这个方法只能调用无参数的构造方法,比较方便,

    String s=StringClass.forName(“java.lang.String”).newInstance();

    同样也创建了一个String对象;

    三、成员变量的反射(Field)getField()

    public class ReflectPoint {
         private int x;
         public int y;
         public ReflectPoint(int x, int y) {
             super();
             this.x = x;
             this.y = y;
         }
    }

      ReflectPoint pt1 = new ReflectPoint(3,5);
      //利用反射得到成员Y,用Filed类,先得到该类的字节码。
      //然后得到字节码中的成员变量。
      Field fieldY = pt1.getClass().getField("y");
      //fieldY的值是多少那?5,错,fieldY对应的是该类的字节码中变量y
      //fieldY不是对象身上的变量,而是类上,
      //要用它去取某个对象上的值;
      System.out.println(fieldY);
      System.out.println(fieldY.get(pt1));
      //获取x变量,因为x是私有的,java中定义了专用的方法
      //反射获取私有变量getDeclaredField();
      Field fieldX = pt1.getClass().getDeclaredField("x");
      //前面已经获取,下面设置访问私有变量;
      //强制访问,setAccessible(true)不能省略,否则运行失败,不让访问私有变量
      fieldX.setAccessible(true);
      System.out.println(fieldX.get(pt1));

    ..应用一下:

    //将任意个对象中的所有String类型的成员变量所对应
      //的字符串内容的“b”替换“a”;

    public static void changeStringValue(Object obj)throws Exception
     {
          //获取字节码中所有的成员变量
           Field[] fields = obj.getClass().getFields();
          for(Field field : fields)
         {
              //遍历是否是String类型变量
              //这里应该用==号,而不是equals,因为只有一份String字节码,他们用的都是同一分
            if(field.getType() == String.class)
           {
                String oldValue = (String)field.get(obj);
                //将b替换成a
               String newValue = oldValue.replace("b", "a");
              //把新值set给对象
               field.set(obj, newValue);
          }
        }
     }

    public class ReflectPoint {

          public String str1 = "ball";
          public String str2 = "basketball";
          public String str3 = "itcast";

         @Override
          public String toString()
         {
             return str1+"::"+str2+"::"+str3;
         }

    }

    四、成员方法的反射(Method类)getMethod();

     

    一》反射成员方法格式:

    Method method = String.Class.getMethod(“charAt”,parameterType);

    Method类  反射名 字节码.getMethod(“方法名”,参数列表);

    注意:参数类型用class对象表示;

    二》通过反射方法得到字节码里面的方法,再拿着方法作用于某个对象。

    String str = "abcd";
      //反射格式
      Method methodCharAt = String.class.getMethod("charAt", int.class);
      //调用方法invoke是方法对象的方法(methodCharAt);
      System.out.println(methodCharAt.invoke(str, 3));
      //如果对象参数为null,说明这个方法时静态的;
      //System.out.println(methodCharAt.invoke(null, 3));

    应用:调用某个类的Main方法:

    public class ReflectMain {

     /**
      * @param args
      */
     public static void main(String[] args)throws Exception {
      // TODO Auto-generated method stub
      //为什么要用反射的方式掉用main方法,直接类名.Main(new String[]{"111","222","444"});
      //因为我们可能不知道用户给我们传递的是那个类的main方法
      //假设args的第一个元素就是那个类名;
      String startClassName = args[0];
      Method mainMethod = Class.forName(startClassName).getMethod("main", String[].class);
      //因为main是static,不通过对象调用,所一null
      mainMethod.invoke(null,(Object) new String[]{"111","222"});
     }

    }
    class TestArguments
    {
     public static void main(String[] args)
     {
      for(String arg : args)
      {
       System.out.println(arg);
      }
     }
    }

    五、数组的反射应用

    一》数组的反射: 

    相同元素类型和相同维度(一维和二维)的两个数组的字节码类文件相等,也就是同一份字节码;

    1.数组与Object的关系

    //数组与Object的关系
     public static void arrayMethod()
     {
      int[] arr1 = new int[]{1,2,3};
      int[] arr2 = new int[4];
      int[][] arr3 = new int[2][3];
      String[] arr4 = new String[]{"a","b","c"};
      //相同元素类型和相同维度(一维和二维)的两个数组的字节码类文件相等,也就是同一份字节码;
      System.out.println(arr1.getClass() == arr2.getClass()); //true 相同元素类型,相同维度
      System.out.println(arr1.getClass() == arr3.getClass());//false 相同元素类型,不同维度
      System.out.println(arr1.getClass() == arr4.getClass());//false 不同元素类型,相同维度
      //输出当前数组名称
      System.out.println(arr1.getClass().getName());
      //输出父类名称
      System.out.println(arr1.getClass().getSuperclass().getName());//Object
      System.out.println(arr4.getClass().getSuperclass().getName()); //Object
      //所以数组的父类对象是Objec,因为数组是引用类型
      Object obj1 = arr1;
      Object obj2 = arr3;
      Object obj3 = arr4;
      //因为基本数据类型(int)不能转换Object数组
      //Object[] obj4 = arr1;
      //因为int[]是Object,而int[][] ==Object[]
      Object[] obj5 = arr3;
      //String也是Object,顾String[] == Object【】;
      Object[] obj6 = arr4;
      //数组不能直接打印,可以遍历,而list列表可以直接打印
      //Arrays工具类中asList方法将数组转换成List
      //因为asList(Object【】 obj);跟int【】不匹配,
      System.out.println(Arrays.asList(arr1));
      System.out.println(Arrays.asList(arr4));
     }

    打印数组:

    public static void arrayReflect(Object obj)
     {
      Class clazz = obj.getClass();
      //取出字节码,判断是否是数组
      if(clazz.isArray())
      {
       //得到某个数组对象的长度Array.getLength(obj)
       int len = Array.getLength(obj);
       for(int i=0;i<len;i++)
       {
        //获取数组对象的第i个元素Array.get(obj, i);
        System.out.println(Array.get(obj, i));
       }
      }
      else
      {
       System.out.println(obj);
      }
     }

  • 相关阅读:
    【转载】【贪心】各种覆盖问题
    【转载】【知识点总结】NOIP前夕 2014.11.4
    最大子图形问题
    小知识
    Tyvj——P1864 [Poetize I]守卫者的挑战
    Tyvj——P1952 Easy
    BZOJ——2134: 单选错位
    BZOJ——1620: [Usaco2008 Nov]Time Management 时间管理
    BZOJ——1622: [Usaco2008 Open]Word Power 名字的能量
    洛谷 U3357 C2-走楼梯
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3249192.html
Copyright © 2020-2023  润新知