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

java的反射机制Reflection(重点、难点)

2014-03-10 16:59 489 查看
一、反射机制(Reflection)概述

在java运行时环境中,动态获取类的信息以及动态调用对象的方法都是基于java的反射机制来完成的。

注意点:是在java的运行过程中,不是在编译过程中。讲到Reflection就要联想到运行时环境

二、[b]Reflection的功能[/b]

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

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

3.在运行时判断任意一个类所具有的成员变量;

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

java的这个机制允许在运行时透过Reflection APIs取得任何一个已知名称(可以是自己定义的class、如Person类,或者已知的“java.lang.String”等已知类)的class的内部信息,包括此class的修饰符、父类、实现的接口、属性、方法(包括构造方法)等所有信息,也可以在运行时修改属性内容或调用方法

java其实是一门静态语言,但Reflection是java被视为动态语言(准确来讲,为准动态)的一个关键性质。对于动态语言,允许在程序运行时改变程序结构或变量类型,例如可以访问private类型的属性和方法就是利用的这个原理。

举一个动态语言例子,为特定类型的属性赋值一个其他类型:

int a = 1;

a = "lpp";

a = true;

这个例子说明,程序在运行过程中改变了属性a的数据类型,这在动态语言中是可行的。虽然java是一门静态语言,但利用其反射机制,也能实现如上代码。

三、java中实现[b]Reflection的类[/b]

在java.lang和java.lang.reflect包中,存在几个实现java反射机制的类:

1.Class类:代表一个类。每个类和接口都有一个与之关联的Class对象,无论生成某个类的多少个对象,这些对象都对应此类的同一个Class类;例如生产类Integer的多个对象,这些对象都对应于Integer所对应的Class对象。

java.lang

类 Class<T>

java.lang.Object
java.lang.Class<T>


2.Field类:代表类的成员变量(属性)。

java.lang.reflect

类 Field

java.lang.Object
java.lang.reflect.AccessibleObject
java.lang.reflect.Field


3.Method类:代表类的方法。

java.lang.reflect

类 Method

java.lang.Object
java.lang.reflect.AccessibleObject
java.lang.reflect.Method


4.Constructor类:代表类的构造方法。

java.lang.reflect

类 Constructor<T>

java.lang.Object
java.lang.reflect.AccessibleObject
java.lang.reflect.Constructor<T>


5.Array类:提供了一个动态创建数组,以及访问数组元素的静态方法。专门用于在反射机制中对数组的操作。

java.lang.reflect

类 Array

java.lang.Object
java.lang.reflect.Array


四、实现Reflection的类的使用

1.Reflection中Class类的使用方法

(1)使用反射机制时,首先应得到需要操作的对象所对应的Class类。利用Class类的forName方法得到所操作类的Class对象:

static Class<?>
forName(String className)


          返回与带有给定字符串名的类或接口相关联的
Class
对象。
(2)通过Class类的getMethods()方法得到一个包含某些Method对象的数组,这些Method对象就是Class对象所对应的类或接口的所有public方法(包括继承的父类中的方法、不包括构造方法)。

Method[]
getMethods()


          返回一个包含某些
Method
对象的数组,这些对象反映此
Class
对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共member 方法。
综合(1)(2)给出示例:

import java.lang.reflect.*;

public class  FirstTest

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

//得到java.lang.String对应的Class对象
Class<?> classType = Class.forName("java.lang.String"); 

//得到java.lang.String中定义的所有public的方法
Method[] methods = classType.getMethods();
for(Method m : methods)
{
System.out.println(m);
}
}

}

结果将打印出java.lang.String类中所定义的所有public方法,以及所继承的父类Object中的方法,不包括构造方法。部分结果截图如下:






[b](3)通过Class类的getDeclaredMethods()方法得到一个包含某些Method对象的数组,这些Method对象就是Class对象所对应的类或接口的所有方法(包括[b]private方法、继承的父类中的方法、不包括构造方法)。[/b]
[/b]



 Method[]
getDeclaredMethods()


          返回
Method
对象的一个数组,这些对象反映此
Class
对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
[b]综合(1)(3)给出示例:
[/b]

import java.lang.reflect.*;

public class  FirstTest

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

//得到java.lang.String对应的Class对象
Class<?> classType = Class.forName("java.lang.String");

//得到java.lang.String中定义的所有的方法(包括私有的)
Method[] methods = classType.getDeclaredMethods();
for(Method m : methods)
{
System.out.println(m);
}
}

}

结果将打印出java.lang.String类中所定义的所有方法(包括private方法),以及所继承的父类Object中的方法,不包括构造方法。部分结果截图如下(可以
eb80
看出包含了private方法)



