java反射详解

来源:互联网 发布:溺水 该去救人吗 知乎 编辑:程序博客网 时间:2024/05/22 16:02

  反射是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():通过类的不带参数的构造方法创建这个类的一个对象

通过默认构造方法创建一个新对象:

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

以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

获得对象的所有属性:

-Field fields[]=classType.getDeclaredFields();

-Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象:

如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回

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


 

注: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]);  


 

利用反射访问类的私有方法:

代码示例:


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


 

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


   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 反射机制主要提供了以下功能:

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

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

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

在运行时调用任意一个对象的方法

 

用反射机制实现对数据库数据的增、查例子 :

http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html
 

原创粉丝点击