注解与反射

Shadow, with her veil drawn, follows Light in secret meekness,with her silent steps of love.

阴影戴上她的面幕,秘密地,温顺地,用她的沉默的爱的脚步,跟在“光”后边。

注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

Java 语言中的类、方法、变量、参数和包等都可以被标注。

Annotation作用

  • 不是程序本身,可以对程序作出解释
  • 可以被其他程序(如编译器等)读取

Annotation格式

  • 注解是以”@注释名”在代码中存在,还可以添加一些参数值

Annotation在哪里使用?

可以附加在package,class,method,field等上面相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。表示不鼓励程序员
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解)是

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。(SOURCE < CLASS < RUNTIME)

    • a) 若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如,” @Override” 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,”@Override” 就没有任何作用了。
    • b) 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。
    • c) 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。
  • @Documented - 标记这些注解是否包含在用户文档中。

  • @Target - 标记这个注解应该是哪种 Java 成员。(被描述的注解可以用在什么地方)

  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

  • 使用元注解自定义一个简单注解

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

    使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。

    定义 Annotation 时,@interface 是必须的。

    注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。

    1. @Documented*

    类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。

    定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。

    1. @Target(ElementType.TYPE)

    前面我们说过,ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。

    @Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,MyAnnotation1 是来修饰”类、接口(包括注释类型)或枚举声明”的注解。

    定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地方;若没有 @Target,则该 Annotation 可以用于任何地方。

    1. @Retention(RetentionPolicy.RUNTIME)

    前面我们说过,RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。

    @Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。

    定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。

从 Java 7 开始,额外添加了 3 个注解

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

Annotation 架构

从中,我们可以看出:

  1. 1 个 Annotation 和 1 个 RetentionPolicy 关联。

    每1个Annotation对象,都会有唯一的RetentionPolicy属性。

  2. 1 个 Annotation 和 1~n 个 ElementType 关联。

    对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。

  3. Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等。

    Annotation 的每一个实现类,都 “和 1 个 RetentionPolicy 关联” 并且 “ 和 1~n 个 ElementType 关联”。

1
2
3
4
5
6
7
@Deprecated  -- @Deprecated 所标注内容,不再被建议使用。
@Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。
@Documented -- @Documented 所标注内容,可以出现在javadoc中。
@Inherited -- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。
@Retention -- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
@Target -- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
@SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。

自定义注解

使用@interface自定义注解时,自动继承 java.lang.annotation.Annotation接口

分析:

  • @interface用来声明一个注解,格式public @interface 注解名{定义内容}
  • 其中每一个方法实际上是声明了一个参数配置;
  • 方法的名称就是参数的名称;
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum);
  • 可以通过default来声明参数的默认值;
  • 如果只有一个参数成员,一般参数名为value;
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义注解类
public class TestAnnotation{
@MyAnnotation(name = "bobo")// name没有默认值,所以必须赋值,age有默认值可以不赋值
public void test(){}
@MyAnnotation0("bobo") //当只有一个参数并且参数名为value时,可以直接写参数值,省略参数名
public void test0(){}
}

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
//注解的参数 : 参数类型 + 参数名();
String name();
int age() default 0;
int id() default; //如果默认值为 -1 ,代表不存在
}

@interface MyAnnotation0{
String value(); //当只有一个参数时,参数名可以默认为value
}

Annotation 的作用

  1. 编译检查
  2. 在反射中使用 Annotation
  3. 根据 Annotation 生成帮助文档
  4. 能够帮忙查看查看代码

反射机制

Class c = Class.forName("java.lang.String")

反射指的是我们可以在运行期间加载、探知、使用编译期间完全未知的类。是一个动态的机制,允许我们通过字符串来指挥程序实例化,操作属性、调用方法。使得代码提高了灵活性,但是同时也带来了更多的资源开销。

加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个 类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。

正常方式:引入需要的“包类”名称—>通过new实例化—>取的实例化对象

反射方式:实例化对象—>getCLass()方法—>得到完整的“包类”名称

Java中为什么需要反射?反射要解决什么问题?

Java中编译类型有两种:

  • 静态编译:在编译时确定类型,绑定对象即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。

反射是Java被视为动态(或准动态)语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。

反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误。

实现Java反射机制的类都位于java.lang.reflect包中:

  1. Class类:代表一个类
  2. Field类:代表类的成员变量(类的属性)
  3. Method类:代表类的方法
  4. Constructor类:代表类的构造方法
  5. Array类:提供了动态创建数组,以及访问数组的元素的静态方法

优点 :可以实现动态创建对象和编译,体现出很大的灵活性

缺点 :对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且他满足我们的要求,这类操作总是慢于直接执行相同的操作。

获取Class类的对象

举例

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
62
63
64
65
66
67
68
69
70
71
package com.bobo.reflection;
//什么叫反射
public class Test01 extends Object{
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
Class<?> c1 = Class.forName("com.bobo.reflection.User");
System.out.println(c1);

Class<?> c2 = Class.forName("com.bobo.reflection.User");
Class<?> c3 = Class.forName("com.bobo.reflection.User");
Class<?> c4 = Class.forName("com.bobo.reflection.User");

//一个类在内存中只有一个class对象
//一个类被加载后,类的整个结构都会被封装在class对象
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());

}
}

//实体类 Pojo
class User{
private String name;
private int id;
private int age;

public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}

public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}

/*
输出结果:
class com.bobo.reflection.User //得到类的全类名
789451787
789451787
789451787
//说明一个类在内存中只有一个class对象 , 因为创建三个对象的hashcode相同
*/
  1. 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

    Class c2 = Class.forName("com.bobo.reflection.Student");

  2. 一直某个类的实例,调用该实例的getClass()方法获取Class对象

    Class c1 = person.getClass();

  3. 一直一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常

    Class<Student> c3 = Student.class;

  4. 内置基本数据类型可以直接用类名.Type

    Class<Integer> c4 = Integer.TYPE;

  5. 还可以利用ClassLoader

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
62
63
64
65
66
package com.bobo.reflection;

//测试Class类的创建方式有哪些
public class Test02 {

public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:"+person.name);

//方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());

//方式二:通过forName获得
Class c2 = Class.forName("com.bobo.reflection.Student");
System.out.println(c2.hashCode());

//方式三:通过类名.class获得
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());

//方式四:基本内置类型的包装类都有一个Type属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);

//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}

}

class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name="学生";
}
}
class Teacher extends Person{
public Teacher() {
this.name="学生";
}
}

/*
输出结果:
这个人是:学生
1950409828
1950409828
1950409828
int
class com.bobo.reflection.Person
*/

所有类型的class对象

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
package com.bobo.reflection;
import java.lang.annotation.ElementType;

//所有类型的Class
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举类型
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class

System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);


//只要是元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];

System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}


/*
输出结果:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
580220585
580220585
*/
  • 只要是元素类型与维度一样,就是同一个Class

类加载内存分析

JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用的Java类型的过程。

  1. 加载

    将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口。

  2. 链接

    将Java类的二进制代码合并到JVM的运行状态之中(JRE)。

    • 验证
      确保加载的类信息符合JVM规范,没有安全方面的问题。
    • 准备
      正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成
    • 解析
      虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程。
  3. 初始化

    初始化阶段是执行类构造器()方法的过程。类构造器()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。

    • 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先初始化其父类。
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。

类的初始化

父类

1
2
3
4
5
6
class Father{
static int b = 2;
static {
System.out.println("父类被加载");
}
}

子类

1
2
3
4
5
6
7
8
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
  1. 主动引用(一定会初始化)
  • new一个类的对象;

  • 当虚拟启动时,先初始化main方法所在的类;

  • 调用类的静态成员(除了final常量)和静态方法;

  • 使用java.lang.reflect包的方法对类进行反射调用;

  • 当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类

实现类new一个类的对象主动引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.bobo.reflection;

//测试类什么时候会初始化
public class Test {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) {
//主动引用
Son son = new Son();
}
}

/*
main类被加载
父类被加载
子类被加载
*/

实现类反射产生主动引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.bobo.reflection;

//测试类什么时候会初始化
public class Test {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//反射产生主动引用
Class.forName("com.bobo.reflection.Son");
}
}