[b]为了证明以上2个示例都是在java运行过程中执行的,做如下事情:[/b]

把args[0]作为Class.forName()方法的传入参数Class.forName(args[0]),在编译通过后的执行阶段,手动输入java.lang.String,输出结果不变。这就说明了上述代码是在运行时执行的。



[b]2.Reflection中Method类的使用方法
[/b]

先看一个例子:调用类Math中的add()方法和stringPrint()方法,并给调用的方法传入具体的参数,输出计算结果。

代码如下:

import java.lang.reflect.*;

class Math

{
public int add(int a, int b)
{
return a+b;
}
public String stringPrint(String str)
{
return str;
}

}

class MethodDemo 

{
public static void main(String[] args)  throws Exception
{
//首先生成Math类所对应的Class对象
Class<?> classType = Math.class;
//创建Class类对应的Math类的一个实例,若要调用具体的方法,则必须要声明一个实例,因为在为方法的Method对象调用invoke方法时,第一个参数就是调用此方法的实例
Object mathInstance = classType.newInstance();
//返回包含Math类中所有public方法的Method对象的数组
Method[] method = classType.getMethods();
//遍历Method对象所在的数组
for(Method m : method)
{
System.out.println(m);//打印Math类中所有的public方法
}

System.out.println("--------------调用add()方法----------------------");
//返回add对应的Method类
Method addMethod = classType.getMethod("add",new Class[]{int.class, int.class});
//对mathInstance实例调用addMethod对象所对应的add()方法,返回结果
Object addValue = addMethod.invoke(mathInstance, new Object[]{1, 2});
//打印结果
System.out.println((Integer)addValue);

System.out.println("------------调用stringPrint()方法-------------------------");
//返回stringPrintMethod对应的Method类
Method stringPrintMethod = classType.getMethod("stringPrint", new Class[]{String.class});
//对mathInstance实例调用stringPrintMethod对象所对应的stringPrint()方法,返回结果
Object stringValue = stringPrintMethod.invoke(mathInstance, new Object[]{"hello"});
//打印结果
System.out.println((String)stringValue);
}

}

结果:



3.Reflection中field类的使用方法

package com.lpp.reflect;

import java.lang.reflect.*;

class Math

{

    private int a;

    private int b;

    private String str;

}

class Test

{

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

    {

        Class<?> classType = Math.class;

         //得到Math类这的所有属性

        Field[] field = classType.getDeclaredFields();

        for(Field f: field)//遍历Math类的所有属性

        {

System.out.println(f);   

String str = f.getName();

System.out.println(str);

        }

    }

}

结果:

private int com.lpp.reflect.Math.a

a

private int com.lpp.reflect.Math.b

b

private java.lang.String com.lpp.reflect.Math.str

c

可知,用getDeclaredFields()方法遍历出了Math类的所有属性,包括私有的。

4.Reflection中Constructor类的使用方法

package com.lpp.reflect;

import java.lang.reflect.*;

class Math

{
public int add(int a, int b)
{
return a+b;
}
public String stringPrint(String str)
{
return str;
}

}

class Test

{

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

    {

        Class<?> classType = Math.class;

        //利用constructor方法得到Math类的一个实例

        Constructor<?> constr = classType.getConstructor(new Class[]{});

        Object object = constr.newInstance(new Object[]{});

        //得到add()方法的Method类,并为此类调用add()方法,打印返回值

        Method method = classType.getMethod("add",new Class[]{int.class, int.class});

        Object addValue = method.invoke(object, new Object[]{1,2});        

        System.out.println(addValue);        

    }

}

结果:  3

[b]5.综合使用了Reflection中Class类、Field类、Method类、Constructor类的示例程序[/b]

[b]程序要求:利用反射机制动态的调用Math类中的方法,实现Math类的对象的拷贝。
[/b]

package com.lpp.reflect;

import java.lang.reflect.*;

class Math

{

    private int a;

    private int b;

    private String str;

    public Math()

    {

    

    }

    public Math(int a, int b, String str)

    {

        this.a = a;

        this.b = b;

        this.str = str;

    }

    public int add(int a, int b)

    {

        return a+b;

    }

    public String stringPrint(String str)

    {

        return str;

    }

    public void setA(int a)

    {

        this.a = a;

    }

    public int getA()

    {

        return a;

    }

    public void setB(int b)

    {

        this.b = b;

    }

    public int getB()

    {

        return b;

    }

    public void setStr(String str)

    {

        this.str = str;

    }

    public String getStr()

    {

        return str;

    }

}

public class  Test

{

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

    {

        Math m = new Math(1,20,"hello");

        CopyDemo cd = new CopyDemo();

        Math m1 = (Math)cd.copy(m);

        System.out.println(m1.getA()+","+m1.getB()+","+m1.getStr());

    

    }

}

