转载需注明出处:java反射机制(1)- 知识点总结Java Reflection API操作
什么是反射机制?简单点说就是程序在运行时能够获取自身的信息。在java中,只要给定类的全名,就可以通过反射机制来获取类的所有信息。
复杂点说就是:通过Class类,可以在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能酒称为java的反射机制。
Java反射机制主要提供了一下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理;
静态加载类(编译时加载类):大多数情况下都是使用这种形式。比如我们定义了一个类A,实例化采用A a = new A()
接着就可以通过a对象调用相关方法或属性,这就是静态加载类的过程。
动态加载类(运行时加载类):所谓动态加载类,只需要通过Class clazz = Class.forName("类的全名")
即可获得类类型,然后通过调用A a = clazz.newInstance()
方法即可实例化这个类。
本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。
JDK中,主要通过一下类实现java反射机制,这些类抖位于java.lang.reflect包中。
Array:提供了动态创建数组,以及访问数组元素的静态方法
下面直接通过几个案例说明相关API操作:
案例1:获取类名、方法、属性已经构造函数等基本信息。
在 java.lang.Object 类中定义了 getClass()方法, 因此对于任意一个 Java 对象, 都可以通过此方法获得对象的类型。Class 类是 Reflection API 中的核心类,它有以下方法。
getName():获得类的完整名字。
package com.markliu.reflection.getinfo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class DumpMethodsConstructorsFields {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
/* Returns the Class object associated with the classname */
Class> clazz = Class.forName("java.util.Stack");
Class> clazz1 = Class.forName("java.io.FileInputStream", true, java.io.FileInputStream.class.getClassLoader());
System.out.println(clazz);
System.out.println(clazz1);
System.out.println("--------interfaces------");
Class>[] interfaces = clazz1.getInterfaces();
for(Class> iClass : interfaces) {
System.out.println(iClass.getName());
}
System.out.println("--------------");
/*
* 获取所有声明的方法(不包括构造函数),public,protected,缺省和private类型,
* 包括返回类型、方法名,参数和抛出异常.
* getDeclaredMethods():获得类的所有方法。
*/
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods) {
System.out.println(m);
}
System.out.println("--------------");
/*
* getMethods():获得类的 public 类型的方法。
*/
Method[] methods1 = clazz1.getMethods();
for(Method m : methods1) {
System.out.println(m);
}
System.out.println("--------------");
/*
* getMethod(String name, Class[] parameterTypes):获得类的特定方法,
* name 参数指定方法的名字
* parameterTypes 参数指定方法的参数类型。
* 获取:public int java.io.FileInputStream.read(byte[],int,int) throws java.io.IOException
*/
Method method = clazz1.getMethod("read", new Class[]{byte[].class, int.class, int.class});
System.out.println(method);
System.out.println("-------public Constructors-------");
Constructor>[] publicConstructors = clazz1.getConstructors();
for(Constructor> c : publicConstructors) {
System.out.println(c);
}
System.out.println("-------constructors-------");
Constructor>[] constructors = clazz1.getDeclaredConstructors();
for(Constructor> c : constructors) {
System.out.println(c);
}
/* 根据参数数组获取指定的public型构造函数*/
Constructor> constructor = clazz1.getConstructor(new Class[]{java.io.File.class});
System.out.println(constructor);
System.out.println("-------public field-------");
/* 获得类的所有public类型的属性。*/
Field[] fields = clazz1.getFields();
for(Field field : fields) {
System.out.println(field);
}
System.out.println("-------field-------");
/* 获得类的所有属性。*/
Field[] fields1 = clazz1.getDeclaredFields();
for(Field field : fields1) {
System.out.println(field);
}
System.out.println("-------field's setters and getters methods-------");
/*
* 获取属性的set和get方法
* 我们不能直接获取setter和getter方法,需要获取到所有的方法,
* 再进行循环选择。注意对于boolean类型,默认采用的是isXxx()
*/
Class> clazz2 = Class.forName("com.markliu.reflection.invoke.Person");
Method[] methods2 = clazz2.getMethods();
for(Method m : methods2) {
if (isGetter(m))
System.out.println(m);
if (isSetter(m))
System.out.println(m);
}
}
public static boolean isGetter(Method method) {
String methodName = method.getName();
Class> returnType = method.getReturnType();
Class> parameterTypes[] = method.getParameterTypes();
if(returnType.equals(void.class)) {
return false;
}
if ( (methodName.startsWith("get") || methodName.startsWith("is"))
&& parameterTypes.length == 0) {
return true;
}
return false;
}
public static boolean isSetter(Method method) {
String methodName = method.getName();
Class> parameterTypes[] = method.getParameterTypes();
if (methodName.startsWith("set") && parameterTypes.length == 1) {
return true;
}
return false;
}
}
输出结果为(考虑篇幅,只列出获取setter和getter方法的结果,其他的省略):
public boolean com.markliu.reflection.invoke.Person.isGoodman()
public void com.markliu.reflection.invoke.Person.setGoodman(boolean)
public java.lang.Integer com.markliu.reflection.invoke.Person.getAge()
public void com.markliu.reflection.invoke.Person.setAge(java.lang.Integer)
public java.lang.String com.markliu.reflection.invoke.Person.getName()
public void com.markliu.reflection.invoke.Person.setName(java.lang.String)
public final native java.lang.Class java.lang.Object.getClass()
案例2:获取类的私有属性和私有方法。
person类的私有属性和方法:
private String name;
private Integer age;
private void introduce() {
System.out.println("My name is " + name + ", I'm " + age + " years old!");
}
System.out.println("-------private fields and methods-------");
Person person = new Person("SunnyMarkLiu", 22);
System.out.println("name:" + person.getName());
Field privateField = Person.class.getDeclaredField("name");
// !important 设置标志为true后,被反射的类会抑制java的访问检查机制
privateField.setAccessible(true);
privateField.set(person, "HasChanged!");
System.out.println("反射修改后:" + person.getName());
Method privateMethod = Person.class.getDeclaredMethod("introduce", new Class[]{});
// !important 设置标志为true后,被反射的类会抑制java的访问检查机制
privateMethod.setAccessible(true);
privateMethod.invoke(person, new Object[]{});
注意:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置setAccessible(true)
使被反射的类抑制java的访问检查机制。否则会报IllegalAccessException
异常!
运行结果如下:
name:SunnyMarkLiu
反射修改后:HasChanged!
My name is HasChanged!, I'm 22 years old!
案例3:通过调用构造方法创建一个新的对象:。
先调用 Class 类的 getConstructor方法获得一个 Constructor 对象,然后调用 Constructor 对象的 newInstance方法构造一个实例。
package com.markliu.reflection.newInstance;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class NewInstance {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
Class> clazz = Class.forName("com.markliu.reflection.newInstance.PersonBean");
System.out.println(clazz);
// 获取有参构造函数
Constructor> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});
PersonBean person = (PersonBean) constructor.newInstance(new Object[]{"SunnyMarkLiu", 20});
person.info();
// 获取有参构造函数
Constructor> constructor1 = clazz.getConstructor(new Class[]{});
PersonBean person1 = (PersonBean) constructor1.newInstance(new Object[]{});
person1.info();
}
}
class PersonBean implements Person{
private String name;
private Integer age;
public PersonBean() {}
public PersonBean(String name, Integer age) {
this.name = name;
this.age = age;
}
// 省略了get和set方法
public void info() {
System.out.println(this.name + ":" +this.age);
}
}
interface Person {
public void info();
}
案例4:运用反射机制调用对象的方法。
Method 类的 invoke(Object obj,Object args[])方法接收的参数必须为对象, 如果参数为基本类型数据, 必须转换为相应的包装类型的对象。 invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据, 那么 invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
package com.markliu.reflection.invoke;
import java.lang.reflect.Method;
public class MethodInvoke {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.markliu.reflection.invoke.PersonService");
PersonService personService = (PersonService) clazz.getConstructor(new Class>[]{}).newInstance(new Object[]{});
// 获取要调用的方法
Method method = clazz.getMethod("add", new Class[]{Person.class});
/*
* 调用personService对象的method方法,同时传递参数。
* 返回值result为调用该函数的返回值,如果函数返回类型为void,则result为null
*/
Object result = method.invoke(personService,
new Object[]{new Person("SunnyMarkLiu", 22)});
System.out.println(result);
Method method1 = clazz.getMethod("get", new Class[]{int.class});
Object result1 = method1.invoke(personService,
new Object[]{0});
System.out.println(result1);
Person p = (Person) result1;
System.out.println(p.getName()+":"+p.getAge());
}
}
// PersonService类
package com.markliu.reflection.invoke;
import java.util.List;
import java.util.ArrayList;
public class PersonService {
private List persons = new ArrayList();
public String add(Person p) {
persons.add(p);
return "add-done";
}
public Person get(int i) {
return persons.get(i);
}
}
// Person类
package com.markliu.reflection.invoke;
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
案例5:Array 类提供了动态创建和访问数组元素的各种静态方法。
package com.markliu.reflection.array;
import java.lang.reflect.*;
public class ArrayTester {
public static void main(String args[]) throws Exception {
// 设置数组的维度为5x10
int dims[] = new int[] { 5, 10, 15 };
Class> classType = Class.forName("java.lang.String");
Object array = Array.newInstance(classType, dims);
// Object array = Array.newInstance(Integer.TYPE, dims);
// 使 arrayObj 引用 array[3]
Object arrayObj = Array.get(array, 3);
Class> cls = arrayObj.getClass().getComponentType();
System.out.println("ComponentType" + cls);
// 使 arrayObj 引用 array[3][5]
Object arrayObj1 = Array.get(arrayObj, 5);
// 把元素 array[3][5][10]设为 37
Array.set(arrayObj1, 10, "hello");
String arrayCast[][][] = (String[][][]) array;
System.out.println(arrayCast[3][5][10]);
/*
* 输出:
* ComponentTypeclass [Ljava.lang.String;
* hello
*/
}
}
Java 反射机制是 Java 语言的一个重要特性。考虑实现一个 newInstance(String className)方法,它的作用是根据参数 className 指定的类名,通过该类的不带参数的构造方法创建这个类的对象,并将其返回。如果不运用 Java 反射机制,必须在newInstance()方法中罗列参数 className 所有可能的取值,然后创建相应的对象:
public Object newInstance(String className) throws Exception{
if(className.equals("HelloService1"))
return new HelloService1();
if(className.equals("HelloService2"))
return new HelloService2();
if(className.equals("HelloService3"))
return new HelloService3();
if(className.equals("HelloService4"))
return new HelloService4();
...
if(className.equals("HelloService1000"))
return new HelloService1000();
}
以上程序代码很冗长,而且可维护性差。如果在以后软件的升级版本中去除了一个 HelloService4 类,或者增加了一个 HelloService1001 类,都需要修改以上 newInstance()方法。如果运用反射机制,就可以简化程序代码,并且提高软件系统的可维护性和可扩展性:
public Object newInstance(String className) throws Exception{
Class classType=Class.forName(className);
return classType.newInstance();
}
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求 ,动态调用某一个对象的特定方法。此外 ,有一种ORM(Object-Relation Mapping,对象-关系映射,例如Hibernate)中间件能够把任意一个 JavaBean 持久化到关系数据库中。在 ORM 中间件的实现中,运用 Java 反射机制来读取任意一个JavaBean 的所有属性, 或者给这些属性赋值。 (小结摘自:孙卫琴《Java网络编程精解》第10章)