/*
main类被加载
父类被加载
子类被加载
*/
  1. 被动引用
  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。例如:通过子类引用父类的静态变量,不会导致子类初始化。
  • 通过数组定义类引用,不会触发此类的初始化。
  • 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)。

实现类子类引用父类的静态变量,不会导致子类的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.bobo.reflection;
//测试类什么时候不会初始化
public class Test {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) {

//不会产生类的引用的方法
//子类引用父类的静态变量,不会导致子类的初始化
System.out.println(Son.b);
}
}

/*
main类被加载
父类被加载
2
*/

实现类只是为一片内存赋名,不会初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bobo.reflection;
//测试类什么时候不会初始化
public class Test {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) {
//只是为一片内存赋名,不会初始化
Son[] array = new Son[5];
}
}

/*
main类被加载
*/

实现类只是为一片内存赋名,不会初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.bobo.reflection;
//测试类什么时候不会初始化
public class Test {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) {
//引用常量不会触发此类的初始化
System.out.println(Son.M);
}
}

/*
main类被加载
1
*/

类加载器的原理

类缓存

标准的Java SE类加载器可以按要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象。

类加载的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类加载器分类

  • 自底向上检查类是否已装载

  • 自顶向下尝试加载类

  1. 引导类加载器(bootstrap class loader)

    1. 它用来加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar,sun.boot.class.path路径下的内容),是用原生代码(C语言)来实现的,并不继承自 java.lang.ClassLoader。
    2. 加载扩展类和应用程序类加载器。并指定他们的父类加载器。
  2. 扩展类加载器(extensions class loader)

    1. 用来加载 Java 的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容) 。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java类。
    2. 由sun.misc.Launcher$ExtClassLoader实现。
  3. 应用程序类加载器(application class loader)

    1. 它根据 Java 应用的类路径(classpath,java.class.path 路径下的内容)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。
    2. 由sun.misc.Launcher$AppClassLoader实现。
  4. 自定义类加载器

    1. 开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
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
package com.bobo.reflection;

public class Test06 {
public static void main(String[] args) throws ClassNotFoundException {

//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);

//获取系统类加载起的父类加载器 --> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);

//获取扩展类加载起的父类加载器 --> 根类加载器(C/C++)
ClassLoader grandparent = parent.getParent();
//用来装载核心类库,改加载类无法直接获取
System.out.println(grandparent);

//测试当前类是哪个加载起加载的
ClassLoader classLoader = Class.forName("com.bobo.reflection.Test06").getClassLoader();
System.out.println(classLoader);

//测试jdk内置的类是哪个加载起加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);

//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}

/*
输出结果:
jdk.internal.loader.ClassLoaders$AppClassLoader@55054057
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2f0e140b
null
jdk.internal.loader.ClassLoaders$AppClassLoader@55054057
null
/Users/renbo/Desktop/研究生/学习笔记/编程/JAVA/狂神/relfection/target/classes
*/
  • 使用JDK11编译结果如上

  • 使用JDK8编译结果如下

    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
    sun.misc.Launcher$AppClassLoader@18b4aac2
    sun.misc.Launcher$ExtClassLoader@1b6d3586
    null
    sun.misc.Launcher$AppClassLoader@18b4aac2
    null
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/charsets.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/deploy.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/cldrdata.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/dnsns.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/jaccess.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/jfxrt.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/localedata.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/nashorn.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunec.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/zipfs.ja;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/javaws.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jce.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfr.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfxswt.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jsse.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/management-agent.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/plugin.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/resources.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/ant-javafx.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/dt.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/javafx-mx.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/jconsole.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/packager.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/sa-jdi.jar;
    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar;
    /Users/renbo/Desktop/研究生/学习笔记/编程/JAVA/狂神/relfection/target/classes

双亲委派机制

  1. 如果一个类加载器收到了类加载请求,它并不会自己先加载,而是把这个请求委托给父类的加载器去执行
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的引导类加载器;
  3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制
  4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常

优势

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改
    • 自定义类:java.lang.String (没用)
    • 自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)

获得类的运行时结构

Java中反射获取类的成员时需要调用getFields()、getDeclaredFields();getMethods()、getDeclaredMethods();getConstructors()、getDeclaredConstructoers()等方法获取Field、Method、Constructor对象,这几对方法的主要区别如下:

