注解

Annotation

  • 作用:
    • 不是程序本身,可以对程序做出解释
    • 可以被其他程序(比如:编译器等)读取
  • 格式:
    • 注解是以“@注释名”在代码中国存在的,可以添加一些参数值
  • 可以附加在 package、class、method、field 等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

内置注解

  • @Override:定义在java.lang.Override 中,只适用于修饰方法,表示重写方法
  • @Deprecated: 定义在 java.lang.Deprecated 中 ,可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素
  • @SuppressWarnings: 定义在java.labg.SuppressWarnings 中,用来抑制编译时的警告信息,但该注释需要添加参数
java
 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
package com.kuang.annotation;

import java.util.ArrayList;
import java.util.List;

//什么是注解
public class Test01 extends Object {

    //@Override 重写的注解
    @Override
    public String toString() {
        return super.toString();
    }

    //@Deprecated 不推荐程序员使用,但可以使用,或者存在更好的方式
    @Deprecated
    public static void test() {
        System.out.println("Deprecated");
    }

    //镇压警告
    @SuppressWarnings("all")
    public void test02() {
        List list = new ArrayList();
    }

    public static void main(String[] args) {
        test();
    }
}

元注解

  • 作用:负责注解其他注解的注解

  • @Target:用于描述注解的使用范围

    • ElementType可取值:TYPE、FIELD、METHOD、PARAMETER等
  • @Retention:表示需要在什么级别保存该注释信息,表示注解的生命周期

    • RetentionPolicy可取值:(runtime>class>sources)
      • SOURCE:在原文件中有效,被编译器丢弃。
      • CLASS:在class文件有效,可能会被虚拟机忽略。
      • RUNTIME: 在运行时有效。
  • @Documented:说明该注解将被包含在 javadoc 中

  • @Inherited:说明子类可以继承父类中的该注解

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kuang.annotation;

import java.lang.annotation.*;

//测试元注解
public class Test02 {
    @MyAnnotation
    public void tets(){}
}

//@Target 用于描述注解的使用范围。value 可以省略
//@Target({ElementType.METHOD,ElementType.TYPE})
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//@Retention 表示我们的注解在什么地方还有效
//runtime>class>sources
@Retention(RetentionPolicy.RUNTIME)
//@Documented 表示是否将我们的注解生成在 JAVAdoc 中
@Documented
//@Inherited 子类可以继承父类的注解
@Inherited
//定义一个注解
@interface MyAnnotation {}

自定义注解

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

java
 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
package com.kuang.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义注解
public class Test03 {
    //显式赋值,无默认值时必须给注解赋值
    @MyAnnotation2(age=12)
    public void tets(){}

    //可以不写参数名,但 value 必须有值
    @MyAnnotation3("qwe")
    public void tets2(){}
}

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

//只有一个参数时,参数名为 value
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3 {
    String value();
}

反射

静态语言 &动态语言

动态语言:运行时可以改变其结构的语言。例:Object-C、C#、JavaScript、PHP、Python

静态语言:运行时结构不可变的语言。例:Java、C、C++

Java 不是动态语言,但 Java 可以称之为“准动态语言”,即 Java 有一定的动态性,我们可以利用反射机制获得类似动态语言的特性

Java Reflection

  • Reflection(反射)是 Java 被视为动态语言的关键,反射机制允许程序在执行期间借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

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

//什么是反射
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获得类的 class 对象
        Class c1 = Class.forName("com.kuang.reflection.User");
        System.out.println(c1);

        Class c2 = Class.forName("com.kuang.reflection.User");
        Class c3 = Class.forName("com.kuang.reflection.User");
        Class c4 = Class.forName("com.kuang.reflection.User");

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

//实体类:pojo,entity
class User{
    private String name;
    private int age;
    private int id;

    public User() {}

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

    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 "User{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", id=" + id +
        '}';
    }
}

得到 Class 类的方式

Class 类

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

java
1
public final Class getClass()

这个方法的返回值类型是一个 Class 类,Class 类是 Java 反射的源头


Class 类的常用方法:

获取 Class 类的方式

  • 已知具体的类,通过类的 class 属性获取,
java
1
Class clazz = Person.class;
  • 已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象
java
1
Class clazz = person.getClass();
  • 已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException
java
1
Class clazz = Class.forName("demo01.Student");
java
 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
package com.kuang.reflection;

//测试 class 类的创建方式有哪些
public class Test03 {
    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());

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

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

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

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

class Person {
    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 = "student";
    }
}

class Teacher extends Person {
    public Teacher() {
        this.name = "Teacher";
    }
}

Class 对象的类型

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

import java.lang.annotation.ElementType;

//所有类型的 Class
public class Test04 {
    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;
        //void
        Class c8 = void.class;
        //Class
        Class c9 = 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());
    }
}

Java 内存分析

类加载过程:

