java reflection 作用_基本理解java Reflection(反射)

前言

一句话:反射是java中相对高级的功能,它给予了JVM在运行时去检查或者修改应用程序的能力

应用场景

1.通过全限定名去实例化外部对象或者自定义对象,比如Spring的IOC,通过XML或者注解生成用户自定义对象

2.类浏览器,可视化的开发环境。比如IDE工具等等

3.调试器和测试工具

反射的缺点

1.一定的性能开销,因为反射涉及到动态类型的解析,某些JVM优化在反射上是没用的,所以反射操作的性能比非反射的慢。

2.安全限制,反射在security manager下运行可能不起作用,这对于必须在受限制的安全上下文中运行的代码是重要的考虑

3.内部暴露,反射可以访问class的private类型的方法跟字段,可能与原来代码的本意背道而驰,产生bug

反射API的使用

类的反射基本使用

类成员(字段,方法,构造器)

数组和枚举(PS:这2种是特殊类型的类)

反射在类中的应用

Class对象是使用反射API的唯一入口,所以我们先要生成Class对象:

//全限定名去获取相应的class对象

Class> clazz1 = Class.forName("java.lang.String");

//对象的实例如果存在,也可以使用getClass方法

Class> clazz2 = "321".getClass();

//获得基本类型的Class对象

Class> intClazz = int.class;

//数组类型获取class对象

Class> cDoubleArray = Class.forName("[D");//与double[].class一致

Class> cStringArray = Class.forName("[[Ljava.lang.String;");//与String[][].class一致

有了Class对象后,我们基本上就可以干任何与反射事情了,我们先检索下这个类对象的类基本信息:

try{

Class> c = Class.forName("mycom.activiti.Man");//获得此类的修饰符类型

System.out.println("Class:\n" +c.getCanonicalName());

System.out.println("修饰符:\n"+Modifier.toString(c.getModifiers()));//获得类的泛型

System.out.println("类泛型:");

TypeVariable>[] tv =c.getTypeParameters();for (TypeVariable>typeVariable : tv) {

System.out.println(typeVariable.getName());

}//获得类实现的接口

System.out.println("实现的接口:");

Class>[] classes =c.getInterfaces();for (Class>it : classes) {

System.out.println(it);

}//继承的父类

System.out.println("继承的父类:");

Class> c1 =c.getSuperclass();

String fatherClasses= "";while(c1!=null){

fatherClasses+= c1.getName()+" ";

c1=c1.getSuperclass();

}

System.out.println(fatherClasses);//类上的注解

System.out.println("类上的注解:");            Annotation[] annotations = c.getAnnotations();

for (Annotation a : annotations) {System.out.println(a.annotationType());}

//获得此类中的所有(public、private、protected、包权限)类,接口,枚举

classes =c.getDeclaredClasses();

System.out.println("类中的类、接口、枚举:");for (Class>cz : classes) {

System.out.println(cz);

}//获得此类以及父类中所有的public类(内部类),接口,枚举

System.out.println("类以及父类中的公共类、公共接口、公共枚举:");

classes=c.getClasses();for (Class>cz : classes) {

System.out.println(cz);

}

}catch(Exception e) {

e.printStackTrace();

}

上面的代码中是检索类的信息,要说明的是在检索注解的时候,只能检索运行时的注解,而一般的@override,@Deprecated非运行时注解是无法检索的。

接下来我们检索一下类中的成员信息:

检索类中的成员信息主要有2种类别的方法

Class Methods for Locating Fields

List of members?Inherited members?Private members?

Class Methods for Locating Methods

List of members?Inherited members?Private members?

Class Methods for Locating Constructors

List of members?Inherited members?Private members?

检索字段跟方法我就不说了,一看就懂,获得构造器中父类的构造器是获得不到的,因为构造器不能继承。

我们写个demo来检索下成员信息:

//获得类的包

Package p =c.getPackage();

System.out.println("包:");

System.out.println(p.getName());

System.out.println("类的构造器:");

Constructor>[] cs =c.getDeclaredConstructors();for (Constructor>constructor : cs) {

System.out.println(constructor.toGenericString());

}

System.out.println("类的所有字段:");

Field[] fields=c.getDeclaredFields();for(Field field : fields) {

System.out.println(field.toGenericString());

}

System.out.println("类及父类中的非private字段:");

fields=c.getDeclaredFields();for(Field field : fields) {

System.out.println(field.toGenericString());

}

System.out.println("类的所有方法:");

Method[] methods=c.getDeclaredMethods();for(Method method : methods) {

System.out.println(method.toGenericString());

}

System.out.println("类及父类中的非private方法:");

methods=c.getMethods();for(Method method : methods) {

System.out.println(method.toGenericString());

}

检索就说到这里,只要稍微熟悉了Class的几个方法,举一反三都很简单。

接下来我们进入重点,反射生成实例,字段注入,以及方法调用。

首先有一个简单的man类,有一个私有构造器,和一个public与private字段,还有一个printMsg方法如下:

package mycom.activiti;

public classMan {public String name = "321";private intage;privateMan() {}public voidprintMsg(String str) {

System.out.println(name+ ":" + age + ":" +str);

}

}

操作如下:

public static void main(String... args) throwsException {

Class> c = Class.forName("mycom.activiti.Man");//替换成自己的类

Constructor>[] constructors =c.getDeclaredConstructors();

Constructor> con = null;for (Constructor>constructor : constructors) {if (constructor.getParameterTypes().length == 0) {//轮询匹配无参构造器

con =constructor;

}

}

con.setAccessible(true);//以防是私有构造器,通过此方法获取访问权限

Man man =(Man) con.newInstance();

Field field= c.getDeclaredField("name");//字段注入

field.set(man, "张三丰");

field= c.getDeclaredField("age");

field.setAccessible(true);//私有字段获得访问权限

field.setInt(man, 28);

Method m= c.getMethod("printMsg", String.class);

m.invoke(man,new Object[] { "你好"});//调用方法

}

结果:

张三丰:28:你好

说几点需要注意的地方,我这里是偷懒所以直接抛Exception,再者说下class直接new Instance() 和 构造器newInstance()的区别,

首先这两者都是可以实例化对象,但是通过构造器的newInstance()会比class的好得多,

Class.newInstance() 不能实例化private构造器的对象,而Constructor.newInstance()可以通过设置构造器访问权限生成实例对象

还有一点,Constructor.newInstance()会包装在生成对象时抛出的异常信息,便于异常的后续处理与定位错误

我们把实体的构造器改为抛出异常:

publicMan() {throw new RuntimeException("321");

}

当用Class.newInstance()的时候,会直接抛出运行时异常,

而Constructor.newInstance()会包装成InvocationTargetException异常,这样可以通过try Catch 捕获做后续处理!

还需要说明一点的事,8大基本类型与数组和void都有对应的class,方便在反射时判断或检索字段或者修饰符信息

int - int.class,

int[] - int[].class //一维数组

void - void.class //空方法修饰符

......

建议大家多看官方文档上面说的非常详细!

更多的信息请移步:  http://docs.oracle.com/javase/tutorial/reflect/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值