  1. getFields()与getDeclaredFields()

    getFields()返回类中所有public的字段,包括从父类或接口继承的public字段;
    getDeclaredFields()返回本类中声明的所有字段,包括public、protected、private字段,不包括从父类或接口继承的字段

  2. getMethods()与getDeclaredMethods()
    getMethods()返回类中所有public的方法,包括从父类或接口继承的public方法
    getDeclaredMethods()返回本类中声明的所有方法,包括public、protected、private方法,不包括从父类或接口继承的方法

  3. getConstructors()与getDeclaredConstructors()
    getConstructors()返回类中所有public的构造方法;
    getDeclaredConstructors()返回类中声明的所有构造方法,包括public、protected、private构造方法

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.bobo.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//获得类的信息
public class Test07 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.bobo.reflection.User");
User user = new User();
c1 = user.getClass();

//获得类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName()); //获得类名

//获得类得属性
System.out.println("====================================");
Field[] fields = c1.getFields(); //getFields()只能找到public属性
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
Field name = c1.getDeclaredField("name");
System.out.println(name);

//获得类的方法
System.out.println("====================================");
Method[] methods = c1.getMethods(); //获得本类及其父类的全部public方法
Method[] declaredMethods = c1.getDeclaredMethods(); //获得本类的全部方法

for (Method method : methods) {
System.out.println("正常的:"+method);
}
for (Method declaredMethod : declaredMethods) {
System.out.println("declaredMethod:"+declaredMethod);
}

System.out.println("====================================");
//获得指定方法
//重载
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);

//获得指定的构造器
System.out.println("====================================");
Constructor[] constructors = c1.getConstructors();
Constructor[] declaredConstructors = c1.getDeclaredConstructors();

for (Constructor constructor : constructors) {
System.out.println(constructor);
}
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("#"+declaredConstructor);
}

System.out.println("====================================");
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定+"+declaredConstructor);
}
}

/*
com.bobo.reflection.User
User
====================================
private java.lang.String com.bobo.reflection.User.name
private int com.bobo.reflection.User.id
private int com.bobo.reflection.User.age
private java.lang.String com.bobo.reflection.User.name
====================================
正常的:public java.lang.String com.bobo.reflection.User.getName()
正常的:public java.lang.String com.bobo.reflection.User.toString()
正常的:public void com.bobo.reflection.User.setName(java.lang.String)
正常的:public int com.bobo.reflection.User.getId()
正常的:public void com.bobo.reflection.User.setId(int)
正常的:public int com.bobo.reflection.User.getAge()
正常的:public void com.bobo.reflection.User.setAge(int)
正常的:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
正常的:public final void java.lang.Object.wait() throws java.lang.InterruptedException
正常的:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
正常的:public boolean java.lang.Object.equals(java.lang.Object)
正常的:public native int java.lang.Object.hashCode()
正常的:public final native java.lang.Class java.lang.Object.getClass()
正常的:public final native void java.lang.Object.notify()
正常的:public final native void java.lang.Object.notifyAll()
declaredMethod:public java.lang.String com.bobo.reflection.User.getName()
declaredMethod:public java.lang.String com.bobo.reflection.User.toString()
declaredMethod:public void com.bobo.reflection.User.setName(java.lang.String)
declaredMethod:public int com.bobo.reflection.User.getId()
declaredMethod:public void com.bobo.reflection.User.setId(int)
declaredMethod:public int com.bobo.reflection.User.getAge()
declaredMethod:public void com.bobo.reflection.User.setAge(int)
====================================
public java.lang.String com.bobo.reflection.User.getName()
public void com.bobo.reflection.User.setName(java.lang.String)
====================================
public com.bobo.reflection.User()
public com.bobo.reflection.User(java.lang.String,int,int)
#public com.bobo.reflection.User()
#public com.bobo.reflection.User(java.lang.String,int,int)
====================================
指定+public com.bobo.reflection.User(java.lang.String,int,int)
*/

invoke()方法:用于调用指定的方法

