反射是Java中框架设计的核心,通过对类的构造、属性、方法等数据的获取提供抽象的底层构建。
反射需要先获得类的class字节码,由JVM类加载器(ClassLoader)负责加载,并在内存中缓存class的内部结构。借助于Java的反射机制允许快速对类的结构数据进行访问和调用。
在类运行时,能够动态获取并访问其构造结构的机制称为反射机制。
获得class字节码文件的三种方式:
Class classObj1 = new User().getClass();
Class classObj2 = User.class;
Class classObj3 = Class.forName("com.chiang.pojo.User");
注:
使用反射获得类的实例对象:
Object obj = classObj.newInstance();
判断对象的类型:
if(obj.getClass() == User.class){
System.out.println("User类型");
}
else if(obj instanceof Human){
System.out.println("Human类型");
}
获得类的构造方法:
Constructor[] constructors = classObj.getConstructors();
//遍历构造方法
for(Constructor con : constructors){
//获取构造方法的名称
System.out.println(con.getName());
}
根据参数列表获得指定的构造方法:
Constructor con = classObj.getConstructor(int.class,String.class);
obj = con.newInstance(100,"chiang");
User user = (User) obj;
System.out.println(user.details());
根据构造方法得到类的实例对象:
obj = con.newInstance(null);
获得类的属性:
//获取所有属性,可以访问到父类的属性及所有访问级别允许访问到的属性
Field[] fields = classObj.getFields();
//遍历属性对象
for(Field f : fields){
//属性名
System.out.println(f.getName());
}
//获取所有在该类中直接定义的属性
Field[] fields = classObj.getDeclaredFields();
//遍历属性对象
for(Field f : fields){
//属性名
System.out.print(f.getName()+"\t");
//获得属性的类型
Class fieldClass = f.getType();
System.out.println(fieldClass+"\t");
}
//获取指定的属性对象
Field field = classObj.getDeclaredField("age");
Constructor con = classObj.getConstructor(int.class,String.class,String.class,int.class);
obj = con.newInstance(100,"chiang","123",20);
//获取属性的值
Object fieldValue = field.get(obj);
System.out.println(fieldValue);
获取类的方法:
Method[] methods = classObj.getMethods();
for(Method m : methods){
//方法的名称
System.out.print(m.getName());
//方法的返回值类型
Class retunClass = m.getReturnType();
System.out.print(",返回值类型:"+retunClass.getName());
//方法的参数列表
System.out.print(",参数列表:[");
Class[] paramTypes = m.getParameterTypes();
for(Class paramType : paramTypes){
System.out.print(paramType.getName()+" ");
}
System.out.println("]");
}
Constructor con = classObj.getConstructor(int.class,String.class,String.class,int.class);
obj = con.newInstance(100,"chiang","123",20);
//获取指定的方法 第一个参数表示方法的名称,第二个参数表示参数类型
Method method = classObj.getMethod("setUserName", String.class);
//反射调用方法 第一个参数表示执行该方法的对象实例,第二个参数 表示方法需要的参数列表
Object returnValue = method.invoke(obj, "tions");
System.out.println(method.getName()+"方法调用后的返回值是:"+returnValue);
method = classObj.getMethod("getUserName", null);
returnValue = method.invoke(obj, null);
System.out.println(method.getName()+"方法调用后的返回值是:"+returnValue);
注解和Interface,class一样也是一种数据类型,是JDK1.5引入的类型,用于对Java中的类、方法、字段等进行标注。注解可以让开发人员更了解代码功能,让JVM更好地识别类的功能。
注意事项:
定义注解: 注解中只能定义属性,不包含方法
public @interface 注解名
定义属性: 属性的访问修饰符缺省为public abstract,返回值类型不能为void
public abstract 返回值类型 属性名();
使用注解: 类、方法、属性、参数上的使用
@注解名
默认情况下注解中的属性必须显式调用,如果希望属性是选填项需要设置默认值
public abstract 类型 属性名() default 默认值;
注解解析:
注解如何被JVM识别?
JVM通过反射机制动态获取到标注在类、方法、字段上的注解,通过识别注解实现特定的功能,如检测数据是否需要必填,提供数据校验等功能。
反射中用于获取注解的方法:
方法 | 作用 |
---|---|
isAnnotationPresent(annotationClass) | 是否作用指定的注解类型 |
getAnnotation(annotationClass) | 获取指定的注解对象 |
getAnnotations() | 获取包含父类在内作用过的所有注解 |
getDeclaredAnnotations() | 获取直接定义的所有注解 |
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class ParseAnnotation {
public static void main(String[] args) throws Exception {
Class<User> userClass = (Class<User>) Class.forName("com.chiang.annotation.demo.User");
//判断user类是否作用了MyAnnotation注解
if(userClass.isAnnotationPresent(MyAnnotation.class)){
System.out.println("User类作用了MyAnnotation注解");
}
//userName属性是否作用MyAnnotation注解
Field field = userClass.getDeclaredField("userName");
if(field.isAnnotationPresent(MyAnnotation.class)){
System.out.println("userName属性作用了注解");
}
//获取作用的user类上的所有注解
Annotation[] annotations = userClass.getAnnotations();
for(Annotation a : annotations){
System.out.println(a.annotationType());
//根据注解类型进行拆箱
if(a instanceof MyAnnotation){
MyAnnotation my = (MyAnnotation) a;
//获取注解中写入的属性的值
System.out.println(my.method());
}
}
}
}
元注解
元注解是作用在注解上注解,能够让自定义注解在程序运行时被JVM所识别。元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable。
自定义注解需要结合元注解才能被JVM识别。
Retention 保留期的意思。
当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
标识注解是否写入doc文档
允许父类作用注解后子类被继承
指定注解作用的场景对象
注解 | 作用的场景对象 |
---|---|
ElementType.ANNOTATION_TYPE | 可以给一个注解进行注解 |
ElementType.CONSTRUCTOR | 可以给构造方法进行注解 |
ElementType.FIELD | 可以给属性进行注解 |
ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 |
ElementType.METHOD | 可以给方法进行注解 |
ElementType.PACKAGE | 可以给一个包进行注解 |
ElementType.PARAMETER | 可以给一个方法内的参数进行注解 |
ElementType.TYPE | 可以给一个类型进行注解,比如类、接口、枚举 |
@Repeatable 是 JDK 1.8的特性。默认情况下注解只能唯一作用在对象上;@Repeatable允许让注解可以多次出现;
@Repeatable用于重复使用相同的注解描述,如人具备多样的化的角色或职业。
代理模式(简单核心示例):
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**代理工厂,用于为各个对象生成代理类 */
public class ProxyFactory implements MethodInterceptor{
/**创建代理目标的代理类 */
public <T> T getInstance(Class<T> targetClass){
//创建代理类的生成对象
Enhancer enhancer = new Enhancer();
//设置代理类的超类(代理目标),cglib通过继承实现代理模式
enhancer.setSuperclass(targetClass);
//设置代理类的回调,以便于通过intercept对代理方法进行拦截处理
enhancer.setCallback(this);
//创建代理类
T proxy = (T) enhancer.create();
return proxy;
}
/**
* 对代理目标执行的方法进行拦截
* @param obj 代理类
* @param method 拦截的代理目标的方法
* @param args 方法所需要的参数
* @param methodProxy 代理方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("拦截了"+method.getName()+"方法");
//判断当前拦截的方法是否作用了Required注解
if(method.isAnnotationPresent(Required.class)){
//判断参数值是否为null
for(Object arg : args){
if(arg == null || arg.toString().equals("")){
throw new RequiredException();
}
}
}
//使用代理类 执行代理目标的方法
Object returnValue = methodProxy.invokeSuper(obj, args);
return returnValue;
}
}