java反射
1.java的反射机制可让我们在编译期之外的运行期检查类,接口,变量以及方法的信息,还可在运行期实例化对象,
调用方法,通过get/set获取变量的值
2.反射时需要先获取类的Class对象,可反射所有类型包括基本类型(int等),即使是数组与之关联的Class对象
Class c=Class.forName("类的全名"),找不到类就会抛出ClassNotFoundException
c.getName() //获取完整包路径名
c.getSimpleName() //只获取类名
c.getModifiers() //获取修饰符
c.getSuperclass() //获取父类对象
c.getInterfaces() //获取接口数组
c.getConstructors() //获取构造数组
c.getMethods() //获取所有方法数组
c.getFields() //获取成员变量数组
c.getAnnotation() //获取类的注解
c.getConstructor.getParameterTypes() //获取构造方法的参数数组
c.getConstructor().newInstance("可变参数") //实例化一个类
3.反射方法:Method[] methods=MyObject.class.getMethods();
4.变量
a.c.getField().getName() //获取变量名称
b.c.getField().getType() //获取变量类型
c.当获取Field引用时,可通过调用Field.get()或Field.set()获取或设置变量的值
5.方法
a.c.getMethods() //返回的数组中包含指定类中声明为public的所有变量集合
b.c.getMthod("doSth",null) //获取的方法没有参数,在第二个参数传入null即可
c.c.getMthod(“doSth”).getParameterTypes() //获取指定方法的参数
d.c.getMethod("doSth").getReturnType() //获取指定方法的返回值类型
调用一个方法采用c.getMethod("doSth").invoke(null,"parameter-value") //null为要调用方法的对象,
如果是静态方法调用,则可用null代替指定对象作为invoke()的参数;(doSth不是静态方法时,就需要传入真正的实例,而不是null)
6.私有变量和方法
a.Field privateField=c.getDeclaredField("privateVar") //获取一个当前类的私有变量,而不是父类中的
b.privateField.setAccessible(true) //关闭指定类Field实例的反射访问检查,设置true之后不论私有、受保护的以及
包访问的作用域,不论在任何地方都可以被访问到
c.c.getDeclaredMethod(String name,Class[] paramTypes)或者c.getDeclaredMethods()可获取私有方法
d.c.getMethod(String name,Class[] paramTypes)和c.getMethod()获取公有方法
e.c.getMethod().setAccessible(true) //关闭指定Method实例的反射访问检查
7.注解:插入代码中的一种注释或者说是一种元数据
a.@interface 表名这是一个注解 @Retention(RetentionPolicy.RUNTIME) //表示注解可以在运行期通过反射访问
b.@Target(ElementType.TYPE) 表示注解只能用在类型上(比如类和接口) Type也可改成Field或Method
类注解:
Annotation[] annotations=c.getAnnotations(); //获取类注解
或者c.getAnnotation(Aaaa.class)获取指定访问类的注解
方法注解:
Annotation[] annotations=c.getMethod().getDeclaredAnnotations();//获取方法上的注解
或者c.getMethod("name",null).getAnnotation(Aaaa.class);
参数注解:
Annotation[][] paramAnnotation=c.getMethod("name",null).getParameterAnnotations();
Class[] paramType=c.getMethod("name",null).getParameterTypes();
通过instanceof判断是否为某个注解
变量注解:
c.getDeclaredField("name").getDeclaredAnnotations();
或者c.getDeclaredField("name").getAnnotation(Aaa.class);
8.泛型:Java泛型信息并非完全无法在运行期获得,部分情况是可以获取得到的(通过被参数化类型的方法及变量中找)
a.使用泛型的场景:
1.声明一个需要被参数化的类/接口
2.使用一个参数化类
b.![]()

9.数组:java.lang.reflect.Array处理数组
a.int[] arr=(int[])Array.newInstance(int.class,3);
b.不通过反射获取数组的Class对象
Class stringClass=String[].class;
c.通过反射获取对象
Class intArr=Class.forName("[I"); //jvm中I代表int类型,[代表数组,同样使用其他原生数据类型
d.普通对象类型数组
Class stringClass=Class.forName("[Ljava.lang.String;"); //获取一个数组的Class对象后,
则可通过stringClass.getComponentType();获取数组的成员类型(数组存储的数据类型)
10.动态代理:可在运行期动态创建接口的实现
Proxy.newProxyInstance()创建动态代理,参数:
a.类加载器(ClassLoader)用来加载动态代理类
b.待实现的接口数组
c.一个InvocationHandler把所有方法的调用都转到代理上
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class },
handler);
d.使用场景 1.数据库连接及事务管理
2.单元测试中的动态Mock(生成)对象
3.自定义工厂与依赖注入(DI)容器之间的适配器
4.类似AOP的方法拦截器
11.动态类加载与重载:一个类只能被同一个ClassLoader实例加载一次
a.类加载过程是一个递归的模式,一个类所有相关的类都会被加载
b.一个类加载器被用来加载一个类的时候,会首先调用该类加载器的父加载器来加载,
当父加载器服务找到该类时,才会尝试加载该类
c.类加载顺序:c.getClassLoader()
1.检查类是否已经被加载
2.未被加载,先调用父加载器加载
3.父加载器不能加载该类,则尝试加载该类
d.动态类重载:java内置的类加载器在加载一个类之前会检查其是否已经被加载,
因此重载一个类时无法使用Java内置的类加载器(不能使用已经加载过类的类加载器来重载一个雷),
想要重新动态加载一个类需要手动继承ClassLoader,另外c.getClassLoader().resolve()完成被加载类需要的相关链接,
但是resolve是一个final方法,因此没重载一个类都需要一个新的ClassLoader的子类
e.自定义重载:Java中类的全名(包名+类名)作为一个唯一标识来呗ClassLoader实例加载
一个类A分别被两个雷加载器加载,则会被认为是两个不同的类,尽管类全名的完全一样的,如果进行类的实例转换会报ClassCastException
解决:
1.标记变量类型为一个接口,只重载其实现类
2.标记变量类型为一个超类(父类),只重载其子类
参考:https://github.com/mjiderhamn/classloader-leak-prevention
posted on 2018-03-06 18:18 xiaojiayu0011 阅读(167) 评论(0) 收藏 举报
浙公网安备 33010602011771号