class CopyDemo

{

    public static Object copy(Object object) throws Exception

    {

        //利用Object类的getClass()方法,得到object方法对应的Class类

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

        //利用Constructor类生成一个实例

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

        //得到Class对象所对应的类的属性

        Field[] field = classType.getDeclaredFields();

        for(Field f: field)//遍历属性

        {

            System.out.println(f);

            String string = f.getName();//得到属性的名字

            System.out.println(string);

            

            //得到属性名字的第一个字母的的大写

            String firstLetter = string.substring(0, 1).toUpperCase();

            //得到属性对应的set和get方法名

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

            String getMethodName = "get"+ firstLetter +string.substring(1);

            System.out.println(setMethodName+","+getMethodName);

            //得到get和set方法对应的Method对象

            Method getMethod = classType.getDeclaredMethod(getMethodName,new Class[]{});

            Method setMethod = classType.getDeclaredMethod(setMethodName,new Class[]{f.getType()});

            

            //为object对象调用getMethod方法

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

            System.out.println(value);

//为objectCopy对象调用setMethod方法           

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

            

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

        }

        return objectCopy;

    }

}

结果:

private int com.lpp.reflect.Math.a

a

setA,getA

1

--------------

private int com.lpp.reflect.Math.b

b

setB,getB

20

--------------

private java.lang.String com.lpp.reflect.Math.str

str

setStr,getStr

hello

--------------

1,20,hello


[b]6.Reflection中Array类的使用方法[/b]

Array类中提供的都是静态方法,例如:

static
Object
get(Object
array, int index)
static void
set(Object
array, int index,
Object value)
以下程序定义一个数组,调用set方法为数组增加元素,调用get方法输出特定索引的元素。
package com.lpp.reflect;

import java.lang.reflect.*;

public class  Test

{

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

    {

        Class<?> classType = Class.forName("java.lang.String");

        //定义一个长度为10,classType类对应的数组

        Object object = Array.newInstance(classType, 10);

        //向数组添加元素,并取出索引为2的元素,打印

        Array.set(object, 0 , "hello0");

        Array.set(object, 1 , "hello1");

        Array.set(object, 2 , "hello2");

        Object value = Array.get(object, 2);

        System.out.println(value);

        //Array.get(object, 10);//超过数组上限,报错

    }

}

结果:hello2

五、通过Reflection访问类中私有的成员变量或方法

若想访问目标类中私有属性或方法时,需改变java的访问检查方式,这时候就用到java.lang.reflect包下的类AccessibleObject中的setAccessible()方法:public void setAccessible(boolean flag) throws

SecurityException

AccessibleObject类是Constructor,

Field,
Method的父类,因此在调用私有化的
Field,
Method时可先设置setAccessible()方法的参数boolean flag,flag为true时表示java在执行下面的代码时不进行访问检查,flag为false时不影响java对访问权限的检查。

代码示例:

package com.lpp.reflection;

public class PrivateMethod

{    

    private int privateIntegerMethod(int i, int j)

    {

        return i+j;

    }

    private String privateStringMethod(String s)

    {

        return s;

    }

}

package com.lpp.reflection;

import java.lang.reflect.Method;

public class PrivateReflectRequest

{

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

    {

        Class<?> classType = PrivateMethod.class;

        Object obj = classType.newInstance();        

        Method method = classType.getDeclaredMethod("privateIntegerMethod",new Class[]{int.class, int.class});

        //设置setAccessible()的参数为true时java在处理method时忽略其访问权限

        method.setAccessible(true);        

        Object values = method.invoke(obj, new Object[]{1, 5});

        System.out.println(values);

    }

}

结果:6

若不加入method.setAccessible(true);  或者method.setAccessible(false);  则会报错。

六、总结

1、获取某个类或对象的Class类的方法有如下3种:

(1)利用Class类的静态方法forName():

Class<?> classType = Class.forName("java.lang.string");

2)通过java内置方法.class

Class<?> classType = string.class;

(3)通过Object类中提供的getClass()方法

String sr = "lpp";

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

2、利用反射机制实例化目标类有如下两种方法:

(1)通过Class类的newInstance()方法进行目标类的实例化:

Class<?> classType = Math.class;

Object object = classType.newInstance();

(2)利用Class类得到Constructor类,由Constructor类的newInstance()方法进行目标类的实例化:

Class<?> classType = Math.class;

Constructor<?> constr = classType.getConstructor(new Class[]{});

Object object = constr.newInstance(new Object[]{});

3、.TYPE和.Class的区别

Integer.TYPE返回的是int型,即Integer的原生数据类型

Integer.class返回的是Integer对应的Class类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: