疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java反射原理分析


 

   

  一:Java的反射就是把Java类中的各种成分映射成相应的Java类。

  例如:一个Java类中用一个Class类的对象来表示此Java类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示。

  比如汽车是一个类,汽车中的发动机中的发动机,变速箱等一个一个个的类。

  二:一句话概括:反射就是把Java类中的各个成分映射成相应的Java类。

  三:反射的基石:Class类(Java程序中的各个Java类属于同一事物,描述这类事务的类就是Class类)

  四:表示Java类的Class类显然要提供一系列的方法来获取其中的变量,方法,构造方法,修饰符,包等信息(对应映射类Field,Method,Constructor,Package等)

  五:得到映射对象必须先得到字节码!三种方式获取字节码!

  1. 对象.getClass() Person p=new Person(); Class clazz=p.getClass(); //类的字节码已经加载到内存,只需要对象调用

  2.类加载器 Class clazz=Class.forName("java.lang.String"); //类的完整名称

  3. 类名.ClassClass clazz=Person.class;

  六:8个基本类型对应8个Class对象(boolean, byte, char, short, int, long, float, double)+void

  七:映射类

  Constructor类:代表某个类中的一个构造方法

  1.得到某个类中的所有构造方法:

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

  2.得到其中一个构造方法:

  Constructor constructor=Class.forName("java.lang.String").getConstructor("StringBuffer.Class"); //后面跟参数的字节码

  3.创建实例对象:

  通常方式:String str1=new String(new StringBuffer("abc"));

  反射方式:String str2=(String)constuctor.newInstance(new BufferString("abc"));//通过构造方法再映射的对象,参数的类型必须一致

  PS:得到构造方法Constructor的对象时必须传递参数类型,创建实例化对象时必须是相同的参数类型!!

  Class -> Constructor -> new object

  Class.newInstance()方法:

  String str=(String)Class.forName("java.lang.String").newInstance();//该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象用到了缓存机制来保存默认构造方法的实例对象。

  Field类:代表Java类中的成员变量

  Method类:代表Java类中成员方法的反射

  String str=new String("abc");

  Method m=String.class.getMethod("charAt",int.class);

  System.out.println(m.invoke(str,1);

  通过反射方式得到字符码的某个方法m,再拿着此方法作用于某个对象str,只用对象自己拿着方法调用执行。m只相当于介质,执行的还是str对象。

  PS:调用方法必须在某个对象上调,参数类型必须一致。当对象为null时,执行的是静态方法。

  八:Java测试代码

  /*

  * 测试反射的Field(Java类的成员变量)类

  */

  public class ReflectTest2 {

  private int x;

  public int y;

  public String str1 = "ball";

  public String str2 = "boll";

  public String str3 = "call";

  public ReflectTest2(int x, int y) {

  super();

  this.x = x;

  this.y = y;

  }

  public static void main(String[] args) {

  }

  @Override

  public String toString() {

  return "ReflectTest2 [x=" + x + ", y=" + y + ", str1=" + str1

  + ", str2=" + str2 + ", str3=" + str3 + "]";

  }

  }

  /*

  * 映射类

  */

  public class ReflectTest {

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

  String str = "abc";

  // ----------------字节码----------------------------------

  // 得到字节码的3种方式

  Class cls1 = str.getClass();

  Class cls2 = String.class;

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

  // PS:cls1,cls2,cls3都是同一份字节码对象.cls1=cls2=cls3

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

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

  System.out.println(cls1.isPrimitive()); // 返回false. String不是基本类型字节码。

  System.out.println(int.class == Integer.class); // 返回false

  System.out.println(int.class == Integer.TYPE); // 返回true

  // .TYPE返回其包装类型的字节码

  System.out.println(int[].class.isPrimitive()); // 返回false判断是否是基本数据类型的映射

  System.out.println(int[].class.isArray()); // 返回true 判断字节码类型是数组类型

  // -------------------------Constructor--------------------------------

  // 得到某一个构造方法: 参数类型为StringBuffer类型

  Constructor constructor = String.class.getConstructor(StringBuffer.class);

  // 通过构造方法创建实例化对象

  // 通常方式

  String str1 = new String(new StringBuffer("abc"));

  // 反射方式

  String str2 = (String) constructor.newInstance(new StringBuffer("abc"));

  System.out.println(str2.charAt(2));

  // PS:得到构造方法constructor对象时必须传递参数类型,创建实例化对象时必须是相同的参数类型

  // -----------------------Filed------------------------------------------

  ReflectTest2 rt = new ReflectTest2(2, 3);

  // 得到Java类中的成员变量

  Field fieldY = rt.getClass().getField("y");

  // fieldY不是Java对象身上的变量,而是类上,从某个Java对象中取出具体的值

  System.out.println(fieldY.get(rt));

  // 对于私有不可见变量

  Field fieldX = rt.getClass().getDeclaredField("x");

  fieldX.setAccessible(true); // 暴力反射

  System.out.println(fieldX.get(rt));

  changeValue(rt);

  System.out.println(rt);

  // --------------------------Method-----------------------------------------

  // 通过反射得到Java类方法

  Method methodCharAt = String.class.getMethod("charAt", int.class);

  // 调用方法必须在某个对象上调

  System.out.println(methodCharAt.invoke(str1, 1));

  // 反射执行类中的main方法 main方法为静态方法。传递对象可为null

  Method methodMain = Class.forName("com.test.MethodTest").getMethod(

  "main", String[].class);

  System.out.println(methodMain.invoke(null, new Object[] { new String[] {

  "aaa", "bbb" } }));

  // ---------------------------数组映射-----------------------------------------

  printObject(new String[] { "aaa", "bbb" });

  }

  // 打印数组

  private static void printObject(Object obj) {

  Class clazz = obj.getClass();

  if (clazz.isArray()) {

  int length = Array.getLength(obj);

  for (int i = 0; i < length; i++) {

  System.out.println(Array.get(obj, i));

  }

  } else {

  System.out.println(obj);

  }

  }

  // field运用:改变类型名为String类型的成员变量的值

  private static void changeValue(Object obj) throws Exception {

  Field[] fields = obj.getClass().getFields();

  for (Field field : fields) {

  // 比较字节码用"="比较 因为是同一份字节码

  if (field.getType() == String.class) {

  String oldValue = (String) field.get(obj);

  String newValue = oldValue.replace("b", "a");

  // 重新设值

  field.set(obj, newValue);

  }

  }

  }

  }

  // method运用。调用类中的main方法

  class MethodTest {

  public static void main(String[] args) {

  for (String arg : args) {

  System.out.println(arg);

  }

  }

  }