参考:https://javaguide.cn/java/jvm/class-loading-process.html

  • **加载:**将类的 class 文件字节码加载到内存,并将静态数据转化为方法区的运行时数据结构,然后生成一个 java.lang.Class 对象
  • **链接:**将java类二进制数据合并到 JRE中
    • **验证:**确保加载的类信息符合JVM规范,没有安全方面的问题
    • **准备:**正式为类变量 (static) 分配内存并设置类变量默认初始值的阶段(所以说static在初始化之前就已经有了一个值),这些内存都将在方法区中进行分配
    • 解析:虚拟机将常量池内的符号引用替换为直接引用的过程
  • 初始化:执行初始化方法 <font style="color:rgb(60, 60, 67);"><clinit> ()</font>方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。<font style="color:rgb(60, 60, 67);"><clinit> ()</font>方法会将该类的静态变量合并。
java
 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
package com.kuang.reflection;

public class Test05 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
        /*
        1、加载到内存,会在堆产生一个类对应的 Class 对象
        2、链接,链接结束后 m=0
        3、初始化
            <clinit>(){
                System.out.println("A类静态代码块初始化");
                m = 300;
                m = 100;
            }
            m = 100
         */
    }
}

class A{
    static {
        System.out.println("A类静态代码块初始化");
        m = 300;
    }

    static int m = 100;

    public A() {
        System.out.println("A类的无参构造初始化");
    }
}

类加载器:

java
 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
ppackage com.kuang.reflection;

public class Test07 {
    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 parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是哪个类加载器加载的
        ClassLoader classLoader = Class.forName("com.kuang.reflection.Test07").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"));
        /*
        D:\Java\jdk1.8\jre\lib\charsets.jar;
        D:\Java\jdk1.8\jre\lib\deploy.jar;
        D:\Java\jdk1.8\jre\lib\ext\access-bridge-64.jar;
        D:\Java\jdk1.8\jre\lib\ext\cldrdata.jar;
        D:\Java\jdk1.8\jre\lib\ext\dnsns.jar;
        D:\Java\jdk1.8\jre\lib\ext\jaccess.jar;
        D:\Java\jdk1.8\jre\lib\ext\jfxrt.jar;
        D:\Java\jdk1.8\jre\lib\ext\localedata.jar;
        D:\Java\jdk1.8\jre\lib\ext\nashorn.jar;
        D:\Java\jdk1.8\jre\lib\ext\sunec.jar;
        D:\Java\jdk1.8\jre\lib\ext\sunjce_provider.jar;
        D:\Java\jdk1.8\jre\lib\ext\sunmscapi.jar;
        D:\Java\jdk1.8\jre\lib\ext\sunpkcs11.jar;
        D:\Java\jdk1.8\jre\lib\ext\zipfs.jar;
        D:\Java\jdk1.8\jre\lib\javaws.jar;
        D:\Java\jdk1.8\jre\lib\jce.jar;
        D:\Java\jdk1.8\jre\lib\jfr.jar;
        D:\Java\jdk1.8\jre\lib\jfxswt.jar;
        D:\Java\jdk1.8\jre\lib\jsse.jar;
        D:\Java\jdk1.8\jre\lib\management-agent.jar;
        D:\Java\jdk1.8\jre\lib\plugin.jar;
        D:\Java\jdk1.8\jre\lib\resources.jar;
        D:\Java\jdk1.8\jre\lib\rt.jar;
        D:\JavaCode\study_code\annotation&reflection\annotation\target\classes;
        C:\Users\SZZY\AppData\Local\Programs\IntelliJ IDEA Ultimate\lib\idea_rt.jar

         */
    }
}

双亲委派机制

参考:https://javaguide.cn/java/jvm/classloader.html#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B

https://www.cnblogs.com/luckforefforts/p/13642685.html

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

获取运行时类的完整结构:

java
 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.kuang.reflection;

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

//获得类的信息
public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.kuang.reflection.User");

        //获得包名 + 类名:com.kuang.reflection.User
        System.out.println(c1.getName());
        //获得类名:User
        System.out.println(c1.getSimpleName());

        System.out.println("================");

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

        System.out.println("================");

        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        System.out.println("================");

        //获得类的方法
        //获得本类及父类的 public 方法
        Method[] methods = c1.getMethods();
        for(Method method: methods){
            System.out.println("正常的:"+method);
        }
        //获得本类的全部方法
        methods=c1.getDeclaredMethods();
        for(Method m: methods){
            System.out.println("getDeclaredMethods:"+m);

        }
        //获得指定方法
        //重载
        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();
        for(Constructor constructor: constructors){
            System.out.println(constructor);
        }
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for(Constructor constructor: declaredConstructors){
            System.out.println("全部:"+constructor);
        }
        //获得指定构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定:"+declaredConstructor);

    }
}

获取类的名称:

  • clazz.getName(); //类的全限定名
  • clazz.getSimpleName(); //简单类名
  • clazz.getPackage(); // 包信息
  • clazz.getModifiers(); // 修饰符

获得类的属性:

  • getFields() :获得 public 属性
  • getDeclaredFields() : 获得全部属性
  • getDeclaredField(String name) :获得指定属性的值