1
Object invoke(Object obj,Object... args)
  • 第一个Object对应原方法的返回值,若原方法无返回值则返回null
  • 括号里的第一个Object代表调用这个方法的对象
  • 若原方法形参列表为空,Object[] args为null
  • 如果原方法声明为private,需要在调用invoke()之前调用setAccessible()方法

setAccessible()方法:用于启动和禁止访问安全检查的开关

1
void setAccessible(boolean flag)

参数为false表示反射的对象应该实施访问检查,参数为true则关闭

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
package com.bobo.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//动态的创建对象,通过反射
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("com.bobo.reflection.User");

//构造一个对象
//User user = (User) c1.newInstance(); //本质是调用了类无参构造器
//System.out.println(user);
Constructor constructor = (Constructor) c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("bobo", 001, 18);
System.out.println(user2);

//通过反射调用普通方法
User user3 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke() 激活
//(对象,"方法的值")
setName.invoke(user3,"bobo");
System.out.println(user3.getName());

//通过反射操作属性
System.out.println("================================");
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测 属性或者方法的 setAccessible(true)
name.setAccessible(true);
name.set(user4,"bobo2");
System.out.println(user4.getName());
}
}

/*
输出结果:
User{name='bobo', id=1, age=18}
bobo
================================
bobo2
*/

性能对比分析

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
package com.bobo.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//分析性能问题
public class Test09 {

//普通方式调用
public static void test01(){
User user = new User();
//获取开始时间
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
//获取结束时间
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
}

//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
//获取开始时间
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
//获取结束时间
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
}

//反射方式调用 关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
//获取开始时间
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
//获取结束时间
long endTime = System.currentTimeMillis();
System.out.println("关闭检测,反射方式执行10亿次:"+(endTime-startTime)+"ms");
}

public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
test01();
test02();
test03();
}
}
1
2
3
普通方式执行10亿次:5ms
反射方式执行10亿次:2470ms
关闭检测,反射方式执行10亿次:1169ms

反射操作泛型

Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是编译一旦完成,所有和泛型有关的类型全部被擦除。

为了通过反射操作这些类型以迎合实际开发的需要,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • ParameterizedType:表示一种参数化的类型,比如Collection<String>
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式,比如? extends Number? super Integer。(wildcard是一个单词:就是”通配符“)
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
package com.bobo.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

//通过反射获取泛型
public class Test {

public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}

public Map<String ,User> test02(){
System.out.println("test02");
return null;
}

public static void main(String[] args) throws NoSuchMethodException {
//参数是泛型
Method method = Test.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("genericParameterType:"+genericParameterType);
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
//返回值是泛型
method = Test.class.getMethod("test02", null);
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
1
2
3
4
5
6
7
8
9
返回结果:
genericParameterType:java.util.Map<java.lang.String,com.bobo.reflection.User>
class java.lang.String
class com.bobo.reflection.User
genericParameterType:java.util.List<com.bobo.reflection.User>
class com.bobo.reflection.User

class java.lang.String
class com.bobo.reflection.User

反射操作注解

获取注解的方法:

  • Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
  • Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
  • Class.getDeclaredAnnotations() 获取自己声明的注解
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.bobo.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//练习反射操作注解
public class Test11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.bobo.reflection.Students");

//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}

//获得注解的value的值
Tablebobo tablebobo = (Tablebobo) c1.getAnnotation(Tablebobo.class);
String value = tablebobo.value();
System.out.println(value);

//获得类指定的注解
Field name = c1.getDeclaredField("name");
Filebobo annotation = name.getAnnotation(Filebobo.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}

@Tablebobo("db_students")
class Students{
@Filebobo(columnName = "db_id",type = "int",length = 10)
private int id;
@Filebobo(columnName = "db_age",type = "int",length = 10)
private int age;
@Filebobo(columnName = "db_name",type = "varcher",length = 3)
private String name;

public Students() {
}
public Students(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}

public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Students{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}

//@Target 注解作用域
//@Rtention 在什么级别可以获取
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablebobo{
String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filebobo{
String columnName();
String type();
int length();
}
1
2
3
4
5
6
运行结果:
@com.bobo.reflection.Tablebobo("db_students")
db_students
db_name
varcher
3
查看评论