粤嵌打卡第13天(反射+注解)

今日内容:

  • 反射
  • 注解

1.反射

动态语言:

  • 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结
    构上的变化。比如常见的 JavaScript 就是动态语言,除此之外 Ruby,Python 等也属于动态语言,
    而 C、C++则不属于动态语言。从反射角度说 JAVA属于半动态语言。

  • 反射机制图解概念 (运行状态中知道类所有的属性和方法)


Java 中的反射机制:

在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;
并且对于任意一个对象,都能够调用它的任意一个方法
,这种动态获取信息以及动态调用对象方
法的功能成为 Java 语言的反射机制。

  • 编译时类型和运行时类型:

在 Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。

编译时的类型由
声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定 。

如:
Person p=new Student();

其中编译时类型为 Person,运行时类型为 Student。

  • 反射适用场景

程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为 Object,但是程序有需要调用
该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。
然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象
和类的真实信息,此时就必须使用到反射了。

  • Java 反射 API
  1. Class 类:反射的核心类,可以获取类的属性,方法等信息。
  2. Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性
    值。
  3. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或
    者执行方法。
  4. Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
  • 反射使用步骤
  1. 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方
    法。
  2. 调用 Class 类中的方法,既就是反射的使用阶段。
  3. 使用反射 API 来操作这些信息。
  • 获取 Class 对象的 3 种方法(特别重要)

1、调用某个对象的 getClass()方法

Person p=new Person();
Class clazz=p.getClass();

2、调用某个类的 class 属性来获取该类对应的 Class 对象

Class clazz=Person.class;

3、使用 Class 类中的 forName()静态方法(最安全/性能最好)

Class clazz=Class.forName("类的全路径"); (最常用)

当我们获得了想要操作的类的 Class 对象后,可以通过 Class 类中的方法获取并查看该类中的方法
和属性。

//获取 Person 类的 Class 对象
Class clazz=Class.forName("reflection.Person");
//获取 Person 类的所有方法信息
Method[] method=clazz.getDeclaredMethods();
for(Method m:method){
System.out.println(m.toString());
}
//获取 Person 类的所有成员属性信息
Field[] field=clazz.getDeclaredFields();
for(Field f:field){
System.out.println(f.toString());
}
//获取 Person 类的所有构造方法信息
Constructor[] constructor=clazz.getDeclaredConstructors();
for(Constructor c:constructor){
System.out.println(c.toString());
}
  • 创建对象的两种方法

Class 对象的 newInstance()

  1. 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求
    该 Class 对象对应的类有默认的空构造器。
  2. 先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()
    方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。
//获取 Person 类的 Class 对象
Class clazz=Class.forName("reflection.Person"); 
//使用.newInstane 方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1=(Person) c.newInstance("李四","男",20);
  • 反射中属性的解析


示例代码:

                //动态获取属性
		Account acc = new Account("小明",20,5000.0,new Date());
		//反射 通过对象获取类的getClass对象
		Class accCls = acc.getClass();
		//通过对象所在的Class对象获取Account类中定义的信息
		//获取属性的定义,将属性定义在Field类中
		//getFields() 获取类中及父类公有的属性
		//getDeclaredFields() 获取自己定义的所有类型的属性,不能访问父类的属性
		//getModifiers() 获取属性或方法的修饰符
		Field[] fields = accCls.getFields();
//		Field[] fields = accCls.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getName()+"|"+field.getType()+"|"+getModifiers(field.getModifiers()));
		}
/**
	 * 测试属性的修饰符
	 */
	public static String getModifiers(int modifiers) {
		StringBuffer sb = new StringBuffer();
		//判断属性的类型
		if(Modifier.isPublic(modifiers)) {
			sb.append("public ");
		}else if(Modifier.isPrivate(modifiers)) {
			sb.append("private ");
		}else if(Modifier.isProtected(modifiers)) {
			sb.append("protected ");
		}else{
			sb.append(" ");
		}
		
		//判断是否静态
		if(Modifier.isStatic(modifiers)) {
			sb.append("static ");
		}
		//判断是否final
		if(Modifier.isFinal(modifiers)) {
			sb.append("final ");
		}
		return sb.toString();
	}
  • 反射中方法的解析


示例代码:

//动态获取构造方法
		Constructor[] cs = accCls.getConstructors();
		for (Constructor<?> c : cs) {
			StringBuffer sb = new StringBuffer();
			//获取方法参数信息
			Parameter[] parameters = c.getParameters();
			for (Parameter p : parameters) {
				sb.append(p.getType().getSimpleName()+"---"+p.getName());
			}
			System.out.println(c.getName()+"|"+getModifiers(c.getModifiers())+"|"+sb);
		}
  • 反射中构造方法的获取

