JAVA反射机制(Reflection)是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对J象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制(Reflection)。反射(Reflection)给java提供了,运行时获取一个类实例的可能,这一点非常灵活,你仅仅传一个类的全包名路径,就能通过反射(Reflection),来获取对应的类实例,我们一般会用Class类,来调用这个被反射(Reflection)的Objcet类下的,构造方法,属性,或方法等,反射(Reflection)在一些开源框架里用的非常之多,Spring,Struts,Hibnerate等都有它的影子,下面具体看看反射(Reflection)的有关概念!
一、Java反射(Reflection)的概念。
反射(Reflection)含义:可以获取正在运行的Java对象。
二、Java反射(Reflection)的功能。
(1) 可以判断运行时对象所属的类;
(2) 可以判断运行时对象所具有的成员变量和方法;
(3) 通过反射(Reflection)甚至可以调用到private的方法;
(4) 生成动态代理。
反射(Reflection)虽然很灵活,能够使得写的代码,变的大幅精简,所以在用的时候,一定要注意具体的应用场景,反射(Reflection)的优缺点如下:
优点:
(1) 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性;
(2) 与Java动态编译相结合,可以实现无比强大的功能 。
缺点:
(1) 使用反射(Reflection)的性能较低 ;
(2) 使用反射(Reflection)相对来说不安全 ;
(3) 破坏了类的封装性,可以通过反射(Reflection)获取这个类的私有方法和属性 。
了解了反射的基本概念后,接下来,看看Java提供的反射(Reflection) API。
三、Java Reflection API简介。
在JDK中,主要由以下类来实现Java反射机制:
Class类:代表一个类,位于java.lang包中。
Field类:代表类的成员变量(成员变量也称为类的属性),位于java.lang.reflect包中。
Method类:代表类的方法,位于java.lang.reflect包中。
Constructor类:代表类的构造方法,位于java.lang.reflect包中。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法,位于java.lang.reflect包中。
Package:类的包定义,位于java.lang包中。
3.1 获取Class对象。
要想使用反射,首先需要获得待操作的类所对应的Class对象。
所有类的对象其实都是Class的实例。
获取Class对象的3种方式:
1.使用Class类的静态方法。( 一般尽量采用这种形式)例如:
try{
Class<?> demo1=Class.forName("com.xinxing.Demo");
}catch(Exception e){
e.printStackTrace();
}
2.使用类的.class语法。如:
Class<?> demo3=Demo.class;
3.使用对象的getClass()方法。如:
Class<?> demo2=new Demo().getClass();
下面通过代码使用以上3种方式获取一个类的Class对象,代码示例:
(1) 在com.xinxing包中,新建一个Demo类,具体实现如下所示,
package com.xinxing;
public class Demo {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
(2) 新建一个Hello类,代码具体如下,
public class Hello {
public static void main(String[] args) {
Class<?> demo1=null;
Class<?> demo2=null;
Class<?> demo3=null;
try{
demo1=Class.forName("com.xinxing.Demo");
}catch(Exception e){
e.printStackTrace();
}
demo2=Demo.class;
demo3=new Demo().getClass();
System.out.println("类名称 "+demo1.getName());
}
}
System.out.println("类名称 "+demo2.getName()); System.out.println("类名称 "+demo3.getName()); }}
运行结果截图如下,
3.2 获取对象的属性。获取Class对象的属性常用的有2种,
(1) public Field getField(String name) throws NoSuchFieldException, SecurityException
(2) public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
方法(1)和方法(2)的区别是:方法(1)只能获取类的public 字段;方法(2)是可以获取一个类的所有字段。下面分别列举一下,方法(1)和(2)的使用,具体代码如下,Demo类的实现如上所示,其中'id'属性是私有的(private),
(1) 方法1,
public static void main(String[] args) {
Class<?> demo1=null;
try{
//一般尽量采用这种形式
demo1=Class.forName("com.xinxing.Demo");
Field field=demo1.getField("id");
System.out.println("field名称 "+field.getName());
}catch(Exception e){
e.printStackTrace();
}
}
运行截图如下,
可以看出,因为属性‘id’的修饰符是私有的(private),所以通过,getField()方法获取不到私有的(private)属性。
(2) 方法2,
public static void main(String[] args) {
Class<?> demo1=null;
try{
//一般尽量采用这种形式
demo1=Class.forName("com.xinxing.Demo");
Field field=demo1.getDeclaredField("id");
System.out.println("field名称 "+field.getName());
}catch(Exception e){
e.printStackTrace();
}
}
运行结果截图如下,
可以看出,可以获取到属性名称,所以通过,getDeclaredField()方法是可以获取到私有属性的。
ps:
public void setAccessible(boolean flag)
throws SecurityException
在java的反射使用中,如果字段是私有的,那么必须要对这个字段设置 Field.setAccessible(true),这样才可以正常使用。
Field.setAccessible(true) 并不是将方法的访问权限改成了public,而是取消java的权限控制检查。所以即使是public方法,其accessible 。
3.3 获取方法。
(1) 获取类的所有方法,代码如下,
public static void main(String[] args) {
Class<?> demo1=null;
try{
//一般尽量采用这种形式
demo1=Class.forName("com.xinxing.Demo");
//返回class对象所对应的类或接口中,所声明的所有方法的数组(包括私有方法)
Method[] methods = demo1.getDeclaredMethods();
//遍历输出所有方法声明
for(Method method : methods)
{
System.out.println(method);
}
}catch(Exception e){
e.printStackTrace();
}
}
运行结果截图如下,
可以看到,Demo类中的所有方法全部都打印出来了。
(2) 获取类的某一个方法,代码如下,
public static void main(String[] args) {
Class<?> demo1=null;
try{
//一般尽量采用这种形式
demo1=Class.forName("com.xinxing.Demo");
String fieldName="id";
String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method =demo1.getMethod(getMethodName);
System.out.println(method);
}catch(Exception e){
e.printStackTrace();
}
}
运行结果截图如下,
PS:获取没有参数的方法时,getMethod(String name,
Class<?>... parameterTypes),第二个参数可以不写;获取有参数的方法时,第二个参数是参数类型,例如‘ getMethod("getId", int.class )’。
通过set设置值,然后get方法获取值,代码如下,
public static void main(String[] args) {
Class<?> demo1=null;
Demo demo=new Demo();
try{
//一般尽量采用这种形式
demo1=Class.forName("com.xinxing.Demo");
String fieldName="name";
String getMethodName="set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method =demo1.getMethod(getMethodName,String.class);
method.setAccessible(true);
String[] inArgs ={"xinxingdemo"};
method.invoke(demo,inArgs);//调用set方法设置值
String getMethodName1="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method1 =demo1.getMethod(getMethodName1);
Object object=(Object)method1.invoke(demo);//调用get方法获取值
System.out.println(object);
}catch(Exception e){
e.printStackTrace();
}
}
运行结果截图如下,
四、总结。
综上所述,常用的返回API:
4.1 获取Class对象的属性:
(1) public Field getField(String name)
只能获取类的public 字段。
(2) public Field getDeclaredField(String name)
获取一个类的所有字段。
4.2 getDeclaredMethods(),
返回所有方法
4.3 setAccessible() ,
如果字段是私有的,那么必须要对这个字段设置 Field.setAccessible(true),这样才可以正常使用。
Field.setAccessible(true) 并不是将方法的访问权限改成了public,而是取消java的权限控制检查。所以即使是public方法,其accessible 。
4.4 getMethod(),
获取具体的某一个方法。
4.5 invoke(),
反射调用类的方法,有参数的话,需要参数参数。PS:参数为一个对象数组。
4.6 Constructor<?> cons[]=demo.getConstructors(),
取得全部的构造函数。
4.7 Class<?> intes[]=demo.getInterfaces()。
获取所有的接口。
4.8Class<?> temp=demo.getSuperclass(),
取得父类。
4.9 Class<?> type = field[i].getType(),
属性类型。
4.10 newInstance(),
生成一个对象。
4.11 getPackage(),
获取包。