主要指程序可以访问、控制和修改它本身状态或行为的一种能力。
在CS领域,反射是指一类应用,它们能够自我描述和自控制。也就是说这类应用通过采用某种机制对自己行为的描述self-representation和检测examination,并能根据自身的行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
允许动态发现和绑定类、方法、字段、以及其它由与语言产生的元素。
反射是java被视为动态语言的关键。
java反射机制主要提供以下功能:
java反射需要的类并不多,主要有java.lang.Class和java.lang.reflect包中的Filed、Constructor、Method、Array类。
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反射机制来获取类的详细信息、创建类的对象、访问属性值、调用类的方法等。
先获取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方法创建对象。
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();
}
}
在运行时动态创建目标对象的代理对象。
原文:https://www.cnblogs.com/DengSchoo/p/12847214.html