JAVA 反射
1.什么是反射
指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法。这种动态获取信息,以及动态调用对象方法的功能叫java语言的反射机制。
反射就是将java类中的各种成分映射成相应的java类,这样就可以对各个组成部分进行剖析,得到每个组成部分,并对每一部分进行操作。在比较复杂的程序或框架中来使用反射技术,可以简化代码提高程序的复用性。
例如,一个java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示。表示java类的Class类显然要提供一系列的方法来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,它们是Field,Method,Constructor,Package等等。
一个类中每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class实例对象的方法可以得到这些实例对象并加以应用。
2.反射的作用
Java反射API的第一个主要作用:
获取程序在运行时刻的内部结构。这对于程序的检查工具和调试器来说,是非常实用的功能。只需要短短的十几行代码,就可以遍历出来一个Java类的内部结构,包括其中的构造方法、声明的域和定义的方法等。这不得不说是一个很强大的能力。只要有了java.lang.Class类的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。这三个方法还有相应的getDeclaredXXX版本,区别在于getDeclaredXXX版本的方法只会获取该类自身所声明的元素,而不会考虑继承下来的。Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据。
反射API的另外一个作用:
在运行时刻对一个Java对象进行操作。这些操作包括动态创建一个Java类的对象,获取某个域的值以及调用某个方法。在Java源代码中编写的对类和对象的操作,都可以在运行时刻通过反射API来实现。
3.反射常用方法
// 获取类(class)
getClass()
getClassLoader()
// 获取构造器(constructor)
getConstructor()
getDeclaredConstructor()
// 获取方法(method)
getMethod()
getDeclaredMethod()
// 获取属性值(field)
getField()
getDeclaredField()
4.反射的使用
下面一个简单的Java类:
class MyClass {
public int count;
public MyClass(int start) { // 构造方法
count = start;
}
public void increase(int step) {
count = count + step;
}
}
一般做法:
MyClass myClass = new MyClass(0); //实例化
myClass.increase(2);
System.out.println("Normal -> " + myClass.count);
反射API做法:
try {
Constructor constructor = MyClass.class.getConstructor(int.class); //获取构造方法
MyClass myClassReflect = constructor.newInstance(10); //创建对象
Method method = MyClass.class.getMethod("increase", int.class); //获取方法
method.invoke(myClassReflect, 5); //调用方法
Field field = MyClass.class.getField("count"); //获取域
System.out.println("Reflect -> " + field.getInt(myClassReflect)); //获取域的值
} catch (Exception e) {
e.printStackTrace();
}
使用Java反射API的时候可以绕过Java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要在获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true即可。有了这种机制,就可以很方便的在运行时刻获取到程序的内部状态。
JAVA Annotation 注解
1.什么是注解(Annotation)
Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类
- Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法
- Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据
- Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中
2.基本Annotation
- @Override 限定重写父类方法,该注解只能用于方法
- @Inherited 指定注解是可继承的
- @Target 指定注解的运用目标,该注解用在什么上
ElementType.ANNOTATION_TYPE // 可以给一个注解进行注解 ElementType.CONSTRUCTOR // 可以给构造方法进行注解 ElementType.FIELD // 可以给属性进行注解 ElementType.LOCAL_VARIABLE // 可以给局部变量进行注解 ElementType.METHOD // 可以给方法进行注解 ElementType.PACKAGE // 可以给一个包进行注解 ElementType.PARAMETER // 可以给一个方法内的参数进行注解 ElementType.TYPE // 可以给一个类型进行注解,比如类、接口、枚举
- @Retention 注解的生命周期:应该可以用生命周期这个词来形容吧..
RetentionPolicy.SOURCE // 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 RetentionPolicy.CLASS // 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 RetentionPolicy.RUNTIME /* 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中, 所以在程序运行时可以获取到它们 */
- @Repeatable jdk1.8新特性,对于一个域可重复添加该注解
- @Documented 它的作用是能够将注解中的元素包含到 Javadoc 中去。
3.自定义Annotation
- 使用@interface定义Annotation
- 使用Annotation修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前
- 定义带成员变量的Annotation
- 为Annotation的成员变量指定初始值
4.提取Annotation信息
- Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的父接口
- AnnotatedElement接口代表程序中可以接受注释的程序元素
- 调用AnnotatedElement对象的如下三个方法来访问Annotation信息:
- getAnnotation(Class<T> annotationClass):返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null
- Annotation[] getAnnotations():返回该程序元素上存在的所有注释
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false
5.Annotation 注解实例1
自定义Annotation:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义一个注解
*/
@Target(ElementType.METHOD) // 这是一个对方法的注解,还可以是包、类、变量等很多东西
@Retention(RetentionPolicy.RUNTIME) // 保留时间,一般注解就是为了框架开发时代替配置文件使用,
JVM运行时用反射取参数处理,所以一般都为RUNTIME类型。
@Documented // 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如
javadoc此类的工具文档化。
public @interface OneAnnotation {
// 定义注解的参数,类型可以为基本类型以及String、Class、enum、数组等,default为默认值
String parameter1() default "";
int parameter2() default -1;
}
使用注解:
/**
* 一个用到了自定义的注解的类
*/
public class OClass {
@OneAnnotation(parameter1="YES", parameter2=10)
public void oneMethod () {
}
}
获取注解参数:
import java.lang.reflect.Method;
public class TestDemo {
public static void main(String[] args) throws Exception {
// 提取到被注解的方法Method,这里用到了反射的知识
Method method = Class.forName("OneClass").getDeclaredMethod("oneMethod");
// 从Method方法中通过方法getAnnotation获得我们设置的注解
OneAnnotation oneAnnotation = method.getAnnotation(OneAnnotation.class);
// 得到注解的俩参数
System.out.println(oneAnnotation.parameter1());
System.out.println(oneAnnotation.parameter2());
}
}
6.Annotation 注解实例2
Demo功能:使用一个工厂类来根据传递的类名来创建实例,并用注解标明需要创建实例的个数。
6.1.创建一个注解类,用于表明类需要被实例的个数
package hbi.banktest.dto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Reflection{
int num() default 1;
}
6.2.创建测试bean并加入注解
package hbi.banktest.dto;
@Reflection(num = 5)
public class TestBean {
private long id;
private String name;
private static final long DEFAULT_ID = 1;
private static final String DEFAULT_NAME = "xixi";
public static long i = 0;
public TestBean(){
id = DEFAULT_ID + i++;
name = DEFAULT_NAME + id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return id+","+name;
}
}
6.3.创建工厂类并测试
package hbi.banktest.dto;
import java.util.ArrayList;
import java.util.List;
public class ReflectionFactory<T> {
public List<T> getBean(Class<T> clazz){
System.out.println("aaa");
List<T> list = new ArrayList<>();
Reflection reflection = clazz.getAnnotation(Reflection.class);
//Assert.notNull(reflection,"无Reflection注解");
if(reflection == null){return null;}
int length = reflection.num();
try {
for (int i =0;i < length;i++){
T t = clazz.newInstance();
list.add(t);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return list;
}
public static void main(String[] args) {
ReflectionFactory reflectionFactory = new ReflectionFactory();
List list = reflectionFactory.getBean(TestBean.class);
System.out.println(list.size());
for (int i = 0;i < list.size();i++){
System.out.println(list.get(i).toString());
}
}
}
/*
aaa
5
1,xixi1
1,xixi1
1,xixi1
1,xixi1
1,xixi1
*/