最近本着不求甚解的态度重温了一下主流框架的源码,所以趁热打铁的总结一下,学习框架或开发框架所必备的基础知识:
一、反射
1.1、简介
本文简单总结一下Java反射和注解,反射可以算是必备条件了,基本学习的主流框架中都会看到反射的身影,他的灵活使用也为很多逻辑的扩展提供了可能,通过Java的反射机制,可以在程序中访问已经加载到JVM中的Java对象的描述,实现访问、检测、修复和修改描述Java本身对象的功能,Java中的java.lang.reflect包提供使用注解功能。
Java中的所有类均即成了Object类,在Object类中定义了getClass方法,该方法返回一个Class的对象:
Class c = object.getClass();
利用返回的Class,即可访问Class描述信息,如:属性、方法‘构造函数等;
1.2、访问构造函数
- Java提供了一下通过反射获取构造函数的方法:
- getConstructors():获取权限位Public的构造函数
- getConstructors(Class<?> … parameterTypes):获取权限为public,指定参数类型的构造函数
- getDeclaredConstructors():按照声明顺序返回所有构造函数
- getConstructors(Class<?> … parameterTypes):获取指定参数类型的构造函数
- 针对Constructor常用的方法:
- isVarArgs():是否允许可变数量参数,返回boolean ,true表示有,false表示没有
- getParameterTypes():按照声明的顺序返回参数类型
- getExceptionTypes():返回抛出异常的类型
- newInstance():根据传入的参数调用构造函数创建实例
- setAccessible():默认不允许反射privite修饰的方法和属性,但如果先执行并设置为true,则可以反射
- getModifiers():获得可以解析出方法修饰符的整数
-
Modifier常用的解析方法
- isPublic(int mode):是否为Public修饰符
- isProtected(int mode):是否为Protected修饰符
- isPrivate(int mode):是否为Private修饰符
- isStatic(int mode):是否为Static修饰符
- isFinal(int mode):是否为Final修饰符
- toString(int mode):以字符串返回修饰符
- 实例:
- 创建实例ConstructExample
public class ConstructorExample {
int number;
public String name;
protected float aFloat;
private ConstructorExample() {
}
protected ConstructorExample(int number) {
this.number = number;
}
public ConstructorExample(String name, int number) throws NumberFormatException {
this.number = number;
this.name = name;
}
public void prinln() {
System.out.println("name = " + name);
System.out.println("number = " + number);
}
}
2、反射获取构造函数信息
public static void main(String[] args) {
ConstructorExample constructorExample = new ConstructorExample(10);
Class<? extends ConstructorExample> classExample = constructorExample.getClass();
Constructor[] constructors = classExample.getDeclaredConstructors(); // 获取所有构造函数
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i]; //获取构造函数
System.out.println("是否有可变参数");
System.out.println(constructor.isVarArgs());
System.out.println("构造函数的参数类型");
Class[] classes = constructor.getParameterTypes(); // 获取参数类型
System.out.println("构造函数的修饰类型");
int modifier = constructor.getModifiers();
if (modifier == Modifier.PRIVATE){ // 判断修饰符类型
System.out.println("Private");
}else if (modifier == Modifier.PROTECTED){
System.out.println("Protected");
}else if (modifier == Modifier.PUBLIC){
System.out.println("Public");
}
for (Class c : classes) {
System.out.println(c);
}
System.out.println("可能抛出的异常类型");
Class[] classException = constructor.getExceptionTypes(); // 获取抛出异常类型
for (Class c : classException) {
System.out.println(c);
}
ConstructorExample example = null;
while (example == null) {
try {
if (i == 2) {
example = (ConstructorExample) constructor.newInstance();// 创建实例
} else if (i == 1) {
example = (ConstructorExample) constructor.newInstance(20);
} else {
example = (ConstructorExample) constructor.newInstance("ABC", 50);
}
} catch (Exception e) {
System.out.println("私有构造函数抛出异常,设置Accessible为true");
constructor.setAccessible(true); // 处理私有构造函数
}
}
if (example != null) {
example.prinln(); // 调用方法
System.out.println();
}
}
3、运行代码输出信息(依次为三个构造函数信息)
1.3、访问成员变量
- Java提供反射获取变量方法
- getFields():获取所有权限为Public的属性
- getFields(String name):获取名称为name,修饰符Public的属性
- getDeclaredFields():按声明顺序返回所有属性
- getDeclaredFields(String name):获取指定成员变量
- Fields的常用方法
- getName():获取成员变量的名称
- getType():获取成员变量的Class对象
- get(Object o):获取指定对象Object的成员变量
- set(Object,Value):指定Object对象的值为value
- getInt()/setInt():获取和设置Object中类型为Int的属性值
- getFloat()/setFloat():获取和设置Object中类型为Float的属性值
- getBoolean()/setBoolean():获取和设置Object中类型为Boolean的属性值
- setAccessible():默认不允许反射privite修饰的方法和属性,但如果先执行并设置为true,则可以反射
- getModifiers():获得可以解析出方法修饰符的整数
- 实例
- 在上面的类中添加成员变量(Private 处理和构造函数一致,这里不做处理)
int number;
public String name;
protected float aFloat;
2、反射获取成员变量信息
Field[] fields = classExample.getDeclaredFields();
for (Field f: fields) {
System.out.println("属性名称 = "+f.getName());
System.out.println("属性类型 = "+f.getType());
try {
System.out.println("属性默认值 = "+f.get(constructorExample));
if (f.getType().equals(int.class)){
f.set(constructorExample,5);
System.out.println("属性值修改 = "+ f.get(constructorExample));
}else if (f.getType().equals(String.class)) {
f.set(constructorExample,"ABC");
System.out.println("属性值修改 = "+f.get(constructorExample));
}else {
f.set(constructorExample,99.99f);
System.out.println("属性值修改 = "+f.get(constructorExample));
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("-------------");
}
3、实例输出信息
1.4 、访问方法
- Java提供反射获取方法属性的方法:
- getMethod():获取所有为Public的方法
- getMethod(String name,Class<?> … parameterTypes):获取指定方法名和参数类型的public函数
- getDeclaredMethod():获取所有方法
- getDeclaredMethod(String name,Class<?> … parameterTypes):获取指定方法名和参数类型的函数
- Method的常用方法
- getName():获取方法名
- getParameterType():按声明顺序返回参数类型
- getReturnType():获取方法的返回数据类型
- getExceptionTypes():返回抛出异常的类型
- invoke(Object o,Object…argbs):利用参数argbs执行对象Object中的该方法
- isVarArgs():是否允许可变数量参数,返回boolean ,true表示有,false表示没有
- getModifiers():获得可以解析出方法修饰符的整数
- 实例
- 在上面类中添加几个简单方法
public void prinln() {
System.out.println("name = " + name);
System.out.println("number = " + number);
}
public int action(int i,int j){
System.out.println("执行了action");
System.out.println(i+j);
return i+j;
}
protected void function() throws NullPointerException{
System.out.println("执行了function");
}
2、反射获取并调用方法
Method[] methods = classExample.getDeclaredMethods();
System.out.println("-------------");
for (Method method : methods){
System.out.println("方法名:"+method.getName());
System.out.println("返回值类型:"+method.getReturnType());
System.out.println("方法抛出的异常类型为");
Class[] classException = method.getExceptionTypes();
for (Class c : classException) {
System.out.println(c);
}
Class[] classes = method.getParameterTypes();
System.out.println("方法的参数类型为");
for (Class c:classes
) {
System.out.println(c);
}
try {
if (classes.length != 0) {
method.invoke(constructorExample, 3, 7); // 执行方法
}else {
method.invoke(constructorExample); // 执行方法
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("-------------");
}
3、输出信息
以上就是反射获取类的构造方法、属性和方法的过程,有了这三步就可以访问类中的所有信息,极大地提高了代码的灵活性;下面来介绍下注解,基本反射和注解是一个很好的搭档,加入我们要为某种行为创造框架,那么就可以在需要使用的地方,添加注解标记,然后用反射找到指定的类或方法属性,执行相应的过程,仔细想想框架是不是就是这样出来的呢?
二、注解
2.1、注解形式
public @interface Action { // 无属性值
}
public @interface Action { // 有属性值
String action() default "A";
int value() default 0;
}
2.2、注解的标记
- @target:设置使用的元素种类,如果未设置就表示用于所有元素,使用ElementType设置
- ElementType 枚举常量有:
- ANNOTATION_TYPE:表示用于Annotation的类型
- TYPE:作用与类、接口、枚举
- CONSTRUCTOR:作用于构造方法
- FIELD:作用于属性
- METHOD:作用于方法
- PARAMETER:作用于参数
- LOCAL_VARIABLE:表示局部变量
- PACKAGE:表示用于包
- @Retention:设置注解的有效范围
- SOURCE:不编译到Annotation类的文件中
- CLASS:编译到Annotation的文件中,运行时不加在到JVM中
- RUNTING:运行时加载到JVM中,有效范围最大
- CONSTRUCTOR、METHOD、FIELD都继承了AccessibleObject类,在AccessibleObject中定义了三个方法
- isAnnomationPresent(Class<? extends Annotation> class):返回boolean,表示是否有指定的注解
- getAnnotation(Class class):获取指定的注解
- getAnnotations():获取所有的注解
2.3、实例
- 声明两个注解
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String action() default "构造函数";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
String describe() default "描述";
Class type() default void.class;
}
注解@Action 使用于构造函数,有一个描述属性,@FieldAnnotation适用于注解属性常量,包含一个String类型的描述和修饰的类型
- 使用注解
public class ConstructorExample {
@FieldAnnotation(describe = "名字" ,type = String.class)
public String name;
@FieldAnnotation(describe = "分数",type = int.class)
protected int core;
@Action()
public ConstructorExample(String name, int core) throws NumberFormatException {
this.core = core;
this.name = name;
}
}
- 反射获取
public class ReflectConstructor {
public static void main(String[] args) {
ConstructorExample constructorExample = new ConstructorExample("ABC",10);
Class<? extends ConstructorExample> classExample = constructorExample.getClass();
Constructor[] constructors = classExample.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
if (constructor.isAnnotationPresent(Action.class)){//是否包含Action.class注解
System.out.println("包含Action注解");
Action action = (Action) constructor.getAnnotation(Action.class);
System.out.println("注解值为 :"+action.action());
}
System.out.println("-------------");
}
Field[] fields = classExample.getDeclaredFields();
for (Field f: fields) {
System.out.println("属性名称 = "+f.getName());
System.out.println("属性类型 = "+f.getType());
try {
if (f.isAnnotationPresent(FieldAnnotation.class)) {
System.out.println("使用FieldAnnotation注解");
FieldAnnotation annotation = f.getAnnotation(FieldAnnotation.class);
System.out.println("FieldAnnotation注解参数:");
System.out.println("Describe:"+annotation.describe());
System.out.println("Type:"+annotation.type());
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("-------------");
}
}
}
- 输出信息
反射和注解的介绍,以及二者的使用介绍完毕,在平时的使用过程中也基本就是这样使用的,只不过功能逻辑的实现复杂程度不同,后面会有一篇关于注解自动生成代码的文章,也是框架中注解的常用实例。