反射与注解

用来进行框架底层设计,开发中很少直接使用
框架=反射+注解+设计模式

一.反射

用来在程序的运行期间可以提供的一些API来获取某个类的内部结构信息,并基于或得到的信息完成对象的创建和访问操作。
反射相关的类都是定义在java.lang.reflect包中。

  • 反射的使用: 1.获取Class类型的对象有以下三种情况:     1.1 通过类名:Class clazz = Person.class;     1.2 通过对象:Class clazz = person.getClass();     1.3 通过全类名:Class clazz =Class.forName(“全限定类名”); 2.利用反射来创建对象:     2.1 调用该类的无参构造方法:Object obj = clazz.newInstance();//     2.2 通过构造方法来调用:
Constructor<?> constructor = clazz.getConstructor();//拿到无参构造
 Object obj = constructor.newInstance();
 Constructor<?> constructor1 = clazz.getConstructor(int.class);//拿到有参构造
 Object obj = constructor1.newInstance(4);

  • 3.在程序运行期间,可以通过反射创建一个对象,我们可以获取到该对象的内部所有的信息,如:成员变量(private、protected)、成员方法、构造器、父类类型、注解等等。
    4.哪些类型可以有Class对象
         4.1 class:外部类,成员类(成员内部类,静态内部类),局部内部类,匿名内部类
        4.2 interface:接口
        4.3 []:数组,也包括二维数组
        4.4 enum:枚举
        4.5 annotation:注解@interface
        4.6 primitive type:基本数据类型
  • 反射的经典例子:
    第一个:通过反射来实现对象数据的拷贝
/**
 * 经典例子:通过反射来实现对象数据的拷贝
 */
public class ObjectCopy {
	/**
	 * 拷贝方法:通过反射来实现对象数据的拷贝
	 * @param obj:表示传进来的对象,以便反射创建该对象
	 */
	public static Object copy(Object obj) throws Exception {
		Class clazz = obj.getClass();//反射的入口,获取一个Classde的对象
		Object newInstance = clazz.newInstance();//实例化一个对象
		//我要做的是通过person参数传入。调用老对象的getXXX的方法拿到值
		//然后调用新对象的setXXX的方法设置值。
		Field[] fields = clazz.getDeclaredFields();//获取到该类的所有成员变量
		for(Field f:fields) {
			String fieldName = f.getName();//获取属性名:age,name
			//age->getAge,setAge  name->getName,setName
			String setMethodName = "set"+  String.valueOf(fieldName.charAt(0)).toUpperCase()+fieldName.substring(1);
			String getMethodName = "get"+  String.valueOf(fieldName.charAt(0)).toUpperCase()+fieldName.substring(1);
			Method getMethod = clazz.getMethod(getMethodName);
			Method setMethod = clazz.getMethod(setMethodName,f.getType());//这里注意调用字段的类型是.getType()方法
			//调用老对象的方法。这里注意一切实例方法都必须由对象区调用,这是java的规则,不可违背
			//所以我们在这里需要传入一个对象obj
			Object value = getMethod.invoke(obj);
			setMethod.invoke(newInstance, value);
		}
		return newInstance;
	}
	public static void main(String[] args) throws Exception {
		Person person = new Person(18,"张三");
		Object copyObj = copy(person);
		System.out.println(copyObj);
	}
}


//person类
public class Person {
	private int age;
	private String name;
	public Person(int age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	public Person() {
		super();
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + "]";
	}
}

第二个反射加注解的经典例子(例子很长,请耐心推看):

public class ReflectTest {
	public static void main(String[] args) {
		//Calculate这里,我们可以灵活更换其他类
		ClassParse classParse = new ClassParse(Calculate.class);	
		Scanner sr = new Scanner(System.in);
		while(true) {
			String operate = sr.next();
			classParse.judge(operate);
		}
	}
}

//Calculate类:根据客户输入的内容调用相应的方法
class Calculate{
	//这里的注解的内容相当于客户输入一个别名的操作方法,add表示真是操作名
	@Process("添加")
	public void add() {
		System.out.println("add方法调用...");
	}
	
	//这里没有注解表示没有别名
	public void edit() {
		System.out.println("edit方法调用...");
	}
	
	@Process("删除")
	public void delete() {
		System.out.println("delete方法调用...");
	}
	
	@Process("查看")
	public void look() {
		System.out.println("look方法调用...");
	}
}

/**
 * 
 * 这里我们需要一个解析类:表示根据传过来的某一个Calculate类,先解析Calculate的内部信息
 * 然后根据客户输入来判断我们需要执行哪一个操作。这里采用反射可以使我们的代码更加灵活
 */
class ClassParse {
	//声明一个Class类对象,方便后面构造方法接受一个Class类对象
	private Class clazz;
	
