java 注解与反射

本文详细介绍了Java中的注解(Annotation)和反射(Reflection)机制。内容包括注解的定义、内置注解、元注解和自定义注解的使用,以及反射的概述、获取反射对象、Class类、类加载器的作用、类的初始化等关键知识点。此外,还探讨了反射在获取类的运行时结构、动态创建对象、执行方法、获取泛型信息和注解信息等方面的应用。
摘要由CSDN通过智能技术生成

学习视频来自B站,感谢狂神的分享:B站视频地址

注解 java.annotation

1.什么是注解

  • Annotation是JDK5.0引进的新技术
  • Annotation的作用:
    1.不是程序本身,但可以对程序作出解释(跟注释类似)
    2.可以被其他程序(比如编译器)读取
  • Annotation的格式
    注解以"@注解名"格式存在代码中,有些可以添加参数
    比如 @SuperssWarning(value = “uncheked”)
  • Annotation在哪里使用?
    可以添加在package、class、method、field等上面,相当于添加了额外的辅助信息。可以通过反射机制编程,实现对这些元数据的访问

2.内置注解

  1. @Override: 只能用于修饰方法,说明该方法是重写父类的方法(java1.5更新)
  2. @Deprecated: 可修饰方法、属性、类,表示存在安全问题或有更好选择而不鼓励使用。如果强行使用,则编译器会发出警告(java1.5更新)
  3. @SuppressWarning: 用于抑制编辑器所显示的警告。该注解需要添加参数,具体如下(java1.5更新):
    @SuppressWarning(“all”) //抑制单一警告
    @SuppressWarning(value = {“unchecked”,“rawtypes”}) //抑制多个警告
    SuppressWarning参数如下:
参数说明
all抑制所有警告
rawtypes在类参数上使用泛型时抑制与非特定类型相关的警告
boxing抑制装箱、拆箱操作时候的警告
cast抑制映射相关的警告
dep-ann抑制启用注释的警告
deprecation抑制过期方法警告
fallthrough抑制确在switch中缺失breaks的警告
finally抑制finally模块没有返回的警告
hiding抑制相对于隐藏变量的局部变量的警告
incomplete-switch忽略没有完整的switch语句
nls忽略非nls格式的字符
null忽略对null的操作
restriction抑制与不鼓励或禁止引用的使用相关的警告
serial忽略在serializable类中没有声明serialVersionUID变量
static-access抑制不正确的静态访问方式警告
synthetic-access抑制子类没有按最优方法访问内部类的警告
unchecked抑制没有进行类型检查操作的警告
unqualified-field-access抑制没有权限访问的域的警告
unused抑制没被使用过的代码的警告
resource对于J2EE,可使用@Resource来完成依赖注入
或者叫资源注入,但是当你在一个类中
使用已经使用注解的类,却没有为其注入依赖时,
"resource"关键字会抑制其没有注入依赖的警告。
  1. @SafeVarargs 专门为抑制“堆污染”警告提供的(java.17更新)。
  2. @FunctionalInterface 用来指定某个接口必须是函数式接口,否则就会编译出错(java1.8更新)。
    函数式接口:Java8规定,如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。

3.元注解

元注解的作用,就是注解其他注解。java定义了4个标准的meta-annotation,为其他annotation提供类型说明。
4个元注解,在java.lang.annotation包中,具体如下:

名称作用
@Target用户描述注解的适用范围
@Retention表示需要在什么级别保存该注解信息
用于描述注解的声明周期
Source < Class < Runtime
@Document说明该注解,将被保存在javadoc中
@Inhetited说明子类可以继承父类的注解
//------------通过自定义注解,了解四个元注解的作用:------------

//@Traget :定义注解可以在什么地方有效,传参为枚举类型
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//@Retention :注解作用域:在什么地方有效
// runtime > class > source
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented :是否将注解,生成到javac中
@Documented
//子类可以继承父类的注解
@Inherited
@interface MyAnnotation{

}

