您的位置:首页 > 编程语言 > Java开发

java反射详解

2015-03-03 13:43 204 查看
反射是JDK5.0提供的java新特性,反射的出现打破了java一些常规的规则,如,私有变量不可访问。
简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。

一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语

API简介
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
–Class类:代表一个类。
–Field 类:代表类的成员变量(成员变量也称为类的属性)。
–Method类:代表类的方法。
–Constructor 类:代表类的构造方法。
–Array类:提供了动态创建数组,以及访问数组的元素的静态方法
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。
Class类是Reflection API 中的核心类,它有以下方法
–getName():获得类的完整名字。
–getFields():获得类的public类型的属性。
–getDeclaredFields():获得类的所有属性。
–getMethods():获得类的public类型的方法。
–getDeclaredMethods():获得类的所有方法。
-getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
-getConstructors():获得类的public类型的构造方法。
?getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
?newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
?(2)通过默认构造方法创建一个新对象:
?Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
?以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
?(3)获得对象的所有属性:
?Field fields[]=classType.getDeclaredFields();
?Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性
(4)Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回
(5)Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。 要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
获取某个类或某个对象所对应的Class对象的常用的3种方式:
a) 使用Class类的静态方法forName:Class.forName(“java.lang.String”);
b) 使用类的.class语法:String.class;
c) 使用对象的getClass()方法:String s = “aa”; Class<?> clazz = s.getClass();
下面写一个程序来用一下这些API吧:

//获得MethodInvoke类对应的一个clas对象

Class<?> MethodInvok=MethodInvoke.class;

//获得一个MethodInvoke类对应的对象实例

Object MethodInvo=MethodInvok.newInstance();

//获得MethodInvo对象对应的add方法对应的一个对象实例

1):Method method=MethodInvok.getMethod("add", int.class,int.class);

//调用MethodInvo对象对应的add方法对应的一个对象(MethodInvo)实例所代表的方法,并获得结果

2)Object result= method.invoke(MethodInvo, 1,2);

System.out.println(result);

System.out.println("--------------------------------------");

Method method1=MethodInvok.getMethod("print",String.class);

Object Result1=method1.invoke(MethodInvo, "tom");

System.out.println(Result1);

<span style="color:#008000;">//获得MethodInvoke类对应的一个clas对象  </span>Class<?> MethodInvok=MethodInvoke.class;  <span style="color:#008000;">//获得一个MethodInvoke类对应的对象实例  </span>Object MethodInvo=MethodInvok.newInstance();  <span style="color:#008000;">//获得MethodInvo对象对应的add方法对应的一个对象实例  </span>1):Method method=MethodInvok.getMethod("add", int.class,int.class);  <span style="color:#008000;">//调用MethodInvo对象对应的add方法对应的一个对象(MethodInvo)实例所代表的方法,并获得结果  </span>  2)Object result= method.invoke(MethodInvo, 1,2);    System.out.println(result);    System.out.println("--------------------------------------");     Method method1=MethodInvok.getMethod("print",String.class);     Object Result1=method1.invoke(MethodInvo, "tom");    System.out.println(Result1);


注:1)处的int.class,int.class可以写为new Class[]{int.class,int.class}
原因在于getMethod方法的第二个参数是一个可变参数。
2)处的1,2可以写为new int【】{1,2},原因如1);
4.若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
a) 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:
Class<?> classType = String.class;
Object obj = classType.newInstance();
b) 先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
注:
4. 若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{“hello”, 3});
代码示例:

// 该方法实现对Customer对象的拷贝操作

public Object copy(Object object) throws Exception

{

Class<?> classType = object.getClass();

//先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用

Constructor对象的newInstance()方法构造一个实例。

Object objectCopy = classType.getConstructor(new Class[] {})

.newInstance(new Object[] {});

// Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默

认和private访问级别的属性

Field[] fields = classType.getDeclaredFields();

for (Field field : fields)

{

String name = field.getName();

// 将属性的首字母转换为大写

String firstLetter = name.substring(0, 1).toUpperCase(); String getMethodName = "get" + firstLetter + name.substring(1);

String setMethodName = "set" + firstLetter + name.substring(1);

Method getMethod = classType.getMethod(getMethodName,

new Class[] {});

Method setMethod = classType.getMethod(setMethodName,

new Class[] { field.getType() });

Object value = getMethod.invoke(object, new Object[] {});

setMethod.invoke(objectCopy, new Object[] { value });

}

// 以上两行代码等价于下面一行

// Object obj2 = classType.newInstance();

// System.out.println(obj);

return objectCopy;

}

 