	//方法调用的需要的对象,方便后期反射来调用对象的方法时需要一个对象。
	private Object obj;
	
	//aliasToRealName 表示别名映射了一个真实操作名(edit,add...)
	private Map<String, String> aliasToRealName = new HashMap<>();
	//realNameToMethod 表示真实操作名对应了一个Method类型的方法
	private Map<String, Method> realNameToMethod = new HashMap<>();
	
	//在类被调用时我们通过构造器要初始化信息,也就是说我们要提前初始化传过来的Class对象对应的类
	public ClassParse(Class clazz) {
		this.clazz=clazz;
		this.init();
	}
	
	//通过clazz创建对象并初始化aliasToRealName,realNameToMethod这两个集合内容
	private void init() {
		try {
			Object newInstance = this.clazz.newInstance();
			this.obj=newInstance;
			
			Method[] mt = this.clazz.getDeclaredMethods();
			for(Method m : mt) {
				String methodeRealName = m.getName();//add、edit、delete...
				//获取到方法上方的注解。后面可以采用process.value() 获取到注解的内容
				Process process = m.getAnnotation(Process.class);
				this.realNameToMethod.put(methodeRealName, m);
				if(process!=null) {
					this.aliasToRealName.put(process.value(), methodeRealName);
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//该方法用来接受用户传过来的值,通过用户传过来的值去调用相应的方法
	/**
	 * 执行的流程:拿到用户输入的字符串,第一步应该先从别名对应真实操作名找,如果有拿到真实操作名然后去拿到真实操作名对应的Method方法然后执行
	 * 如果没有获取到别名那么用户可能输入的就是真实操作名,这个时候就去对应的map集合中找
	 * 如果前两者都不是,说明没有这个操作,用户可能输入错了,这样给用户throw 一个异常信息。

	 */
	public void judge(String operate) {
		try {
			String realName = this.aliasToRealName.get(operate);
			if(realName!=null) {
				Method method = this.realNameToMethod.get(realName);
				method.invoke(this.obj);
			}else {
				Method method = this.realNameToMethod.get(operate);
				if(method!=null) {
					method.invoke(this.obj);
				}else {
					throw new IllegalArgumentException(String.format("%s 方法不存在", operate));
				}
			}
			
			
		}  catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public Class getClazz() {
		return clazz;
	}
	public void setClazz(Class clazz) {
		this.clazz = clazz;
	}
	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
	public Map<String, String> getAliasToRealName() {
		return aliasToRealName;
	}
	public void setAliasToRealName(Map<String, String> aliasToRealName) {
		this.aliasToRealName = aliasToRealName;
	}
	public Map<String, Method> getRealNameToMethod() {
		return realNameToMethod;
	}
	public void setRealNameToMethod(Map<String, Method> realNameToMethod) {
		this.realNameToMethod = realNameToMethod;
	}
}

二、注解

注解是JDK5引进的,我们需要掌握的有内置注解、元注解和自定义注解。

  1. 内置注解:
    @Override:子类重写父类的注解,规范我们的代码格式
    @Deprecated:不推荐使用,但是可以使用。一般用来表示该方式过时或者有更好的方式。
    @SuppressWarnings(“all”):抑制警告信息
  2. 元注解:
1.  @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER}):表示这个注解可以定义在方法、类、字段上或者参数的前面(@Param)
 @Retention(value={RetentionPolicy.RUNTIME,RetentionPolicy.SOURCE,RetentionPolicy.CLASS})
 RetentionPolicy.SOURCE:表示该注解是源码级别,源码期间有效,编译后就无效了
 RetentionPolicy.CLASS:ClASS表示class文件,java编译后的文件,这里表示编译时的级别,运行时就无效了
 RetentionPolicy.RUNTIME:表示运行时的级别,在运行时还可以读取到里面的值
 @Documented:表示是否将我们的注解生成在JAVA文档中
 @Inherited:表示子类可以继承父类的注解
  1. 自定义注解:
    关键字:@interface
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	//注解可以显示赋值,也可以设置默认值
    //注意如果参数名为value,则在方法或者类上赋值的时候可以省略value,但是前提是该注解只有一个参数时
	String name();
	int age() default 0;
	//String[] hobby(); 
}

//注解的使用
@MyAnnotation(name="zs")
public class test{
	@MyAnnotation(name="zs")
	private int a;
	@MyAnnotation(name="zs")
	public void a(){
		
	}
}