4.自定义注解

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

  1. @interface用来声明注解,格式:public @ interface 注解名 {定义内容}
  2. 其中每个方法,实际上是声明了一个配置参数
  3. 方法的名称就是参数名称
  4. 返回值类型就是参数类型(只能返回基本类型:Class,String,enum)
  5. 可以通过default来指定参数默的认值
  6. 如果只有一个参数成员,一般参数名为value
  7. 注解元素必须有值。定义注解元素时,经常用空字符串、0作为默认值
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Inherited
@interface MyAnnotation2{
    //注解的参数格式: 参数类型 + 参数名 + "()"
    //使用default 设置默认值
    String name() default "hello";
    int id() default -1;    //设置成-1,可以表示不存在等含义
    String[] school() default {"清华","北大"};
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Inherited
@interface MyAnnotation3{
    //如果只有一个参数,则参数名写成value,
    //使用注解的时候可省略参数名( value= )
    String value();
}

反射 java.reflection

1.反射概述

静态 & 动态语言
静态 & 动态语言

  • Reflection(反射)是被视为动态语言的关键。反射机制允许程序在执行期借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性或方法(包括private修饰的字段)
Class c = Class.forName("java.lang.String")
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过对象看到类的结构。这个对象就像一面镜子,通过这个镜子看到类的结构,所以称之为:反射
    类加载注解

2.获取反射对象

反射机制提供的功能:

