今日内容:
- 反射
- 注解
1.反射
动态语言:
-
动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结
构上的变化。比如常见的 JavaScript 就是动态语言,除此之外 Ruby,Python 等也属于动态语言,
而 C、C++则不属于动态语言。从反射角度说JAVA
属于半动态语言。 -
反射机制图解概念 (运行状态中知道类所有的属性和方法)
Java 中的反射机制:
在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;
并且对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象方
法的功能成为 Java 语言的反射机制。
- 编译时类型和运行时类型:
在 Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。
编译时的类型由
声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定 。
如:
Person p=new Student();
其中编译时类型为 Person,运行时类型为 Student。
- 反射适用场景
程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为 Object,但是程序有需要调用
该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。
然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象
和类的真实信息,此时就必须使用到反射了。
- Java 反射 API
- Class 类:反射的核心类,可以获取类的属性,方法等信息。
- Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性
值。 - Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或
者执行方法。 - Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
- 反射使用步骤
- 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方
法。 - 调用 Class 类中的方法,既就是反射的使用阶段。
- 使用反射 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()
- 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求
该 Class 对象对应的类有默认的空构造器。 - 先使用 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
您的点赞与关注是对作者最大的支持与鼓励。谢谢!!!
下一篇博客:利用反射+元注解=自定义实现注解