Java反射
反射
- 反射定义
- java反射API
- 使用反射获取类信息
- 使用反射创建对象
- 使用反射调用方法和操作成员变量
- 代理模式
反射概述
主要指程序可以访问、控制和修改它本身状态或行为的一种能力。
在CS领域,反射是指一类应用,它们能够自我描述和自控制。也就是说这类应用通过采用某种机制对自己行为的描述self-representation和检测examination,并能根据自身的行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
Java的反射机制
允许动态发现和绑定类、方法、字段、以及其它由与语言产生的元素。
反射是java被视为动态语言的关键。
java反射机制主要提供以下功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法。甚至可以调用private方法
- 生成动态代理
Java反射API
java反射需要的类并不多,主要有java.lang.Class和java.lang.reflect包中的Filed、Constructor、Method、Array类。
- Class类:Class类的实例表示正在运行的java应用程序中的类和接口
- Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,封装反射类的属性的类。
- Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限,封装了反射类的构造方法
- Method类:提供关于类或接口上单独某个方法的信息。封装反射类方法的类
- Array类:提供动态创建数组和访问数组的静态方法。所有方法都是静态方法。
Class类是反射的起源,要反射一个类,必须先为它产生一个Class类的对象,才能通过Class对象获取其他想要的新。
Class类
java程序运行时,系统会对所有对象进行所谓的运行时类型标识,用于保存这些类型信息的类就是Class类。Class类封装一个对象和接口运行时的状态。
、每种类型都有一个Class对象。需要创建某个类的实例时,JVM首先检查所要加载的类的Class对象是否已经存在。如果还不存在,JVM根据类名查找对应字节码文件并加载,接着创建对应的Class对象,最后才创建出这个类的实例。
java基本数据类型(八种)和关键字void也都对应一个Class对象。每个数组属性也被映射为Class对象,相同类型和相同维数的数组都共享该Class对象。
运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类和接口的类型信息。想要获取类和接口的对应信息,必须先获取这个Class对象。
-
获得Class对象
三种方式获得Class对象:
-
调用Object类的getClass()方法来得到Class对象,也是常见的方法
MyObject x; Class c1 = x.getClass();
-
使用Class类的forName()静态方法获得与字符串对应Class对象。
Class c2 = Class.forName("java.lang.String");
参数字符串必须是类或接口的全限定名
-
使用类型名.class获取该类型的Class对象
Class c11 = Manager.class; Class c12 = int.class; Class c13 = double[].class;
-
-
常用方法
提供大量方法来获取所代表实体(类、接口、数组、枚举、注解、基本类型或void)的信息。
常用方法:
使用java反射
使用java反射机制来获取类的详细信息、创建类的对象、访问属性值、调用类的方法等。
获取类型信息
先获取Class对象,再调用方法来获取信息。
-
获取指定类对应的Class对象
Class clazz = Class.forName("java.util.ArrayList");
-
获取类的包名
getPackage()方法获得一个java.lang.Package类的对象。
通过Package类提供的方法可以访问相关的信息。
String packageName = clazz.getPackage().getName(); //获取全名
-
获取类的修饰符
int mod = clazz.getModifiers();//获取整数表示的类修饰符值
把这个整数值转换到对应的字符串,使用java.lang.reflect.Modifier类提供的toString(int mod)静态方法
String modifier = Modifier.toString(mod);
\ -
获取类的全限定名
String className = clazz.getName();
-
获取类的父类
Class superClazz = clazz.getSuperClass();
-
获取类实现的接口
class[] interfaces = clazz.getInterfaces();
-
获取类的成员变量
Class对象的getFields()方法获取到此Class对象所对应的实体的所有public字段(成员变量)。如果要获取所有的字段,可以使用getDeclareFields()方法
Field[] fields = clazz.getDeclaredFields();
Filed类用来代表字段的详细信息。通过调用Field类提供的相应方法就可以获取字段的修饰符、数据类型、字段名等信息。
for (Filed field : fields) { String modifier = Modifer.toString(field.getModifiers());//访问修饰符 Class type = filed.getType();//数据类型 String name = field.getName(); //字段名 if (type.isArray()) { //是数组类型需要特别处理 String arrType = type.getComponentType().getName()+"[]"; } }
-
获取类的构造方法
Class对象的getConstructors方法获取到所有public的构造方法。getDeclaredConstructors()获取所有构造方法
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor类用来表示类的构造方法的相关信息。可以获得该构造方法的修饰符、构造方法名、参数列表等信息。
for (Constructor constructor : constructros) { String name = constructor.getName();//得到构造方法名 String modifier = Modifier.toString(constructor.getModifiers()); Class[] paramTypes = constructor.getParameterTypes();//得到方法的参数列表 for (int i = 0; i < paramTypes.length; i++) { if (i > 0) System.out.println(", "); if (paramTypes[i].isArray()) { System.out.println(paramTypes[i].getComponentType().getName() + "[]"); } else { System.out.println(paramTypes[i].getName()); } } }
-
获取类的成员方法
Class对象的getMethods方法获取到所有public的构造方法。getDeclaredMethods()获取所有构造方法
Methods[] methods = clazz.getDeclaredMethonds();
Methond类用来代表类的成员方法的相关信息。通过调用Method类提供的响应方法也可以获得该成员方法的修饰符、返回值类型、方法名、参数列表等信息。
for (Method method : methods) { String modifier = Modifier.toString(method.getModifiers()); Class returnType = method.getReturnType();//返回类型 if (returnTyper.isArray()) { System.out.println(returnType.getComponentType().getName()+"[]"); } else { System.out.println(returnType.getName()); } Class[] paramTypes = methods.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { if (i > 0) System.out.println(", "); if (paramTypes[i].isArray()) { System.out.println(paramTypes[i].getComponentType().getName() + "[]"); } else { System.out.println(paramTypes[i].getName()); } } }
创建对象
-
使用无参构造方法
调用Class对象的newInstance()方法。
Class c = Class.forName("java.util.ArrayList"); List list = (List) c.newInstance();
指定的类没有无参构造方法会报NoSuchMethodException异常
import java.util.Date public class NoArgsCreateInstanceTest { public static void main(String[] args) { Date currentDate = (Date)newInstance("java.util.Date"); System.out.println(currenDate); } public static Object newInstance(String className) { Object obj = null; try { obj = Class.forName(className).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } return obj; } }
-
使用带参构造方法
首先需要获得指定名称的Class对象,然后通过反射获取满足指定参数类型要求的构造方法信息类(java.lang.reflect.Constructor)对象,调用newInstance方法创建对象。
- 第一步:获取指定类的Class对象
- 第二步:通过Class对象获取满足指定参数类型要求的构造方法类对象。
- 调用指定Constructor对象的newInstance方法传入对应的参数值,创建对象。
package JavaIO; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Date; public class AgrsCreateInstance { @SuppressWarnings("unchecked") public static void main(String[] args) { try { Class clazz = Class.forName("java.util.Date"); Constructor con = clazz.getConstructor(long.class); Date date = (Date)con.newInstance(123456789L); System.out.println(date); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
调用方法
使用反射可以获得指定类的指定方法的对象代表,方法的对象代表就是java.lang.reflect.Method类的实例,通过Method类的invoke方法可以动态调用这个方法。
invoke()方法的第一个参数是对象类型,表示要再指定的这个对象上调用这个方法,第二个参数是一个可变参数,用来为这个方法传递参数值;invoke方法的返回值即为动态调用指定方法后的实际返回值。
若要访问一个反射调用类的私有方法,矿可以在这个私有方法对应的Method对象上调用setAccessible(true)来取消java语言对本方法的访问检查,然后再调用invoke方法来真正执行这个私有方法。
package JavaIO;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectInvokeMethodTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("JavaIO.Product");
Product prod = (Product) clazz.newInstance();
// 获取一个Prodcut类的方法名为setName,带有参数类型为String
Method method1 = clazz.getDeclaredMethod("setName", String.class);
//调用函数获取返回值
Object returnValue = method1.invoke(prod, "爪哇");
System.out.println("返回值:"+returnValue);
//获取dispalyInfo的对象代表
Method method2 = clazz.getDeclaredMethod("displayInfo");
// 取消访问检查 允许访问私有方法 否则会报错
method2.setAccessible(true);
returnValue = method2.invoke(prod);
System.out.println("返回值:"+returnValue);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Product{
private static long count = 0;
private long id;
private String name = "NoBody";
public Product() {
System.out.println("默认构造方法");
id = ++count;
}
public long getId() {return id;}
public void setId(long id ) {this.id = id;}
public String getName(){return name;}
public void setName(String name) {
System.out.println("调用setName方法");
this.name = name;
}
private void displayInfo(){ //私有方法
System.out.println(getClass().getName() + "[ id="+id+",name="+name+" ]");
}
}
访问成员变量的值
使用反射可以取得类的成员变量的对象代表,成员变量的对象代表是java.lang.reflect.Field类的实例,可以使用getXXX方法来获取指定对象上的值,也可以调用它的setXXX方法来动态修改指定对象上的值,其中XXX表示成员变量的数据类型。
package JavaIO;
import java.lang.reflect.Field;
public class ReflectFieldTest {
@SupepressWarnings("unchecked")
public static void main(String[] args) {
try {
Class c = Class.forName("JavaIO.Product");
Product prod = (Product)c.newInstance();
//调用私有属性
Filed idField = c.getDeclaredField("id");
idFiled.setAccessible(true); //取消对本字段的访问检查
//设置prod对象的idField成员变量的值
idFiled.setLong(prod, 100);
//获取idField成员变量的值
System.out.println("id="+idFiled.getLong(prod));
Filed nameField = c.getDeclaredField("name");
nameFiled.setAccessible(true); //取消对本字段的访问检查
//设置prod对象的idField成员变量的值
nameFiled.set(prod, "张三");
//获取idField成员变量的值
System.out.println("id="+nameFiled.get(prod));
}
}
}
操作数组
数组也是一个对象,可以通过反射来查看数组的各个属性信息。
short[] sArr = new short[5];
String str = sArr.getClass().getComponentType().getName();
数组通过反射动态创建,利用java.lang.reflect.Array类来操作
import java.lang.reflect.Array
Object obj = Array.newInstance(int.class, 5);
for (int i = 0; i < 5; i++) {
Array.setInt(obj, i, i * 10);
}
for (int i = 0; i < 5; i++) {
System.out.println(Array.getInt(obj, i));
}
反射与动态代理
静态代理
public interface ClothingFactory { //服装厂接口
void productClothing();//生产衣服接口
}
public class LiNingCompany implements ClothingFactory { //生产公司类
public void productClothing() {
System.out.println("生产出一批衣服");
}
}
public ProxyCompany implements ClothingFactory { //中介代理类
private ClothingFactory cf;
public ProxyCompany(ClothingFactory cf){
this.cf = cf;
}
public void productClothing() {
System.out.println("收取10000中介费");
cf.productClothing(); //委托真正的服务公司生产服装
}
}
public class Customer {
public static void main(String[] args) { //顾客类
//找一家中介公司
ClothingFactory cf = new ProxyCompany(new LiNingCompany());
cf.productClothing();
}
}
特征代理类和目标对象的类都已经在编译期间就已经确定下俩,不利于程序的扩展。
需要新增的时候需要一一添加
动态代理
在程序运行时根据需要动态创建目标类的代理对象。
java.lang.reflect包中提供了对动态代理的支持的类和接口。
-
InvocationHandler接口:代理类的处理类都要实现这个接口,接口中只要一个方法
public Object invoke(Object proxy, Method method, Object[] args) thorws Throwable;
proxy指代理类,method是被代理的方法的CLass对象,第三个参数为args传给该方法的参数值数组。
-
Proxy类:提供创建动态代理类和实例的静态方法。
import java.lang.reflect.*; public class DynaProxyHandler implements InvocationHandler { private Object target; //目标对象 public Object newProxyInstance(Object target) { this.target = target; return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterface(), this ) } public Object invoke(Object proxy, Method method, Object[] args) thorws Throwable { Object result = null; try { result = method.invoke(this.target, args); } catch(Exception e){ throw e; } return result ; } }
//客户端的代码修改 public class Customer { public static void main(String[] args) { //顾客类 //找一家中介公司 DynaProxyHandler handler = new DynaProxyHandler(); ClothingFactory cf2 = (ClothingFactory)handler.newProxyInstance(new LiNingCompany()); cf2.productClothing(); } }
在运行时动态创建目标对象的代理对象。