  • 运行时判断任意一个对象所属的类
  • 运行时构造任意一个类的对象
  • 运行时判断任意一个类所具有的成员变量和方法
  • 运行时获取泛型信息
  • 运行时调用任意一个对象的成员变量或方法
  • 运行时处理注解
  • 生成动态代理
    反射优缺点:
  • 优点:动态创建对象和编译,灵活性高
  • 缺点:性能不高:反射是一种解释操作,告诉JVM我们希望做什么,然后它满足我们的要求。这比直接执行相同操作慢
//-------- java反射的主要API: ------------
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Construtor:代表类的构造器
//根据包路径,得到Class
public class MyReflection01 {
    public static void main(String[] args) {
        try {
            //一个类在内存中,只有一个Class对象
            //类被加载后,整个结构都会被封装在Class对象中
            Class c1 = Class.forName("com.xinan.Annotation.user");
            System.out.println(c1);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//定义一个类,在上面利用反射得到Class对象
@MyAnnotation2
class user{
    String name;
    int age;
    private void print(){
        System.out.println("这是私有方法");
    }
}

3.Class类

  • 在Object类中定义了 public final Class getClass() 方法,此方法被所有子类继承。
  • 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对象的方法:

  1. 已知具体的类,通过类的class属性获取(最安全可靠,性能最高)
		Class class = Persion.Class;
  1. 已知某个类的实例,调用该实例的 getClass()方法获取
		Class class = persion.Class;
  1. 已知类的全路径和类名,可通过Class类的静态方法forName()获取,可能抛出ClassNotFontException异常
		Class class = Class.forName("com.xxx.Person");
  1. 基本类型的封装类,可以 类名.Type
  2. 利用ClassLoader获取
// ------------- 获取Class对象的几种方法 ---------------
public class MyReflection02 {
    public static void main(String[] args) {
        try {
            Person person = new Student();
            System.out.println(person.name);
            //1.通过Class属性获得
            Class c1 = Student.class;
            //2.通过getClass方法获取
            Class aClass = person.getClass();
            //3.通过全路径获取
            Class c3 = Class.forName("com.xinan.Annotation.Student");
            //4.通过基本数据类型的 包装类的 Type属性
            Class<Integer> type = Integer.TYPE;

            //获取父类
            Class superclass = c1.getSuperclass();
            System.out.println(c1);
            System.out.println(superclass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class Person{
    public String name;
    //无参与有参构造
    public Person() {}
    public Person(String name) {
        this.name = name;
    }
}

class Student extends Person{
    public Student(){ this.name = "学生"; }
}

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

4.哪些类型有Class对象

类型说明
class外部类,内部类(四大内部类)
interface接口
[]数组
enum枚举
annotation注解@interface
primitive type基本数据类型
void
Class c1 = Object.class;    //类      class java.lang.Object
Class c2 = Comparable.class;//接口    interface java.lang.Comparable
Class c3 = String[].class;  //一维数组   class [Ljava.lang.String;
Class c4 = int[][].class;   //二维数组   class [[I
Class c5 = Override.class;  //注解		interface java.lang.Override
Class c6 = ElementType.class;//枚举      class java.lang.annotation.ElementType
Class c7 = Integer.class;   //基本数据类型 class java.lang.Integer
Class c8 = void.class;      //void           void
Class c9 = Class.class;     //Class本身     class java.lang.Class

//只有元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());//24408301
System.out.println(b.getClass().hashCode());//24408301

5.java内存分析

java内存分类

类别作用
1.存放new的对象和数组
2.可以被所有的线程共享,不会存放别的对象引用
1.存放基本类型变量(包含数值)
2.引用对象的变量(存放这个引用在堆中的地址)
方法区1.可以被所有的线程共享
2.包含所有的class和static变量

类加载过程

1. 加载(load)
将类的class文件字节码读入内存,将静态数据转换成方法区的运行时数据结构,然后为之创建一个java.lang.Class对象。此过程由类加载器完成。
2. .链接(link)
将java类的二进制代码,合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息,符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存,并设置类变量的默认初始值,这些内存都在方法区中进行分配
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3. 初始化(initialize)
执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类的信息,不是构造该类对象的构造器)
当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发其父类的初始化
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

静态代码块:用staitc声明,jvm加载类时执行,仅执行一次
构造代码块:类中直接用{}定义,每一次创建对象时执行。
执行顺序优先级:静态块 > main() > 构造块 > 构造方法。

new 对象创建过程

  1. 在堆区给对象分配内存:包含父类和本类,但不包含静态变量,静态变量在加载时已经在分配
  2. 对所有实例变量进行默认赋值:将方法去对于实例变量的定义,拷贝一份到堆区,然后进行赋值。如果有引用类型属性,会在栈内存中申请一个空间用来指向的实际对象。
  3. 执行初始化代码:先初始化父类,后后初始化子类。

盗了一张图,原作连接:new创建对象就两步:初始化和实例化。如下图:可以简单理解②③④为初始化,⑤实例化
类创建过程

6.类的初始化

类的主动引用
类的主动引用,一定会发生类的初始化,主要分为以下几种情况:

1.当虚拟机,先初始化main方法所在的类
2.new一个类的对象
3.调用类的静态成员(除了final常量)和静态方法
4.使用java.lang.reflect包的方法,对类进行反射调用
5.初始化一个类,如果其父类没有初始化,则先初始化其父类

类的被动引用
类的被动引用,不会发生类的初始化,有以下几种情况:

1.当访问一个静态域,只有真正声明这个域的类才会被初始化。
如:通过子类调用父类的静态变量,只有父类会被初始化,子类不会初始化
2.通过数组定义类引用,不会触发此类的初始化
3.引用常量不会触发此类的初始化(常量在链接阶段,就存入该类的常量池中了)

7.类加载器的作用

类加载的作用: 将class文件字节码内容加载到内存,并将这些静态变量转换成方法区的运行时数据结构,然后在堆中生成相应的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存: 标准的javaSE类加载器可以按照要求查找类。但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过jvm垃圾回收机制,可以回收这些 Class 对象。
类加载过程
类加载器的作用,是把类加载到内存。JVM定义了以下几种类加载器:
类加载器的种类
1.引导类加载器,又称为根加载器(bootstrap class loader)。它用来加载 Java 的核心类,是由C++实现,并不继承自 java.lang.ClassLoader(负责加载jre/lib/rt.jar里所有的class)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到类加载器的引用。
2.扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
3.系统类加载器(system class loader):(也称为应用类加载器)。它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为扩展类加载器(ExtClassLoader)。

类加载器加载Class大致要经过如下8个步骤:

  1. 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
  2. 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
  3. 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
  4. 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
  5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
  6. 从文件中载入Class,成功后跳至第8步。
  7. 抛出ClassNotFountException异常。
  8. 返回对应的java.lang.Class对象。

JVM的类加载机制主要有三种:

  • 全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
  • 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为什么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

双亲委派机制:

  • 双亲委派机制的工作原理是:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。
  • 双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
//1. 获得 系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);  //sun.misc.Launcher$AppClassLoader@b4aac2
//2. 获得系统类加载器的父加载器-->扩展类加载器
ClassLoader extensionClassLoader = systemClassLoader.getParent();
System.out.println(extensionClassLoader);   //sun.misc.Launcher$ExtClassLoader@154617c
//3. 获得扩展类加载器的父加载器-->跟加载器(C++编写,获取不到)
ClassLoader bootstrapClassLoader = extensionClassLoader.getParent();
System.out.println(bootstrapClassLoader);   //null

//测试当前类,是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.xinan.reflection.MyClassLoader01").getClassLoader();
System.out.println(classLoader);   //sun.misc.Launcher$AppClassLoader@b4aac2 (系统类加载器)

//测试JDK内置核心类,是哪个加载器加载
ClassLoader classLoader1 = Object.class.getClassLoader();
System.out.println(classLoader1);   //null(根加载器)

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

8.获取类的运行时结构

通过反射,获取运行时类的完整结构

Field、Method、Constructor、SuperClass、Interface、Annotation

// --------------- 被反射的类:User ---------------
public class User {
    public int id;
    private String name;
    int age;
    //get/set方法
    public void setId(int id) { this.id = id; }
    public int getId() { return id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    //构造方法
    public User() {}
    private User(String name) { this.name = name; }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    //私有方法
    private void printLn(){
        System.out.println("这是私有打印方法");
    }
    //公共方法
    public void printPub(){
        System.out.println("这是公共方法");
    }
    @Override
    public String toString() {
        return "id: "+id+" name: "+name + " age: "+age;
    }
}
// --------------- 利用反射,操作上面的User类  ---------------
public class MyReflection02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class userClass = Class.forName("com.xinan.reflection.User");
        // ------------ 获取类的名字 ------------
        System.out.println(userClass.getName());    //包路径+类名:com.xinan.reflection.User
        System.out.println(userClass.getSimpleName());  //类名:User
        // ------------ 类的属性 ------------
        Field id = userClass.getField("id");  //根据名称,获取指定属性(只能是public属性)
        Field[] publicField = userClass.getFields();    //获取共有属性
        Field[] declaredFields = userClass.getDeclaredFields(); //获取所有属性
        // ------------ 获取类的方法 ------------
        Method[] methods = userClass.getMethods();      //获得公共方法,包含父类
        Method[] declaredMethods = userClass.getDeclaredMethods();//获取本类所有方法,包含私有,不包括父类
        Method getName = userClass.getMethod("setName", String.class);  //根据方法名、参数列表,获取指定方法
        System.out.println(getName);
        // ------------ 获取类构造器 ------------
        Constructor[] constructors = userClass.getConstructors();   //获取public构造方法
        Constructor[] declaredConstructors = userClass.getDeclaredConstructors();   //获取所有构造方法
        Constructor declaredConstructor = userClass.getDeclaredConstructor(String.class);   //获取指定构造方法
    }
}

9.动态创建对象&执行方法

通过反射创建对象

  • 调用newInstance()方法:必须有无参构造器,且访问权限足够
  • 通过构造器创建对对象:
    1.通过getDecalredContructor获取构造器;
    2.设置可见性;
    3.调用构造器newInstance方法,并传递对应参数

调用指定方法

  • 通过Class对象的getDeclaredMethod(String name,Class …parameterTypes)获取指定方法,其中name是方法名,parameterTypes是参数列表
  • 设置可见性
  • 通过Object invoke(Object obj,Object[] args) 调用该方法:其中obj是要操作的对象,args是传入的参数信息。

Object invoke(Object obj , Object …args)

  1. Object 对应原方法的返回值,若原方法没有返回值,此时返回null
  2. 若原方法为静态方法,则形参 obj 可为 null
  3. 若原方法形参列表为空,则 args 为 null
  4. 若原方法为private修饰,则调用invoke之前,显式调用该方法对象的setAccessible(true),就可调用该方法

setAccessible

  1. Method、Field、Constructor对象,都有setAccessible方法
  2. setAccessible的作用是,启动 & 进制访问安全检查的开关
  3. 参数值为true,指反射的对象在使用时,取消java语言访问检查。
    提高反射效率。如果必须用反射,且该方法或属性被频繁调用,则设置为true
    可以访问私有属性或方法
  4. 参数值为false,指反射的对象应进行java语言访问检查
public static void main(String[] args) throws Exception {
    Class userClass = Class.forName("com.xinan.reflection.User");
    // ------------- 通过 newInstance() 创建对象 -------------
    User user1 = (User)userClass.newInstance();  //本质是调用了无参构造器

    // ------------- 通过构造器创建对象 -------------
    Constructor constructor = userClass.getDeclaredConstructor(String.class);//获取构造器
    constructor.setAccessible(true);    //如果构造器为私有,则设置可见性为true
    User user2 = (User)constructor.newInstance("xiaoming");

    // ------------- 通过反射调用普通方法 -------------
    User user3 = (User)userClass.newInstance();
    Method setName = userClass.getDeclaredMethod("setName", String.class);  //获得方法
    setName.setAccessible(true);                //设置方法可见性
    setName.invoke(user3,"你好啊");     //通过invoke() 调用指定对象的该方法(invoke:调用)

    // ------------- 通过反射操作属性 -------------
    User user4 = (User)userClass.newInstance();
    Field name = userClass.getDeclaredField("name");    //获取属性
    name.setAccessible(true);       //如果是私有属性,设置可见性
    name.set(user4,"第四个");       //给指定对象的该属性赋值
}

10.获取泛型信息

  • java通过泛型擦除机制来引入泛型(为了兼容以前的代码)。java中的泛型仅仅是给编译器 javac 用的,确保数据安全性和免去强制类型转换问题。一旦便已完成,所有和泛型有关的类型信息全部擦除。
  • 为了通过反射操作这些类型,java新增了以下几种类型,来代表不能被归一到Class类中、但又和原始类型齐名的类型。

ParameterizedType:表示一种参数化类型,比如Collection<String>
GenericArrayType:表示元素类型是参数化类型 或 类型变量是数组类型
TypeVariable:各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式

//通过反射获取泛型信息
public class MyReflection04 {
    //第一个方法
    public static void method01(Map<String,User> map, List<String> list){
        System.out.println("方法:method01");
    }
    //第二个方法
    public static Map<String,User> method02(){
        System.out.println("方法:method02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Class myReflection04Class = MyReflection04.class;
        //获取method01方法
        Method test01 = myReflection04Class.getMethod("method01", Map.class, List.class);
        //获取 泛型参数类型
        Type[] genericParameterTypes = test01.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("参数信息:   "+genericParameterType);
        }
        //获取参数中的泛型信息
        for (Type genericParameterType : genericParameterTypes) {
            //判断 泛型参数类型 是否为 结构化参数类型
            if(genericParameterType instanceof ParameterizedType){
                //强转为 参数化泛型
                ParameterizedType parameterType = (ParameterizedType) genericParameterType;
                //获取真实参数类型
                Type[] actualTypeArguments = parameterType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("method01真实参数类型: "+actualTypeArgument);
                }
            }
        }
        //获取method02方法
        Method method02 = myReflection04Class.getMethod("method02");
        //获取返回值类型
        Type genericReturnType = method02.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
            //强转为 参数化泛型
            ParameterizedType parameterType = (ParameterizedType) genericReturnType;
            //获取真实参数类型
            Type[] actualTypeArguments = parameterType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("method02真实参数类型: "+actualTypeArgument);
            }
        }
    }
}
输出结果如下:
参数信息:   java.util.Map<java.lang.String, com.tl.demo.reflection.User>
参数信息:   java.util.List<java.lang.String>

method01真实参数类型: class java.lang.String
method01真实参数类型: class com.tl.demo.reflection.User
method01真实参数类型: class java.lang.String

method02真实参数类型: class java.lang.String
method02真实参数类型: class com.tl.demo.reflection.User

11.获取注解信息

ORM:(Object Relationship Mapping)对象关系映射
类 <----> 表   属性<----> 字段   对象 <----> 记录

public class MyReflection05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class stuClass = Class.forName("com.tl.demo.reflection.Student");
        // -------------- 通过反射获得注解 --------------
        Annotation[] annotations = stuClass.getAnnotations();
        for (Annotation annotation : annotations) {
        	//输出:  @com.tl.demo.reflection.ClassAnnotation(value=t_student)
            System.out.println(annotation);
        }

        // -------------- 获得注解中的value值 --------------
        //根据注解名称,获得指定注解
        ClassAnnotation classAnnotation = (ClassAnnotation)stuClass.getAnnotation(ClassAnnotation.class);
        String value = classAnnotation.value();
        System.out.println(value);      //输出    t_student

        // -------------- 获得类属性上的注解 --------------
        Field nameField = stuClass.getDeclaredField("id");
        FieldAnnotation fieldAnnotation  = nameField.getAnnotation(FieldAnnotation.class);
        System.out.println(fieldAnnotation.columnName());   //输出    id
        System.out.println(fieldAnnotation.type());         //输出    int
        System.out.println(fieldAnnotation.length());       //输出    11
    }
}

//定义学生类,并添加注解
@ClassAnnotation("t_student")
class Student{
    @FieldAnnotation(columnName = "id",type="int",length = 11)
    private int id;
    @FieldAnnotation(columnName = "name",type="varchar",length = 3)
    private String name;
}

//类的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnnotation{
    String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    String columnName();    //列名
    String type();      //数据类型
    int length();       //长度
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值