反射机制
1.反射机制由来
1.MVC设计模式 ,单标CRUD , 分页,上传
2.传统的代码开发之中,发现所有的操作只能够使用硬编码的方式进行操作,例如:MVC设计模式的请求操作类型都需要额外传递一个附加参数,这样的操作本来就很麻烦
3.每一个Servlet 即使使用了Annotation 进行配置,实际上代码也没少多少
4.java语言新增了反射机制
2.反射机制应用
1.反射机制的基本概念
2.分析简单Java类与反射的关系
3.利用反射解决Servlet代码过多的问题
4.利用反射来解决工厂设计模式,代理设计模式的操作
5.动态代理模式,CGLIB实现的动态代理设计模式
6.使用Annotation 编写注解操作
3.具体内容
3.1 反射的基本概念
如果在正常的情况下,如果是使用一个类,则必须按照如下的步骤操作:
使用import导入类所在的包
明确的使用类名称或接口定义对象
通过关键字 new 进行类对象实例化(构造方法: java.lang.reflect.Constructor)
产生对象可以使用”对象.属性” 进行类中属性的使用(属性: java.lang.reflect.Field)
通过”对象.方法()” 调用类中的方法(方法: java.lang.reflect.Method)
而反射的过程,不需要明确类型的对象,所有的对象使用Object表示:
可以利用Object与反射机制的混合调用类中的方法
3.1.1 Class类
Class类是整个反射操作的源头,而这个类的定义如下:
public final class Class<T> //反射的泛型几乎无用 使用的时候就用 “?” extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
但如果想要使用Class类进行操作,那么就必须首先产生Class类的实例化对象.
有三种方式产生Class类的实例化对象:
方式一.Object类提供一个返回Class类对象的方法: public Class<?> getClass()
Class class = new 类名().getClass();
方式二.利用 “类.class” 获得,日后见得最多的是Hibernate上
Class class = 类名.class
方式三.利用Class类的static方法取得:public static Class<?> forName(String className) throwsClassNOtFoundException;
Class class = Class.forName(“类的路径”);
如果是程序设计人员,使用最多的一定是forName()方法,但如果是使用者,肯定会使用”类.class”.通过之前的分析知道,工厂设计模式最好利用反射机制解决 耦合问题
面试题: 请解释Object类之中的所有方法以及每一个方法使用上的注意事项
1.对象克隆:
public Object clone() throws CloneNotSupportedException
克隆对象所在的类一定要实现java.lang.Cloneable接口,而且子类只需要继续调用Object类的Clone()就可以实现克隆操作.
2.对象输出:
public String toString()
直接输出对象时会默认调用toString()方法
3.对象比较:
public boolean equals()
当保存Set集合时,会依靠hashCode()和equals()判断是否为重复对象
4.获取hash码,
public int bashCode()
可以理解为为每一个类对象的唯一编码,比较时会先判断编码是否相同,而后比较equals()里面的内容
5.取得Class类对象
public Class<?> getClass()
通过一个已经实例化好的对象进行对象的反射操作
6.线程等待:
public void wait() throws IntercepterException
执行到此代码时线程要等待执行,一直到执行notify() 或 notifyAll()
唤醒第一个线程:notify()
唤醒所有线程:notifyAll()
7.垃圾回收前释放:
public void finalized() throws Throwable
当使用GC回收无用的垃圾空间时,默认调用.
3.1.2 利用反射 实例化对象
class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance()方法默认去掉用类之中的无参构造方法进行操作:
public T newInstance() throws InstantiationException, IllegalAccessException
此泛型使用不到
在我们整个程序的编写之中,即使完全不知道类的结构,即使不导入包.类,也可以对类进行实例化操作.
但是如果我们使用反射实例化类对象,必须满足类中有无参构造方法,因为默认使用newInstance()方法只能够找到无参构造方法.
在Class类里面定义了可以获得一个类之中构造方法的操作:
1. 取得类之中的全部构造方法:
public Constructor<?>[] getConstructors() throws SecurityException
2.取得类之中指定的构造方法:
public Constructor<T> getConstructor(Class<>... parameterTypes) throws NoSuchMethodException, SecurityException
所以如果现在想要进行制定构造方法的调用,就必须将关注点放在 Constructor 类之中,在此类中定义了一个对象实例化方法: (前提:反射的类中必须要有 无参构造方法)
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
因为如果是通过构造方法实例化对象 规格不统一,所以在进行简单 java类操作的时候 必须有无参构造方法;;
3.1.3调用类种方法
取得了一个类的实例化对象之后,下面主要的任务是要调用类之中可以使用的构造方法,对于一个类之中的方法,实际上有两类:
1. 取得我们父类继承而来的方法
取得全部方法:
public Method[] getMethods() throws SecurityException
2.取得指定方法:
public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,
SecurityException
2.取得本类中定义的方法
取得全部方法:
public Method[] getDeclaredMethods() throws SecurityException
取得指定方法:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
两种方法定义上区别不大,因为方法大都是public,所以两种方式取得的结果是没有区别的.
程序直接调用Method类之中的toString()实现输出,如果用户想要自己的输出格式,就需要调用Method类中的如下方法:
1.取得方法修饰符:public intgetModifiers();
.程序之中找的不是public等关键字,而是关键字对应的数字,例如:
public --> 1
static --> 200
final --> 30
---> public static ---> 201
---> public static final ---> 231
但是在程序之中,我们不需将其更换成能读懂的信息,可以借助使用Modifier类完成,此类中可以直接利用toString()方法将数字转化为字符串,
转换:public static String toString(int mod).
2.取得方法的返回参数类型:
public Class<?> getReturnTypes()
met[i].getReturnType().getSimpleName() 去包名
3.取得方法的参数:
public Class<?> getParameterTypes()
4.获取方法的抛出异常:
public Class<?> getExceptionTypes()
package com.reflect; import java.lang.reflect.Method; import java.lang.reflect.Modifier; interface Message{ void get(); } class Student implements Message{ public void get(){} public void fun(){} public void print(){} } public class ReflectDemo { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.reflect.Student"); Method met[] = cls.getMethods(); for (int i = 0; i < met.length; i++) { System.out.print(Modifier.toString(met[i].getModifiers()) + " ");//获取修饰符 System.out.print(met[i].getReturnType().getSimpleName() + " ");//获取返回值类型 System.out.print(met[i].getName() + "("); Class<?> params[] = met[i].getParameterTypes();//取得全部参数 if (params.length>0) { //有参数 for (int i1 = 0; i1 < params.length; i1++) { System.out.print(params[i1].getSimpleName() + " arg-"+i1); if (i1<params.length-1){ //还有参数 System.out.print(","); } } } System.out.print(")"); Class<?> exps[] = met[i].getExceptionTypes(); if (exps.length>0){ //有异常抛出 System.out.print("throws"); for (int i1 = 0; i1 < exps.length; i1++) { System.out.print(exps[i1].getSimpleName()); if (i1<exps.length-1){ System.out.print(","); } } } System.out.println(); //换行 } } }
此类代码只会在编写开发工具中使用,而我们使用的所谓的随笔提示功能就是依据以上的代码实现的( . 提示功能),但是与我们开发者密切关联的一定是利用Method调用类中的方法,而且在Method类里面提供了一个重要的方法
调用:
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
package com.reflect; import java.lang.reflect.Method; import java.lang.reflect.Modifier; class StudentA { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Reflect2 { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.reflect.StudentA"); Object obj = cls.newInstance(); //实例化对象 Method setNameMethod = cls.getMethod("setName",String.class); Method getNameMethod = cls.getMethod("getName"); setNameMethod.invoke(obj,"SMITH");//对象.setName("SMITH") System.out.println(getNameMethod.invoke(obj)); } }
正因如此,所以开发之中才强调 setter,getter 方法必须按照严格的要求编写;
3.1.5 调用类中的一个属性(尽量不要使用)
关于类之中的属性也可以直接利用反射操作,二支持的方法有两类:
1. 取得所有继承而来的属性
取得全部属性:
public Field[] getFields() throws SecurityException
取得指定属性:
public Field getField(String name) throws NoSuchFieldException, SecurityException
2. 取得本类定义的属性
取得全部属性:
public Field[] getDeclaredFields() throws SecurityException
取得指定属性:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
范例:取得一个类中的全部属性
package com.reflect; import java.lang.reflect.Field; interface MessageA{ public static final String INFO = "Hello worg."; } class Person { private String name; private Integer age; } class StudentB extends Person implements MessageA{ private String school; private Double price; } public class GetMethods { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.reflect.StudentB"); { //取得继承而来的全部属性 Field fields[] = cls.getFields(); for (int i = 0; i < fields.length; i++) { System.out.println(fields[i]); } } { //取得本类中的全部属性 Field fields[] = cls.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { System.out.println(fields[i]); } } { //取得父类定义的属性 Field fields[] = cls.getSuperclass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { System.out.println(fields[i]); } } } }
在Field类里还定义有进行属性调用的方法:
设置属性内容:
public void set(Object obj,Object value) throws IllegalArgumentException, IllegalAccessException
取得属性内容:
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
在Constructor,Method,Field类有一个共同的父类Accessible里面定义了可以取消封装的操作
范例:直接操作属性
package com.reflect; import java.lang.reflect.Field; class StudentC{ private String school; } public class FieldDemo { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.reflect.StudentC"); Object obj = cls.newInstance(); Field schoolField = cls.getDeclaredField("school"); schoolField.setAccessible(true); schoolField.set(obj,"清华大学"); System.out.println(schoolField.get(obj)); } }
在开发中,灵活使用Class,Constructor,Method,Field,就可以使用反射进行一系列代码的操作.