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);
  }
 }

 

 

 

 

posted @ 2013-08-09 23:06  jlins  阅读(219)  评论(0编辑  收藏  举报