//动态获取构造方法
		Constructor[] cs = accCls.getConstructors();
		for (Constructor<?> c : cs) {
			StringBuffer sb = new StringBuffer();
			//获取方法参数信息
			Parameter[] parameters = c.getParameters();
			for (Parameter p : parameters) {
				sb.append(p.getType().getSimpleName()+"---"+p.getName());
			}
			System.out.println(c.getName()+"|"+getModifiers(c.getModifiers())+"|"+sb);
		}
		//通过Class对象,调用类的无参构造器来生成一个新对象,如果没有无参构造器,异常
		try {
			Object newObj = accCls.newInstance();
			Account newAcc = (Account)newObj;
			//获取setName方法,并封装为Mrthod对象
			Method setNameMethod = accCls.getMethod("setName",String.class);
			//反射的使用调用newAcc的setName方法,invoke(对象名,值)方法
			setNameMethod.invoke(newAcc, "小张");
//			newAcc.setName("张三");
			System.out.println(newAcc);
  • 使用注解代替传统的拷贝方法(将任意一个对象的方法设置到另一个对象中)
package com.yueqian.fanshe;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 拷贝Account1
 * @author LinChi
 *
 */
public class TestAccount2 {
	public static void main(String[] args) {
		Account acc = new Account("小明",20,5000.0,new Date());
		Account2 acc2 = new Account2();
		copyField(acc,acc2);
		System.out.println(acc2);
	}
	/**
	 * 传统方法拷贝同名的属性
	 * @param acc
	 * @param acc2
	 */
//	public static void copyField(Account acc,Account2 acc2) {
//		acc2.setName(acc.getName());
//		acc2.setAge(acc.getAge());
//		acc2.setMoney(acc.getMoney());
//	}
	/**
	 * 反射方法拷贝同名的属性
	 * @param src
	 * @param target
	 */
	public static void copyField(Object src,Object target) {
		//获取目标对象的所有方法名
		for(Method method:target.getClass().getMethods()) {
			//获取方法名
			String methodName = method.getName();
			//判断目标对象的方法是否为set方法
			if(!methodName.startsWith("set")) {
				continue;
			}
			//获取方法名
			String getMethodName = "get"+methodName.substring(3);
			try {
				//从src中获取对应的get方法
				Method getMethod = src.getClass().getMethod(getMethodName);
				//调用src的get方法给target的set方法
				method.invoke(target, getMethod.invoke(src));
			} catch (NoSuchMethodException e) {
				return;
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

2、注解

  • 注解是什么?

Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径
和方法。Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的 Annotation
对象,然后通过该 Annotation 对象来获取注解中的元数据信息。

  • 4种标准元注解

元注解的作用是负责注解其他注解。 Java5.0 定义了 4 个标准的 meta-annotation 类型,它们被
用来提供对其它 annotation 类型作说明。

@Target 修饰的对象范围

@Target说明了Annotation所修饰的对象范围: Annotation可被用于 packages、types(类、
接口、枚举、Annotation 类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数
和本地变量(如循环变量、catch 参数)。在 Annotation 类型的声明中使用了 target 可更加明晰
其修饰的目标

@Retention 定义 被保留的时间长短

Retention 定义了该 Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描
述注解的生命周期(即:被描述的注解在什么范围内有效),取值(RetentionPoicy)由:
„ SOURCE:在源文件中有效(即源文件保留)
„ CLASS:在 class 文件中有效(即 class 保留)
„ RUNTIME:在运行时有效(即运行时保留)

@Documented 描述-javadoc

@ Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共 API,因
此可以被例如 javadoc 此类的工具文档化。

@Inherited 阐述了某个被标注的类型是被继承的

@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一
个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该
class 的子类。

图解注解

  • 注解处理器

如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,
很重要的一部分就是创建于使用注解处理器。Java SE5 扩展了反射机制的 API,以帮助程序员快速
的构造自定义注解处理器。


写在最后

推荐自己的Github地址: https://github.com/Lmobject


您的点赞与关注是对作者最大的支持与鼓励。谢谢!!!

下一篇博客:利用反射+元注解=自定义实现注解

发布了17 篇原创文章 · 获赞 3 · 访问量 501

猜你喜欢

转载自blog.csdn.net/qq_41986840/article/details/104581846