Java知识点详解——第十三章:反射与注解

反射

        反射是Java中框架设计的核心,通过对类的构造、属性、方法等数据的获取提供抽象的底层构建。
        反射需要先获得类的class字节码,由JVM类加载器(ClassLoader)负责加载,并在内存中缓存class的内部结构。借助于Java的反射机制允许快速对类的结构数据进行访问和调用。
        在类运行时,能够动态获取并访问其构造结构的机制称为反射机制。

获得class字节码文件的三种方式:

  1. Object对象的getClass()方法
Class classObj1 = new User().getClass();
  1. 类的class静态属性
Class classObj2 = User.class;
  1. Class.forName(String classPath)的加载    该方法是反射应用的核心
Class classObj3 = Class.forName("com.chiang.pojo.User");

注:

  • 反射允许获取到类的各种构造结构,包括构造方法、方法、属性、接口、注解等;
  • 反射能够对类完成实例构建,并能对方法进行实例调用;
  • 反射需要通过类的Class对象获取。

使用反射获得类的实例对象:

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 static final修饰;
  • 注解中的属性通过抽象方法的形式表现(返回值表示属性的类型,因此不能为void, 且必须是无参方法);
  • 注解可以作用在类、方法、属性、参数中,用于对这些数据进行标识;
  • 如果注解中只有一个名称为value的属性,则在调用时可以缺省属性名。

定义注解: 注解中只能定义属性,不包含方法

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 保留期的意思。
        当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:

  1. RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  2. RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  3. RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
    SOURCE和CLASS将无法在反射中识别注解。
(Ⅱ)  @Documented

标识注解是否写入doc文档

(Ⅲ)  @Inherited

允许父类作用注解后子类被继承

(Ⅳ)  @Target

指定注解作用的场景对象

注解 作用的场景对象
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
(Ⅴ)  @Repeatable

@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;
	}
}

你可能感兴趣的:(Java)