Java中反射技术

来源:互联网 发布:回声电影软件 编辑:程序博客网 时间:2024/05/08 20:51

反射技术:

动态的加载和获取一个类和类中的内容(成员)

好处:极大的提高了程序的扩展性。

对于框架或者服务器Tomcat,底层都用了这种反射技术。 

使用反射技术:

第一步:就是动态获取类文件对象,需要使用对类文件进行描述的类Class

类文件对象的类型就是Class类型。

如何去获取一个字节码文件对象呢?

三种方式:

1,通过每一个对象都具备的object类中的getClass()方法获取;

2,通过每一种数据类型都具备的一个静态的class属性。

3通过Class类中的forName方法。这种方式最常用。扩展性最强 。

Class类就是反射技术主要应用类之一,有了该类就可以动态获取字节码文件对象。

只要获取了字节码文件对象,获取字节码文件中的内容就直接使用该Class对象的方法就哦了。 

这个也可以简单理解为时对类文件的解剖。

获取字节码文件对象三种方式:

 * 1,每一个对象在内存中建立,都有自己对应字节码文件对象。

 * 可以通过Object中的getClass方法获取该对象的字节码文件对象。

 * 这种方式必须要明确具体类名称,并建立该类的对象,才可以通过getClass获取字节码文件对象。有点麻烦。

 * 

public static void getClass_1(){

Person p1 = new Person();

Person p2 = new Person();

Class clazz1 = p1.getClass();

Class clazz2 = p2.getClass();

System.out.println(clazz1==clazz2);

}

 * 2,每一个数据类型都有一个静态的属性class。通过该class属性即可获取该类型的Class对象。

 * 这种方式相对简单,但是还是需要使用具体类来完成该类字节码文件对象获取。

 * 

public static void getClass_2(){

Class clazz = Person.class;

Class c = int.class;

 * 3,通过Class类中的forName方法,通过类字符串名称即可获取该类的字节码文件对象。

 * 这种方式扩展性更好一些,因为只要知道名称即可forName会自动去找与该名称相同的字节码文件.

类 Class<T>

java.lang.Object

  java.lang.Class<T>

类型参数: 

T - 由此 Class 对象建模的类的类型。例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>。

Class 类的实例表示正在运行的 Java 应用程序中的类和接口

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。 

static Class<?>

forName(String className) 
          返回与带有给定字符串名的类或接口相关联的 Class 对象。

Class clazz = Class.forName("cn.itcast.bean.Person");

里面传的参数是类名的字符串形式,在写的时候一定要加上包名,不然会现:ClassNotFoundException类找不到异常。

既然已经拿到了该类的字节码文件对象,就需要对该类中的内容进行获取;

而一个类中的内容无非就是:

     1,字段。    2,方法。    3,构造函数。   

               

               

一, 获取方法:

1,获取的是所有方法的数组;

Method[]

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

Method[]

getDeclaredMethods() 
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

public static void getFunctions() throws ClassNotFoundException{

//获取指定名称的字节码文件对象。

Class clazz = Class.forName("cn.itcast.bean.Person");

//通过字节码对象获取字节码中的内容-函数。

Method[] methods = clazz.getMethods();//返回的是类中的公共方法,包括从object类中继承的方法。

methods = clazz.getDeclaredMethods();//获取的是本类中的方法数组,包含私有的方法。

//遍历方法数组。

for(Method m : methods){

System.out.println(m);

}

}

二, 获取指定的方法,并使其能运行:

Method

getMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。

参数: 

name - 方法名 

parameterTypes - 参数列表 

Class clazz = Class.forName("cn.itcast.bean.Person");

Method method = clazz.getMethod("show"null);

 T

newInstance() 
          创建此 Class 对象所表示的类的一个新实例。

Object obj = clazz.newInstance();创建了一个该类的对象相当于。

这据话就相当于Person p=new Person();

在反射中,如果要让一个反射到的非静态方法执行,两个要素,一个是该方法所属的对象,二是,具体的参数值。

方法对象如何运行,方法对象自己最清楚,应该到Method对象中的去找方法。

这个方法需要传入一个被调用方法所属的对象。

如何获取一个指定的字节码文件对象创建的对象呢?当是通过字节码文件对象来完成

类 Method

java.lang.Object

  java.lang.reflect.AccessibleObject

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 

Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException。 

Object

invoke(Object obj, Object... args) 
          对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

Class clazz = Class.forName("cn.itcast.bean.Person");

Method method = clazz.getMethod("show"null);

Object obj = clazz.newInstance();

method.invoke(obj, null);

三,获取一个静态方法。

public static void getStaticFunciton() throws Exception{

Class clazz = Class.forName("cn.itcast.bean.Person");

Method method = clazz.getMethod("show3"null);

method.invoke(nullnull);由于静态方法不需要对象调用也行,所以在传入对象时就传入null就可以运行了。

四,暴力反射私有方法:

Method

getDeclaredMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

此方法可以根据所传的参数获取指定的私有方法。

如果要运行此私有方法,就需要用到Method对象父类中的setAccessible 方法来取消它的私有权限,对它进行暴力访问。           

void

setAccessible(boolean flag) 
将此对象的 accessible 标志设置为指示的布尔值。

将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

public static void getPrivateFunction() throws Exception{

Class clazz = Class.forName("cn.itcast.bean.Person");

Method method = clazz.getDeclaredMethod("show2"null);

method.setAccessible(true);//取消检查访问权限。暴力访问。

Object obj = clazz.newInstance();

method.invoke(obj, null);

}

五,获取一个带有参数的方法。

public static void getParamterFunction() throws Exception{

Class clazz = Class.forName("cn.itcast.bean.Person");

Method method = clazz.getDeclaredMethod("show4",int.class,String.class );

Object obj = clazz.newInstance();

method.invoke(obj, 31,"hahaha");

}

六,获取构造函数

如何进行指定类中的实例化呢?

 * 通常使用的是newInstance()方法完成,它会调用类中空参数构造函数。

 * 但是如果类中没有空参数构造函数,或者需要通过指定的构造函数实例化。怎么办呢?

 * 

 * 1,必须先获取到指定的构造函数对象。

 * 2,通过指定的构造函数对象进行指定的类的对象的初始化。

Constructor<T>

getConstructor(Class<?>... parameterTypes) 
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

Constructor<?>[]

getConstructors() 
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

类 Constructor<T>

java.lang.Object

  java.lang.reflect.AccessibleObject

      java.lang.reflect.Constructor<T>

类型参数: 

T - 在其中声明构造方法的类。

T

newInstance(Object... initargs) 
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

参数: 

initargs - 将作为变量传递给构造方法调用的对象数组;基本类型的值被包装在适当类型的包装器对象(如 Float 中的 float)中。 

返回: 

通过调用此对象表示的构造方法来创建的新对象 

public static void getConstructor() throws Exception{

Class clazz = Class.forName("cn.itcast.bean.Person");

//获取了到指定的构造器对象。

Constructor constructor = clazz.getConstructor(String.class,int.class);

//可以通过指定的构造器对象进行初始化。

Object obj = constructor.newInstance("lisi",39);

Method method = clazz.getMethod("show5"null);

method.invoke(obj, null);

}

原创粉丝点击