Java反射机制
反射是Java语言的特征之一,它允许动态地发现和绑定类、方法、字段,以及所有其他的语言所产生的元素,反射可以做的不仅仅是简单的列举类、字段以及方法。通过反射,还能够在需要时完成创建实例、调用方法,和访问字段。反射是Java被视为动态的关键。
Java反射提供的功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造一个类的对象。
- 在运行时判断一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法,通过反射甚至可以调用private方法
- 生成动态代理。
Java反射常用类
Class类
Java在运行的时候,系统会对所有的对象进行所谓的1运行时类型标识,用来保存这些信息就是Class类。Class类封装了一个对象和接口运行时的状态。
JVM为每种类型管理着独一无二的Class对象。也就是说,每一个类型都有一个Class对象。Java程序在运行时,当需要创建某个类的实例,JVM会首先加载的类的Class对象是否存在,如果还不存在,JVM会根据类名查找对应的字节码文件并加载,接着创建对应的Class对象,最后创建这个类的实例。
所有具有相同类型和数组度共享该Class对象。
经过上面的分析,可以看出运行中的类和接口在JVM中都会有一个对应的Class对象存在,它保存了对应类和接口的类型信息。要想获取类和接口相应信息,就必须先获取Class对象。
创建class对象
CeShi c = new CeShi();
Class c1 = c.getClass();//调用Class类的getClass方法
System.out.println(c1.toString());
try {
Class c2 = Class.forName("java.lang.String");//通过forName去获取与之字符串对应的Class对象
System.out.println(c2.toString());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Class c3 = CeShi.class;//直接通过类型.class获取该类型的Class对象。
System.out.println(c3.toString());
Class对象的使用(获取方法、字段、构造函数、参数类型等信息)
try {
Class c = Class.forName("java.util.ArrayList");
String packagename = c.getPackage().getName();//获取该Class对象的包的实体全限定名
System.out.println(packagename);
int mod = c.getModifiers();//获取类的修饰符,返回是一个整数表示修饰符的值。
String modifier = Modifier.toString(mod);//将对应的修饰符值换成修饰符字符串
System.out.println(modifier);
String classname = c.getName();//获取类的全限定名
System.out.println(classname);
Class superclass = c.getSuperclass();//获取Class对象直接父类对象
System.out.println(superclass);
Class[] interfaces = c.getInterfaces();//获取Class对象的实现的Class对象数组
for(Class class1:interfaces) {
System.out.println(class1.getName());//获取每一个接口Class对象的全限定名
}
Field[] fields = c.getDeclaredFields();//获取该类public字段的对象数组集合
for(Field field:fields) {
if(field.getType().isArray()) {
System.out.println(field.getType().getComponentType().getName()+"[]");//数组类型需要特别处理
}
System.out.println(field.getType());//获取字段的类型
System.out.println(field.getName());//获取字段名字
}
Constructor[] constructors = c.getDeclaredConstructors();//获取该类Class对象所有构造器的对象数组
for(Constructor constructor:constructors) {
String constructorname = constructor.getName();//获取每一个构造器的名字
String moder = Modifier.toString(constructor.getModifiers());//获取构造器的修饰符
Class[] c1 = constructor.getParameterTypes();//获取构造器所有构造方法中所有参数类型的Class对象数组
for(Class classname1:c1) {
if(classname1.isArray()) {
String type = classname1.getComponentType().getName()+"[]";//获取参数类型是数组的
System.out.println(type);
}
System.out.println(classname1.getName());//获取非数组类型的参数类型
}
}
Method[] methods = c.getDeclaredMethods();//获取所有方法的对象数组。
for(Method method :methods) {
String moder1 = Modifier.toString(method.getModifiers());//获取方法的修饰符
System.out.println(moder1);
Class methodreturn =method.getReturnType();//获取方法的返回值类型的Class对象
String type = null;
if(methodreturn.isArray()) {
type = methodreturn.getComponentType().getName()+"[]";//获取方法的返回值的类型
}else {
type = methodreturn.getName();//返回非数组类型
}
System.out.println(type);
Class[] methodclass = method.getParameterTypes();
String method1type =null;
for(Class methodtype:methodclass) {
if(methodtype.isArray()) {
method1type = methodreturn.getComponentType().getName()+"[]";//获取方法的参数的类型
}else {
method1type = methodtype.getName();//返回非数组的参数类型
}
System.out.println(method1type);
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
动态创建对象
在之前绝大部分创建都是通过new,因为在编译期间,就已经知道他们的对应类的名称,可是,如果当我们需要在运行期间来创建对象,就需要用到反射。
创建对象的两种方式
- 无参构造
try {
Class c = Class.forName("java.util.Date");
Date d = (Date) c.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 使用构造参数
第一步:获取指定类的Class对象。
第二步:通过Class对象获取满足指定参数类型要求的后遭函数方法类对象。
第三步:调用指定的Constructor对象的newInstance方法,传入对应参数作对象。
try {
Class c = Class.forName("java.util.Date");
Constructor constructor = c.getConstructor(long.class);
Date date = (Date) constructor.newInstance(12356);
System.out.println(date);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
调用方法
使用反射就可以取得指定方法的对象代表,根据对象代表可以去获取方法,在通过invoke,执行该方法。需要invoke返回值就是该方法的返回值。
代码:
Class c;
try {
c = Class.forName("com.example.test.Test22");
Test22 t = (Test22) c.newInstance();//获取对象代表
Method m = c.getDeclaredMethod("show", String.class);//根据方法名来获取方法,第二个参数是方法的参数类型。
m.invoke(t, "我是反射调用的方法!");//执行方法,第一个参数是作用的对象,后面是方法的参数。
//上面的方法执行只能用于方法是public,用private修饰就不可以的,所以有了下面的方法。
Method m1 = c.getDeclaredMethod("show1",String.class);
m1.setAccessible(true);//取消访问检查,如果不加这一句直接访问private方法,会报错
//Class com.example.test.Test23 can not access a member of class com
//.example.test.Test22 with modifiers "private"
m1.invoke(t,"我是反射调用的private的方法");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
访问成员变量的值
通过它的getXXX方法来获取字段的值,通过setXXX方法来动态修改指定对象的值。
代码:
Method m1 = c.getDeclaredMethod("show1",String.class);
m1.setAccessible(true);//取消访问检查,如果不加这一句直接访问private方法,会报错
//Class com.example.test.Test23 can not access a member of class com
//.example.test.Test22 with modifiers "private"
m1.invoke(t,"我是反射调用的private的方法");
Field f = c.getDeclaredField("name");//根据字段名来获取字段
f.set(t, "laoqiang");//通过set去改变set、get
System.out.println(t.getName());
Field f1 = c.getDeclaredField("age");
f1.setAccessible(true);//取消字段访问检查
f1.setInt(t, 23);
System.out.println(f1.getInt(t));//t就是创建的对象。
数组
数组也是一个对象,可以通过反射来查看各个数组的属性信息。
int[] a = new int[5];
System.out.println(a.getClass().getComponentType().getName());
Object c = Array.newInstance(int.class, 5);//创建数组对象,
//直接使用Array的newInstance,第一个是数组的类型,第二个是数组的大小。
for(int i = 0;i<5;i++) {
Array.set(c, i, i*3);//给数组设置值,也是通过set,第一个参数是作用的对象,
//第一个是数组的位置,第三个是值
}
for(int i = 0;i<5;i++) {
System.out.println(Array.getInt(c, i));
}
反射与动态代理
代理模式是Java中很常见的一种的设计模式,在企业应用高级框架大量用到,它的原理主要是反射。
静态代理
一个客户不想或者不能直接引用另一个对象,需要通过代理来间接操作目标对象,代理就是在客户端和目标对象之间起中介作用。
代码如下:
//客户的要求
package com.example.test;
public interface ClothFactory {
void makeCloth();//客户的要求
}
//中介、代理
public class ClothProxy implements ClothFactory {
//为什么要实现这个接口,你必须要具备用户的要求。
private Company c;//定义你寻找的真正能实现客户要求的公司。
public ClothProxy(Company c) {
super();
this.c = c;
}
@Override
public void makeCloth() {
// TODO Auto-generated method stub
c.makeCloth();//委托给真正的公司去做衣服
}
}
//真正执行力的公司
public class Company implements ClothFactory {
//为什么要实现这个接口,你必须要具备用户的要求。
@Override
public void makeCloth() {
// TODO Auto-generated method stub
System.out.println("我生产好了");
}
}
动态代理
原理:就是利用反射机制在运行时动态创建目标对象的代理对象。
动态代理的实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynaProxyHandler implements InvocationHandler {
private Object target;
//这个构造方法是创建代理类的实例
public Object newProxyInstance(Object target) {
this.target = target;
/**
* 第一个参数是:定义代理类的类加载器,比如在这,我传入一个object对象,这是会由这个对象的ClassLoader对象来对生成的代理对象进行加载。
* 第二个参数是:代理类要实现的接口,表示的是我将要给我需要代理的对象提供了什么接口,如果我们实现了一组接口,那么这个代理对象就对外宣称实现该接口,这样就能调用接口中的方法
* 第三个参数是:指派方法调用的调用处理程序,需要传入一个InvocationHandler对象,表示的是当我这个代理对象在调用方法的时候,会关联到哪一个invocationHandler对象,一般这个对象就是实现InvocationHandler接口的类
*/
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = null;
//这里可以添加在执行代理类方法之前所要执行的代码
result = method.invoke(this.target,args);
//这里可以添加在执行代理类方法之后所要执行的代码
return result;
}
}
测试代码如下:
DynaProxyHandler dph = new DynaProxyHandler();
ClothFactory c = (ClothFactory) dph.newProxyInstance(new Company());//
c.makeCloth();//这个调用方法涉及到多态,父类是接口,调用时实现其接口的方法。