Java中的反射

来源:互联网 发布:中国石油大学华东网络教育 编辑:程序博客网 时间:2024/06/05 19:54

动态语言

说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言,但是也有人把java叫做“动态语言”,因为java所具有的Reflection是java被视为动态(或准动态)语言的一个关键性质。

java语言的反射机制

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
  • Class 类:代表一个类。
  • Field 类:代表类的成员变量(成员变量也称为类的属性)。
  • Method 类:代表类的方法。
  • Constructor 类:代表类的构造方法。
  • Array 类:提供了动态创建数组,以及访问数组的元素的静态方法

Class类

在java的Object类中的申明了数个应该在所有的java类中被改写的methods:hashCode(), equals(),clone(),toStrin(),getClass()等,其中的getClass()返回一个Class 类型的对象。Class类十分的特殊,它和一般的类一样继承自Object,其实体用以表达java程序运行时的 class和 interface,也用来表达 enum,array,primitive,Java Types 以及关键字void,当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便产生一个Class对象,Class是Reflection起源,针对任何你想探勘的class(类),唯有现为他产生一个Class的对象,接下来才能经由后者唤起为数十多个的反射API。

Class类常用方法

getName()   获得类的完整名字 
getMethods()   获取所有public方法    
getDeclaredMethods()  获取所有方法  
getMethod()   获取特定方法  
getDeclaredMethod()  获取特定方法(包含私有)
getFields()   获取所有public属性  
getDeclaredFields()   获取所有属性  
getField()   获取特定属性 
getDeclaredField()   获取特定属性(包含私有)
getConstructors()   获得类的public类型的构造方法  
getConstructor()   获得类的特定构造方法  
newInstance()   通过类的不带参数的构造方法创建这个类的一个对象

获取某个类所对应的Class对象的三种方式

要想使用反射,首先需要获得待处理类或对象所对应的Class 对象。
//方式1: 使用Class 类的静态方法forName()  Class.forName("java.lang.String");  //方式2: 使用类的.class语法  xxx.class //如: String.class;  //方式3: 使用对象的getClass()方法  String str = "xxx";  Class<?> clazz = str.getClass();

Constructor类

Constructor类的作用是用来生成类所对应的对象

通过类的不带参数的构造方法来生成对象的两种方式

//方式一: 先获得Class 对象,然后通过该Class对象的newInstance()方法直接生成   Class<?> clazz = String.class;Object obj = clazz.newInstance();//方式二: 先获得Class 对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成Class<?> clazz = String.class;Constructor cons = clazz.getConstructor();Object obj = cons.newInstance();

通过类的带参数的构造方法生成对象

要想通过类的带参数的构造方法生成对象,只有下面这一种方式

/** 如:有一个Customer类有下面这样一个带参构造方法*/public Customer(String name, int age) {}Class<?> clazz = Customer.class;Constructor cons = clazz.getConstructor(new Class[]{String.class, int.class});Object obj = cons.newInstance(new Object[]{"tom", 23});

Array类

该类位于java.lang.reflect包中,该类提供了动态创建和访问数组元素的各种静态方法.

Reflection API 的基本使用方法

//InvokeTest.javapackage com.hussion.reflect;import java.lang.reflect.Method;public class InvokeTest {/** 两个简单方法*/public int add(int a, int b) {return a + b;}public String sayHello(String str) {return "Hello " + str;}public static void main(String[] args) throws Exception {// 获取类InvokeTest的Class对象Class<?> clazz = InvokeTest.class;// 获取类InvokeTest的对象Object invokeTester = clazz.newInstance();// 获取add方法Method addMethod = clazz.getMethod("add", int.class, int.class);// 调用add方法Object result1 = addMethod.invoke(invokeTester, 1, 2);Method sayHelloMethod = clazz.getMethod("sayHello", String.class);Object result2 = sayHelloMethod.invoke(invokeTester, "world");// 打印方法执行结果System.out.println(result1);System.out.println(result2);}}

注意:

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象;invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。自从JDK5开始有了自动装箱和拆箱的新特性,所以上面可以直接传入基本数据类型。

//本来应该为Object result1 = addMethod.invoke(invokeTester, new Integer(1), new Integer(2));//简写为Object result1 = addMethod.invoke(invokeTester, 1, 2);
下面这个演示: ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object 同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回;另外这个例子只能复制简单的JavaBean,并假定JavaBean的每个属性都有public类型的getXXX()和setXXX()方法。
//ReflectTest.javapackage com.hussion.reflect;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectTester {// 该方法实现对Customer对象的拷贝操作public Object copy(Object object) throws Exception {Class<?> clazz = object.getClass();Object objectCopy = clazz.getConstructor().newInstance();// 获得对象的所有成员变量Field[] fields = clazz.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 = clazz.getMethod(getMethodName);Method setMethod = clazz.getMethod(setMethodName, field.getType());Object value = getMethod.invoke(object);setMethod.invoke(objectCopy, value);}return objectCopy;}public static void main(String[] args) throws Exception {Customer customer = new Customer("Tom", 20);// 因为此例id是long型,所以要写成1Lcustomer.setId(1L);ReflectTester test = new ReflectTester();Customer customer2 = (Customer) test.copy(customer);System.out.println(customer2.getId() + "," + customer2.getName() + ","+ customer2.getAge());}}class Customer {private Long id;private String name;private int age;public Customer() {}public Customer(String name, int age) {this.name = name;this.age = age;}public Long getId() {return id;}public void setId(Long 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;}}

Array类简单应用

ArrayTest.javapackage com.hussion.reflect;import java.lang.reflect.Array;public class ArrayTest {public static void main(String[] args) throws Exception {// 创建一个长度为10 的字符串数组Object array = Array.newInstance(String.class, 10);// 把索引位置为5的元素设为“Hello world”Array.set(array, 5, "Hello world");// 读取索引位置为5的元素的值String str = (String)Array.get(array, 5);System.out.println(str);}}
访问类private方法和private属性

虽然对被private修饰的属性和方法进行访问违背了java面向对象的封装特点,但是有些时候我们需要访问私有的属性和方法

访问类的私有方法

//private1.javapackage com.hussion.reflect;public class Private1 {private String sayHello(String name) {return "hello: " + name;}}

//private1Test.javapackage com.hussion.reflect;import java.lang.reflect.Method;public class Private1Test {public static void main(String[] args) throws Exception {Private1 p1 = new Private();Class<?> clazz = p1.getClass();Method method = clazz.getDeclaredMethod("sayHello", String.class);// 抑制Java的访问控制检查method.setAccessible(true);String str = (String)method.invoke(p1, "tom");System.out.println(str);}}
访问类的私有属性

package com.hussion.reflect;public class Private2 {private String name = "tom";public String getName(){return name;}}
package com.hussion.reflect;import java.lang.reflect.Field;public class Private2Test {public static void main(String[] args) throws Exception {Private2 p2 = new Private2();Class<?> clazz = p2.getClass();Field field = clazz.getDeclaredField("name");//抑制Java对访问修饰符的检查field.setAccessible(true);// 修改属性值field.set(p2, "jerry");System.out.println(p2.getName());}}