5.Integer.TYPE返回的是int,而Integer.class返回的是Integer类所对应的Class对象。
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法
一维数组的简单创建,设值,取值
Object array = Array.newInstance(classType, 10);
Array.set(array, 5, "hello");
String str = (String)Array.get(array, 5);
二维数组的简单创建,设值,取值

//创建一个设值数组维度的数组

int[] dims = new int[] { 5, 10, 15 };

//利用Array.newInstance创建一个数组对象,第一个参数指定数组的类型,第

二个参数设值数组的维度,下面是创建一个长宽高为:5,10,15的三维数组

Object array = Array.newInstance(Integer.TYPE, dims);

System.out.println(array instanceof int[][][]);

//获取三维数组的索引为3的一个二维数组

Object arrayObj = Array.get(array, 3);

//获取二维数组的索引为5的一个一维数组

arrayObj = Array.get(arrayObj, 5);

//设值一维数组arrayObj下标为10的值设为37

Array.setInt(arrayObj, 10, 37);

int[][][] arrayCast = (int[][][]) array;

System.out.println(arrayCast[3][5][10]);

<span style="color:#008000;">  //创建一个设值数组维度的数组  </span>       int[] dims = new int[] { 5, 10, 15 };  <span style="color:#008000;">      //利用Array.newInstance创建一个数组对象,第一个参数指定数组的类型,第        二个参数设值数组的维度,下面是创建一个长宽高为:5,10,15的三维数组  </span>  Object array = Array.newInstance(Integer.TYPE, dims);      System.out.println(array instanceof int[][][]);   <span style="color:#008000;">      //获取三维数组的索引为3的一个二维数组  </span>  Object arrayObj = Array.get(array, 3);  <span style="color:#008000;">       //获取二维数组的索引为5的一个一维数组  </span>  arrayObj = Array.get(arrayObj, 5);   <span style="color:#008000;">     //设值一维数组arrayObj下标为10的值设为37  </span>  Array.setInt(arrayObj, 10, 37);      int[][][] arrayCast = (int[][][]) array;      System.out.println(arrayCast[3][5][10]);


利用反射访问类的私有方法:
代码示例:

[java]
view plaincopyprint?

Private p = new Private();

Class<?> classType = p.getClass();

Method method = classType.getDeclaredMethod("sayHello",

new Class[] { String.class });

method.setAccessible(true);//压制Java的访问控制检查,使允许访问private方法

String str = (String)method.invoke(p, new Object[]{"zhangsan"});

System.out.println(str);

利用反射访问类的私有变量:

[java]
view plaincopyprint?

Private2 p = new Private2();

Class<?> classType = p.getClass();

Field field = classType.getDeclaredField("name");

field.setAccessible(true);//压制Java对访问修饰符的检查

field.set(p, "lisi");

System.out.println(p.getName());
---------------------------------------

反射的使用:

1.获得一个类的类模版

Class c = 对象名.getClass();

Class c = Class.forName(包名+类名); ---常用

我们也可以根据模版来获取其对应的类对象:

c.newInstance();

2.根据类模版获取类的信息:

获取类的属性:

Filed类----c.getFiled(String pname) 获得指定属

性(公共字段)

Filed类----c.getDeclearedFiled(String pname)

获得指定属性(所有权限字段)

Filed[]----c.getFileds(String pname) 获得所有属

性(公共字段)

Filed[]----c.getDeclearedFileds(String pname)

获得所有属性(所有权限字段)

获取类的方法:

Method类----c.getMethod(String

methodName,class[] params) 获得指定方法(公共方法)

Method类----c.getDeclearedMethod(String

methodName,class[] params) 获得指定方法(所有方法)

Method[]----c.getMethods(String

methodName,class[] params) 获得所有方法(公共方法)

Method[]----c.getDeclearedMethods(String

methodName,class[] params) 获得所有方法(所有权限方法)

反射的优点:

其实就一句话:高类聚低耦合。把代码动态化,提高了代码的灵活度减

少了代码量!Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。

反射的缺点:

代码灵活度增大了,自然会带来效率的问题,因此,大量使用反射会带

来效率低的问题!

那些地方用到反射:

可以在配置文件里配置类名,然后在程序中动态的加载加载配置文件中指定的类并调用其方法,调方法之前还可以先检查这个人有没有这个权限,在Spring Hibernate transaction的处理以及面向切面的编程AOP中都大量用到了反射机制,hibernate、struts都是用反射机制实现的。

反射提供的主要功能:

Java 反射机制主要提供了以下功能:

在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: