反射和注解

反射

反射机制概述

反射被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API 取得任何类的内部信息,并能直接操作任何对象的内部属性和方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个对象),这个对象就包含了完整类的结构信息。

Java反射机制研究及应用:

在运行是判断任何一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时调用任意一个对象的成员变量和方法

在运行时处理注解()

生成动态代理

反射相关的主要API

1
2
3
4
java.lang.Class:代表一个方法
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器

如何理解Class类

在Object类中定义了以下的方法,此方法将被所有子类继承:

1
public final Class getClass()

以上的方法返回值的类型是一个Class类型,此类是Java反射的源头。

对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。

一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

Class本身也是一个类

Class 对象只能由系统建立对象

一个加载的类在 JVM 中只会有一个Class实例

一个Class对象对应的是一个加载到JVM中的一个.class文件

每个类的实例都会记得自己是由哪个 Class 实例所生成

通过Class可以完整地得到一个类中的所有被加载的结构

Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

获取Class类的实例

那些类型可以有Class对象?

class、interface、数组、enum、annotation、primitive type(基本数据类型)、void

获取实例的四种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//先定义一个类
public class Person {
public String name;
public String sex;

public Person() {
}

public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}

}

方法一:

已知具体的类,通过类的class属性获取,该方法最安全可靠,程序性能最高。

1
2
3
//方法一:
Class class1 = Person.class;
System.out.println(class1);

方法二:

已知某个类的实例。

1
2
3
4
//方法二:
Person person = new Person();
Class class2 = person.getClass();
System.out.println(class2);

方法三:

已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出异常ClassNotFoundException(该类为找到)。

方法三更能体现动态性,因为在运行才会判断类是否存在等。

1
2
3
//方法三:
Class class3 = Class.forName("com.jiang.Person");
System.out.println(class3);

方法四:

通过getClassLoader()得到:

1
2
3
4
//方法四:
ClassLoader classLoader = this.getClass().getClassLoader();
Class class4 = classLoader.loadClass("com.jiang.Person");
System.out.println(class4);

创建运行时类的对象

创建完Class对象后,我们可以创建类的对象。

我们调用Class对象的newInstance(),newInstance()方法在jdk1.9之后过时了。

1、 类必须有一个无参数的构造器

2、 类的构造器的访问权限需要足够

1
2
3
4
Class class3 = Class.forName("com.jiang.Person");
//得到运行时类对象
Person person = (Person) class3.newInstance();
System.out.println(person);

我们也可以使用带参数的构造器:

1
2
3
4
5
//调用指定参数结构的构造器,生成Constructor的实例
Constructor con = class1.getConstructor(String.class, String.class);
//通过生成Constructor的实例创建对应类的对象,并初始化类属性
Person person2 = (Person)con.newInstance("jiang", "男");
System.out.println(person2);

Dame:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static Object getInstance(String classPath) throws Exception {
Class class1 = Class.forName(classPath);
return class1.newInstance();
}

public static void main(String[] args) throws Exception {

int num = new Random().nextInt(2);
String classPath = null;
switch (num)
{
case 0:
classPath = "com.jzw.Person";
break;
case 1:
classPath = "java.util.Date";
}

Object instance = getInstance(classPath);
System.out.println(instance);

}

获取运行时类的完整结构

先定义一个复杂的类。

创建一个注解:
只能RetentionPolicy.RUNTIME,才能通过反射得到注解。

1
2
3
4
5
6
7
@Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.LOCAL_VARIABLE,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
public int id();
public String decsription() default "no decsription";
}

创建一个父类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@AnnotationDemo(id = 1)
public class Creature<T> implements Serializable {
private char gender;
public double weight;

private void breath()
{
System.out.println("生物呼吸");
}

public void eat()
{
System.out.println("生物吃东西");
}


}

创建一个接口:

1
2
3
4
public interface MyInterface {
void info();
}

创建一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@AnnotationDemo(id = 0)
public class Person extends Creature<String> implements Comparable<String>,MyInterface {

private String name;
int age;
public int id;

public Person() {
}

@AnnotationDemo(id = 2)
public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}

@AnnotationDemo(id = 2)
private String getNation(String nation)
{
System.out.println("我的国籍是:" + nation);
return nation;
}

@Override
public void eat() {
super.eat();
}

@Override
public void info() {
System.out.println("我是一个人");
}

@AnnotationDemo(id = 0)
@Override
public int compareTo(String o) {
return 0;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
", weight=" + weight +
'}';
}

private static void show()
{
System.out.println("这是一个静态方法");
}
}

获取属性结构

获取当前运行时类及其父类中声明为public的属性

1
2
3
4
5
6
7
8
//拿到运行时类
Class class1 = Class.forName("com.jiang.Person");

//getFields()方法拿到当前运行时类以及父类权限为public的属性
Field[] fields = class1.getFields();
for (Field field : fields) {
System.out.println(field);
}

得到当前运行时类的所有权限的属性,但不包括父类的属性

1
2
3
4
5
//getDeclaredFields()方法得到当前运行时类的所有权限的属性,但不包括父类的属性
Field[] declaredFields = class1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}

获取属性的具体结构

权限修饰符 getModifiers()

数据类型 getType()

变量名 getName()

1
2
3
4
5
6
7
8
9
10
11
12
13
Field[] declaredFields1 = class1.getDeclaredFields();
for (Field declaredField : declaredFields1) {
//获取权限修饰符
int modifiers = declaredField.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");
//获取数据类型
Class type = declaredField.getType();
System.out.print(type.getName() + "\t");

//获取变量名
String name = declaredField.getName();
System.out.println(name);
}

获取方法结构

获取当前运行时类及其父类中声明为public的方法getMethods();

得到当前运行时类的所有权限的方法,但不包括父类的方法getDeclaredMethods();

1
2
3
4
5
6
7
8
9
10
11
Method[] methods = class1.getMethods();
for (Method method : methods) {
System.out.println(method);
}

System.out.println();

Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}

获取方法的具体结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//1、获取注解
Annotation[] annotations = declaredMethod.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}

//2、获取权限修饰符
int modifiers = declaredMethod.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");

//3、返回值类型
System.out.print(declaredMethod.getReturnType().getName() + "\t");

//4、方法名
System.out.print(declaredMethod.getName());

//5、形参列表
System.out.print("(");
Class[] parameterTypes = declaredMethod.getParameterTypes();
if(parameterTypes.length != 0)
{
for (int i = 0; i < parameterTypes.length; i++) {
if(i == parameterTypes.length - 1)
{
System.out.print(parameterTypes[i].getName() + "_args" + i);
break;
}
System.out.print(parameterTypes[i].getName() + "_args" + i + ",");
}
}
System.out.print(")");


// 6、抛出异常
Class[] exceptionTypes = declaredMethod.getExceptionTypes();

if(exceptionTypes.length != 0)
{
System.out.print("throws");
for (int i = 0; i < exceptionTypes.length; i++) {
if(i == exceptionTypes.length - 1)
{
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}


System.out.println();
System.out.println();
}

获取当前运行时类的构造器

1
2
3
4
5
6
7
8
9
10
//获取当前权限修饰符为public的构造器
Constructor[] constructor = class1.getConstructors();
for (Constructor constructor1 : constructor) {
System.out.println(constructor1);
}
//获取全部权限的构造器
Constructor[] declaredConstructor = class1.getDeclaredConstructors();
for (Constructor constructor1 : declaredConstructor) {
System.out.println(constructor1);
}

获取父类的泛型

1
2
3
4
5
6
7
8
9
10
11
//获取带泛型的父类
Type genericSuperclass = class1.getGenericSuperclass();
System.out.println(genericSuperclass);


//获取带泛型的父类的泛型
ParameterizedType genericSuperclass1 = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}

获取运行时类的注解、所在的包、接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//获取运行时类的接口
Class[] interfaces = class1.getInterfaces();

for (Class anInterface : interfaces) {
System.out.println(anInterface);
}

System.out.println();
//获取运行时类父类的接口
Class[] interfaces1 = class1.getSuperclass().getInterfaces();
for (Class aClass : interfaces1) {
System.out.println(aClass);
}


System.out.println();
//获取运行时类所在是包
Package aPackage = class1.getPackage();
System.out.println(aPackage);


System.out.println();
//获取类声明的注解
Annotation[] annotations = class1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}

调用运行时类指定的结构

调用属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//拿到运行时类
Class class1 = Class.forName("com.jiang.Person");
//创建运行时类对象
Person person = (Person)class1.newInstance();