获得类的方法:

  • getMethods():获取本类及父类的所有public方法
  • getDeclaredMethods(): 获取本类的所有方法
  • getMethod(String name, Class… parameterTypes): 获取本类及父类的指定public方法
  • getDeclaredMethod(String name, Class… parameterTypes) :获取本类的所有方法

获得构造器

  • getConstructors():获得所有 public 构造器
  • getDeclaredConstructors():获得所有构造器
  • getConstructor():获取本类及父类的指定public构造器
  • getDeclaredConstructor(Class… parameterTypes):获取本类的所有构造器

**Class<?>... parameterTypes** = 方法参数的类型列表 ,它的值根据他前面的参数所需的类型确定。

通过反射动态创建对象

  1. 通过无参构造器创建对象 Class.newInstance()
  • 使用 Class 对象的 newInstance() 方法调用类(该类必须有一个无参构造器且访问权限足够)的无参构造器
  • Class.newInstance() Java 9 起已过时, 推荐改用 getDeclaredConstructor().newInstance()
  • newInstance() 返回值为 Object,需要强转类型为 User
java
1
2
3
4
5
6
7
 //通过反射获得 Class 对象
        Class c1 = Class.forName("com.kuang.reflection.User");

        //通过无参构造器创建对象
        //本质是 newInstance() 调用了类的无参构造器:public User() {}
        User user = (User) c1.newInstance();
        System.out.println(user);

  1. 通过有参构造器创建对象
java
1
2
3
4
5
6
 //通过有参构造器创建对象
        //获取指定参数类型的构造器
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //调用构造器创建对象
        User user2 = (User)constructor.newInstance("qwe", 1, 2);
        System.out.println(user2);

  1. 通过反射调用普通方法
java
1
2
3
4
5
6
7
//通过反射调用普通方法
        User user3 = (User)c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke(对象, "方法的值") : 激活(对象, "方法的值")
        setName.invoke(user3, "name1");
        System.out.println(user3.getName());

那么在第一步已经用 (User) 强转了 user ,user 变量的真实类型便是 User,在之后的调用应属于正常的 Java 方法调用,不需要反射也可以直接调用:

而通过反射调用方法也有好处:

  • 可使用 setAccessible(true); 调用 private 私有方法
  • 动态调整调用的方法
  1. 通过反射操作属性
java
1
2
3
4
5
6
7
        //通过反射操作属性
        User user4 = (User)c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //通过 setAccessible(true) 关闭程序安全检测,可访问私有属性
        name.setAccessible(true);
        name.set(user4, "name2");
        System.out.println(user4.getName());

性能对比:

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

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

//分析性能
public class Test10 {

    //普通调用
    public static void test01() {
        User user = new User();
        long starTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次"+(endTime - starTime)+"ms");
    }

    //反射调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        long starTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次"+(endTime - starTime)+"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 starTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式(关闭检测)执行10亿次"+(endTime - starTime)+"ms");
    }

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        test01();
        test02();
        test03();
    }
}

反射操作泛型

泛型(Generics)就是 参数化类型 —— 把数据类型当作参数传递。
它让类、接口、方法能在 编写时不指定具体类型,而是在 使用时再确定类型

  • 泛型的好处
  1. 类型安全:编译时就能发现类型错误。
  2. 避免强制类型转换:减少 ClassCastException
  3. 提高代码复用性:写一次逻辑,支持多种数据类型。
java
 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
package com.kuang.reflection;

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

//通过反射获得泛型
public class Test11 {
    //参数类型为泛型:<String,User>,<User>
    public void test01(Map<String,User> map, List<User> list) {
        System.out.println("test01");
    }

    //返回值为泛型:Map<String,User>
    public Map<String,User> test02() {
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //通过反射获取 test01 方法对象
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        //获取该方法的所有参数的泛型类型,返回 Type[] 数组
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        //遍历参数类型
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("!"+genericParameterType);
            //判断参数类型是否为泛型
            if (genericParameterType instanceof ParameterizedType) {
                //如果是,强转为 ParameterizedType(参数化类型),调用 getActualTypeArguments() 获取泛型里的真实类型:对 Map<String,User> 来说,结果是 [String, User];对 List<User> 来说,结果是 [User]。
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("#"+actualTypeArgument);
                }
            }
        }

        //获取 test02 方法对象
        method = Test11.class.getMethod("test02", null);
        //获取 test02 方法的返回类型,结果是 Map<String, User>
        Type genericReturnType = method.getGenericReturnType();

        if  (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("&"+actualTypeArgument);
            }
        }

    }
}

反射操作注解

ORM (Object relationship Mapping) 对象关系映射

  • 类和表结构对应
  • 属性和字段对应
  • 对象和记录对应
java
 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
package com.kuang.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

//练习反射操作注解
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.kuang.reflection.Student2");

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

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

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

}

@Table("db_student")
class Student2 {

    @Filed(columnName = "db_id", type = "int", length = 10)
    private int id;
    @Filed(columnName = "db_age", type = "int", length = 10)
    private int age;
    @Filed(columnName = "db_name", type = "varchar", length = 3)
    private String name;

    public Student2() {}

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface  Filed{
    String columnName();
    String type();
    int length();
}