//1、获取指定属性 要求权限为public 一般不用
Field id = class1.getField("id");
id.set(person,4);

int ids = (int)id.get(person);
System.out.println(ids);

//2、任何权限都可以拿到
Field name = class1.getDeclaredField("name");
//保证当前属性是可访问的
name.setAccessible(true);
name.set(person,"jiang");

String name1 = (String)name.get(person);
System.out.println(name1);

调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
//1、获取指定方法
//参数1:方法名,参数2:形参列表
Method getNation = class1.getDeclaredMethod("getNation", String.class);

getNation.setAccessible(true);
//invoke的返回值就是当前方法的返回值
getNation.invoke(person, "CHINA");

//2、任何调用静态方法
Method show = class1.getDeclaredMethod("show");
show.setAccessible(true);
//因为是一个静态方法,可以传递一个空对象
show.invoke(null);

调用构造器

1
2
3
4
5
6
7
Constructor declaredConstructor = class1.getDeclaredConstructor(String.class, int.class);

declaredConstructor.setAccessible(true);

Person o = (Person)declaredConstructor.newInstance("jiang", 1);

System.out.println(o);

面试题

描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader
是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

注解

注解定义

注解(也称元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在之后的某个时刻非常方便地这些数据。

这些定义太过抽象,简单来说就是注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”。注解就是代码的标签,注解起到标识做用。

三种标准注解

@override

表示当前的方法定义将覆盖超类中的方法。如果不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示。

1
2
3
4
5
@Target(ElementType.METHOD)  //只能用在方法上
@Retention(RetentionPolicy.SOURCE) //在编译器处理完之后,注解失效
public @interface Override {
}

@Deprecated

所修饰的元素已经过时,但为了项目的维护,不可以删除。如果程序员使用了注解为他的元素,那么编译器会发出警告信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
/**
* Returns the version in which the annotated element became deprecated.
* The version string is in the same format and namespace as the value of
* the {@code @since} javadoc tag. The default value is the empty
* string.
*
* @return the version string
* @since 9
*/
String since() default "";

/**
* Indicates whether the annotated element is subject to removal in a
* future version. The default value is {@code false}.
*
* @return whether the element is subject to removal
* @since 9
*/
boolean forRemoval() default false;
}

@SuppressWarnings

关闭不当的编译器警告信息,当代码出现一些警告时,比如变量为使用,泛型等。使用注解可以将警告关闭。注解在使用需要添加参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is <i>not</i> an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* <p> The string {@code "unchecked"} is used to suppress
* unchecked warnings. Compiler vendors should document the
* additional warning names they support in conjunction with this
* annotation type. They are encouraged to cooperate to ensure
* that the same names work across multiple compilers.
* @return the set of warnings to be suppressed
*/
String[] value();
}

元注解

元注解专门负责新注解的创建。注解其他的注解。

@Target

表示注解可以用在什么地方,可能的ElementType参数包括:

一个注解有多个参数。

参数 注解使用具体元素
CONSTRUCTOR 构造器的声明
FIELD 域声明(包括enum实例)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注解类型)或enum声明
ANNOTATION_TYPE 注解声明
1
2
3
4
5
6
7
8
9
10
11
12
13
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}

@Retention

表示需要在什么级别保存该注解信息,一个注解中有唯一的@Retention,默认参数是CLASS 。

参数有:

SOURCE: 注解将被编译器丢弃

CLASS : 注解在class文件中可用,但会被JVM丢弃。

RUNTIME: JVM将在运行 期也保留注解,因此可以通过反射机制读取注解信息。

1
2
3
4
5
6
7
8
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}

@Documented

它的作用是能够将注解中的元素包含到 Javadoc 中去。

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Inherited

允许子类继承父类的注解。

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

定义注解

类,接口,注解是同一级别的。

1
2
3
4
5
6
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
public int id();
public String decsription() default "no decsription";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AnnotationTest {
@AnnotationDemo(id = 47,decsription = "Password must contain at least one numeric")
public boolean validatePassword(String password)
{
return (password.matches("\\w*\\d\\w*"));
}

@AnnotationDemo(id = 48)
public String encryptPassword(String password)
{
return new StringBuilder(password).reverse().toString();
}
}


反射和注解
https://johnjoyjzw.github.io/2020/09/20/反射与注解/
Author
John Joy
Posted on
September 20, 2020
Licensed under