Java 注解和反射详解

这是一个学习的过程,学一点就少一点,懂的越多,不懂的也越多。

注解

什么是注解

  Annotation是从JDK5.0开始引入的新技术。

  • 作用:
    1. 不是程序本身,可以对程序作出解释。可以被其它程序读取,例如:编译器;
    2. 对代码进行规范和约束。
  • 格式:“@注解名”在代码中存在,还可以添加写参数值,例如:@SuppressWarnings(value=“unchecked”)。
  • 使用位置:可以附加在class、method、field等上面,可以通过反射机制编程实现对这些元数据的访问。

内置注解

  • @Override:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算
    重写超类中的另一个方法声明;
  • @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞方法、属性、类,被标识方法说明弃用,已有更好的代替;(新版本已弃用)
  • @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。与前两个注释有所不同,你需要添加一个参数才能正确使用。这些参数都是已经定义好了的,我们选择性的使用就好了。例如:
    1. @SuppressWarnings(“all”);
    2. @SuppressWarnings(“unchecked”);
    3. @SuppressWarnings(value={“unchecked”,“deprecation”})
名称作用
deprecation过时的类或方法警告
unchecked执行了未检查的转换时警告
allthrough当Switch程序块直接通往下一种情况而没有Break时的警告。
path在类路径、源文件路径等中有不存在的路径时的警告。
serial当在可序列化的类上缺少serialVersionUID定义时的警告。
finally任何finally子句不能完成时的警告。
all关于以上所有情况的警告

元注解

  作用:就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,它们被用来提供对其他annotation类型作说明。这些类型和它们所支持的类在java.lang annotation包中可以找到。

名称作用
@Target(开发常用)用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention(开发常用)描述注解被保留的阶段(注解的生命周期)
(SOURCE < CLASS < RUNTIME(开发常用))
@Document说明该注解将被包含在javadoc中
@Inherited说明子类可以继承父类中的该注解

@Target 注解的常用值说明:

名称作用
ElementType.TYPE作用接口、类、枚举、注解
ElementType.FIELD作用属性字段、枚举的常量
ElementType.METHOD作用方法
ElementType.PARAMETER作用方法参数
ElementType.CONSTRUCTOR作用构造函数
ElementType.LOCAL_VARIABLE作用局部变量
ElementType.ANNOTATION_TYPE作用于注解(@Retention注解中就使用该属性)
ElementType.PACKAGE作用于包
ElementType.TYPE_PARAMETER作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)

@Retention 注解的常用值说明:

名称作用
RetentionPolicy.SOURCE仅存在于源码中
RetentionPolicy.CLASS默认的策略,在class字节码文件中存在,但运行时无法获得
RetentionPolicy.RUNTIME在运行时可以通过反射获取到,也是最常用的。

自定义注解

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

 作用:@ interface 用来声明一个注解。

  • 注意:
    1. 其中的每一个方法实际上是声明了一个配置参数;
    2. 方法的名称就是参数的名称;
    3. 返回值类型就是参数的类型(返回值只能是基本类型、ClassStringenum );
    4. 可以通过default来声明参数的默认值;
    5. 如果只有一个参数成员,一般参数名为value;
    6. 注解元素必须要有值,我们定义注解元素时, 经常使用空字符串,0作为默认值。
public class CustomAnnotation {
    // 注解可以显示赋值,如果没有默认值就必须给注解赋值
    @MyAnnotation(age = 20)
    public void test1(){}

    @MyAnnotation2("value")
    public void test2(){}

}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    // 注解的参数格式: 参数类型 + 参数名()
    String name() default "";

    int age();

    int id() default -1; // 如果默认值为-1,代表不存在

    String[] schools() default {"清华大学", "北京大学"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{

    // 只有当参数名称是value时,才可省略不写
    String value();
}

反射

反射机制概述

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

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

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

缺点:对性能有影响。(使用反射基本上是一种解释操作,相当于告诉JVM希望它做什么)。

理解Class类并获取Class实例

聊聊Class

在Object类中定义了 getClass() 方法:

public class Object {
 // ...
 	/*
 	* 此方法返回值类型是一个Class类,此类是Java反射源头,
 	*/
	public final native Class<?> getClass();
 // ...
  • Class本身也是一个类;
  • Class对象只能由系统建立对象;
  • 一个加载的类在JVM中只会有一个Class对象;
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class中的常用方法

方法名功能说明
static ClassforName(String name)返回指定类名name的Class对象
Object newInstance()调用缺省构造函数,返回Class对象的一个实例
getName()返回此Class对象所表示的实体(类,接口,数组类或void)的名称。
Class getSuperClass()返回当前Class对象的父类的Class对象
Class[] getinterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Method getMothed(String name,Class… T)返回一个Method对象,此对象的形参类型为paramType
Field[] getDeclaredFields()返回Field对象的一个数组

获取Class类的实例

方式一

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

Class clazz = Person.class;

方式二
 已知某个类的实例, 调用该实例的getClass()方法获取Class对象。

Class clazz = person.getClass();

方式三

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

Class clazz = Class.forName("demo01 .Student");

方式四

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

Class clazz = Class.forName("demo01 .Student");

方式五

 还可以利用ClassLoader我们之 后讲解

代码示例:

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());

        // 方式二 Class.forName获取
        Class c2 = Class.forName("cn.zhuzicc.reflect.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 {

    public String name;

    public String getName() {
        return name;
    }

    public void setName(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 = "老师";
    }
}
运行结果:
身份:学生
356573597
356573597
356573597
int
class cn.zhuzicc.reflect.Person

哪些类型可以有Class对象?

  • class:外部类、成员(成员内部类,静态内部类)、局部内部类、匿名内部类;
  • interface;
  • 数组;
  • 枚举;
  • annotation;
  • 基本数据类型;
  • void;
        Class<Object> c1 = Object.class;			 // 类
        Class<Comparable> c2 = Comparable.class;     // 接口
        Class<String[]> c3 = String[].class;         // 一维数组
        Class<int[][]> c4 = int[][].class;        	 // 二维数组
        Class<Override> c5 = Override.class;         // 注解
        Class<ElementType> c6 = ElementType.class;   // 枚举
        Class<Integer> c7 = Integer.class;           // 基本数据类型包装类   
        Class<Void> c8 = void.class;  				 // void
        Class<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);
        System.out.println("\n数组长度不相同,是否是同一个class对象?");

        // 数组长度不相同,是否是同一个class对象
        int[] arr1 = new int[10];
        int[] arr2 = new int[100];

        System.out.println(arr1.getClass().hashCode());
        System.out.println(arr2.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

数组长度不相同,是否是同一个class对象?
356573597
356573597

类的加载与ClassLoader

Java内存分析

在这里插入图片描述

  • 类的加载过程:
    1. 将类的class文件读入内存中,并创建一个java.lang.Class对象。此过程由类加载器完成;
    2. 将类的二进制数据合并到JRE中;
    3. JVM负责类的初始化。

类的加载与ClassLoader的理解

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

什么时候会发生类初始化

  • 类的主动引用(一定会发生类的初始化)
    1. 当虚拟机启动,先初始化main方法所在的类;
    2. new一个类的对象;
    3. 调用类的静态成员(除了final常量)和静态方法;
    4. 使用java.lang.reflect包的方法对类进行反射调用;
    5. 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
  • 类的被动引用(不会发生类的初始化)
    1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
    2. 通过数组定义类引用,不会触发此类的初始化
    3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。

类加载器的作用(classload)

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

  类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
在这里插入图片描述

JVM规范定义了几种类加载器类型

  • 引导类加载器:用C++编写的, 是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取;
  • 扩展类加载器:负责jre/lib/ext目录下的jar包或- D java.ext.dirs指定目录下的jar包装入工作库;
  • 系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器;
    在这里插入图片描述

获取类加载器代码示例:

public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException {
		// 获取系统的类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.print("获取系统的类加载器:");
        System.out.println(systemClassLoader);

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

        // 获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供)
        ClassLoader parent1 = parent.getParent();
        System.out.print("获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供):");
        System.out.println(parent1);

        // 测试当前类是哪个类加载器加载的
        ClassLoader classLoader = Class.forName("cn.zhuzicc.reflect.Test07").getClassLoader();
        System.out.print("测试当前类是哪个类加载器加载的:");
        System.out.println(classLoader);

        // 测试JDK内置类是哪个类加载器加载的
        ClassLoader innerClass = Class.forName("java.lang.Object").getClassLoader();
        System.out.print("测试当前类是哪个类加载器加载的:");
        System.out.println(innerClass);

        // 如何获取系统类加载器可以加载的路径
        String[] split = System.getProperty("java.class.path").split(";");
        System.out.println(split.length);
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
 	}
 }
运行结果:
获取系统的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
获取系统的类加载器的父类加载器 -> 扩展类加载器:sun.misc.Launcher$ExtClassLoader@1540e19d
获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供):null
测试当前类是哪个类加载器加载的:sun.misc.Launcher$AppClassLoader@18b4aac2
测试当前类是哪个类加载器加载的:null
25
E:\develop\jdk\jdk1.8\jre\lib\charsets.jar
E:\develop\jdk\jdk1.8\jre\lib\deploy.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\access-bridge-64.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\cldrdata.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\dnsns.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\jaccess.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\jfxrt.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\localedata.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\nashorn.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunec.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunjce_provider.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunmscapi.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunpkcs11.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\zipfs.jar
E:\develop\jdk\jdk1.8\jre\lib\javaws.jar
E:\develop\jdk\jdk1.8\jre\lib\jce.jar
E:\develop\jdk\jdk1.8\jre\lib\jfr.jar
E:\develop\jdk\jdk1.8\jre\lib\jfxswt.jar
E:\develop\jdk\jdk1.8\jre\lib\jsse.jar
E:\develop\jdk\jdk1.8\jre\lib\management-agent.jar
E:\develop\jdk\jdk1.8\jre\lib\plugin.jar
E:\develop\jdk\jdk1.8\jre\lib\resources.jar
E:\develop\jdk\jdk1.8\jre\lib\rt.jar
D:\ideaprojects\java\annotation&reflect\classes\production\java
E:\develop\JetBrains\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar

创建运行时类的对象

 创建类的对象:调用 Class 对象的 newInstance() 方法。

  • 注意:目标类必须有一个无参构造器;(不然会报错:java.lang.NoSuchMethodException

思考:如果没有无参构造器就不能创建对象了吗?

  没有无参构造器也可以创建,只要在操作的时候明确调用类中的哪个构造器,并将参数传递进去以后,也可进行实例化。

  • 操作步骤:
    1. 通过 Class 类的 getDeclaredConstructor(Class<?>... parameterTypes) 方法获取到指定形参类型的构造器;
    2. 向构造器的形参中传递一个对象数组进去,包含该构造器中所需的所有参数;
    3. 通过 Constructor 实例化对象。

代码示例:

public class Test09 {
  public static void main(String[] args) throws Exception {
		Class c1 = Class.forName("cn.zhuzicc.reflect.User");

        // 1. 通过反射构造一个对象
        System.out.println("1. 通过反射构造一个对象");
        // 1.1 本质是调用了类的无参构造器
        User user = (User) c1.newInstance();
        System.out.println("1.1 本质是调用了类的无参构造器:\n" + user);
        // 1.2 通过类的有参构造器创建对象
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, Integer.class, LocalDate.class);
        user = (User) declaredConstructor.newInstance("张三", 21, LocalDate.of(2000, 3, 8));
        System.out.println("1.2 通过类的有参构造器创建对象:\n" + user);
        System.out.println("\n");


        // 2. 通过反射操作方法
        System.out.println("2. 通过反射操作方法");
        // 2.1 通过反射调用普通方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        user = (User) c1.newInstance();
        setName.invoke(user, "李四");
        System.out.println("2.1 通过反射调用普通方法:\n" + user.getName());
        // 2.1 通过反射调用私有方法
        System.out.println("2.1 通过反射调用私有方法:");
        Method thisSecret = c1.getDeclaredMethod("thisSecret", Boolean.class);
        thisSecret.setAccessible(true);
        thisSecret.invoke(user, true);
        System.out.println("\n");

        // 3. 通过反射操作属性
        System.out.println("3. 通过反射操作属性");
        Field name = c1.getDeclaredField("name");
        user = (User) c1.newInstance();
        name.setAccessible(true);
        name.set(user, "王五");
        System.out.println(user.getName());
 }      
运行结果:
1. 通过反射构造一个对象
1.1 本质是调用了类的无参构造器:
User{name='null', age=null, birthday=null}
1.2 通过类的有参构造器创建对象:
User{name='张三', age=21, birthday=2000-03-08}

2. 通过反射操作方法
2.1 通过反射调用普通方法:
李四
2.1 通过反射调用私有方法:
你是我的朋友,你可以知道这个秘密

3. 通过反射操作属性
王五

setAccessible

  • Method和Field、Constructor对象都有setAccessible()方法。setAccessible作用是启动和禁用访问安全检查的开关。

  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

  • 提高反射的效率。

    1. 如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true;
    2. 使得原本无法访问的私有成员也可以访问。
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

setAccessible 效率测试:

public class Test10 {

    public static void main(String[] args) throws Exception {
        test01();
        test02();
        test03();
    }

	/**
     * 普通方式执行
     */
    public static void test01(){
        User user = new User();

        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();

        System.out.println("执行10亿个数,普通方式执行:" + (endTime - beginTime) + "ms");
    }
    /**
     * 反射方式执行, 关闭安全检测
     */
    public static void test02() throws Exception {
        User user = new User();
        Class c1 = user.getClass();

        Method getName = c1.getMethod("getName", null);

        long beginTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("执行10亿个数,反射方式执行, 开启安全检测:" + (endTime - beginTime) + "ms");
    }

    /**
     * 反射方式执行, 开启安全检测
     */
    public static void test03() throws Exception {
        User user = new User();
        Class c1 = user.getClass();

        Method getName = c1.getMethod("getName", null);

        long beginTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.setAccessible(true);
            getName.invoke(user, null);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("执行10亿个数,反射方式执行, 关闭安全检测:" + (endTime - beginTime) + "ms");
    }
}
运行结果:
执行10亿个数,普通方式执行:8ms
执行10亿个数,反射方式执行, 开启安全检测:6448ms
执行10亿个数,反射方式执行, 关闭安全检测:1877ms

反射获取泛型

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

  为了通过反射操作这些类型,Java新增了 ParameterizedType , GenericArrayType , TypeVariableWildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

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

代码示例:

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

        // 1. 获取泛型信息
        Class<Test11> c1 = Test11.class;
        Method method1 = c1.getMethod("test01", Map.class, List.class);

        // 1.1 获取方法参数泛型
        Type[] genericParameterTypes = method1.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("1.1 获取方法参数泛型:\n" + genericParameterType);
            // 1.1.1 获取真实参数类型
            if (genericParameterType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("1.1.1 获取真实参数类型:\n" + actualTypeArgument);
                }
            }
        }

        System.out.println("\n");
        Method method2 = c1.getMethod("test02");
        // 1.2 获取方法返回值泛型
        Type genericReturnType = method2.getGenericReturnType();
        System.out.println("1.2 获取方法返回值泛型:\n" + genericReturnType);
        if (genericReturnType instanceof ParameterizedType) {
            // 1.2.1 获取真实参数类型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("1.2.1 获取真实参数类型:\n" + actualTypeArgument);
            }
        }
    }
}
    public void test01(Map<String, User> map, List<User> list) throws Exception{
        System.out.println("test01");
    }

    public Map<String, User> test02() {
        System.out.println("test02");
        return null;
    }
运行结果:
1.1 获取方法参数泛型:
java.util.Map<java.lang.String, cn.zhuzicc.reflect.User>
1.1.1 获取真实参数类型:
class java.lang.String
1.1.1 获取真实参数类型:
class cn.zhuzicc.reflect.User
1.1 获取方法参数泛型:
java.util.List<cn.zhuzicc.reflect.User>
1.1.1 获取真实参数类型:
class cn.zhuzicc.reflect.User


1.2 获取方法返回值泛型:
java.util.Map<java.lang.String, cn.zhuzicc.reflect.User>
1.2.1 获取真实参数类型:
class java.lang.String
1.2.1 获取真实参数类型:
class cn.zhuzicc.reflect.User

获取运行时类的完整结构

  通过反射获取运行时类的完整结构包括:Field、Method、Constructor、SuperClass、Interface、Annotation。
代码示例:

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

        Class c1 = Class.forName("cn.zhuzicc.reflect.User");

        // 1. 获取类名
        // 1.1 获取类的全限定类名
        System.out.println(c1.getName());
        // 1.2 获取类的名称
        System.out.println(c1.getSimpleName()+"\n");

        // 2.获取类的属性
        // 2.1 获取类public修饰的属性
        Field[] fields = c1.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("\n");
        // 2.2 获取类的所有属性
        Field[] declaredFields = c1.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }
        System.out.println("\n");

        // 2.3 获取类的指定属性
        Field name = c1.getField("name");
        System.out.println("public修饰:" + name);
        Field age = c1.getDeclaredField("age");
        System.out.println("private修饰:" + age);
        System.out.println("\n");

        // 3. 获取类的方法
        System.out.println("3. 获取类的方法");
        // 3.1 获取本类及其父类的所有public方法
        System.out.println("3.1 获取本类及其父类的所有public方法:");
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        // 3.2 获取本类的所有方法
        System.out.println("3.2 获取本类的所有方法:");
        Method[] declaredMethods = c1.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        // 3.3 获取本类指定名称的方法
        System.out.println("3.3 获取本类指定名称的方法:");
        Method getName = c1.getMethod("getName");
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        // 4. 获取构造器
        System.out.println("4. 获取构造器");
        // 4.1 获取public修饰的构造器
        System.out.println("4.1 获取所有构造器:");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        // 4.2 获取所有构造器
        System.out.println("4.2 获取所有构造器:");
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        // 4.3 获取指定构造器
        System.out.println("4.3 获取指定构造器:");
        Constructor constructor = c1.getConstructor(String.class, Integer.class);
        System.out.println(constructor);
    }
}
class User{

    public String name;

    private Integer age;

    private LocalDate birthday;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

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

    public User(String name, Integer age, LocalDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
    
    private void thisSecret(Boolean flag){
        if (flag) {
            System.out.println("你是我的朋友,你可以知道这个秘密");
        } else {
            System.out.println("这是一个不为人知的秘密");
        }
    }
// 省略 getter/setter...
// 省略 toString...
}
运行结果展示:
cn.zhuzicc.reflect.User
User

public java.lang.String cn.zhuzicc.reflect.User.name


public java.lang.String cn.zhuzicc.reflect.User.name
private java.lang.Integer cn.zhuzicc.reflect.User.age
private java.time.LocalDate cn.zhuzicc.reflect.User.birthday


public修饰:public java.lang.String cn.zhuzicc.reflect.User.name
private修饰:private java.lang.Integer cn.zhuzicc.reflect.User.age


3. 获取类的方法
3.1 获取本类及其父类的所有public方法:
public java.lang.String cn.zhuzicc.reflect.User.toString()
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
public java.lang.Integer cn.zhuzicc.reflect.User.getAge()
public void cn.zhuzicc.reflect.User.setAge(java.lang.Integer)
public void cn.zhuzicc.reflect.User.setBirthday(java.time.LocalDate)
public java.time.LocalDate cn.zhuzicc.reflect.User.getBirthday()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) 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()
3.2 获取本类的所有方法:
public java.lang.String cn.zhuzicc.reflect.User.toString()
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
public java.lang.Integer cn.zhuzicc.reflect.User.getAge()
public void cn.zhuzicc.reflect.User.setAge(java.lang.Integer)
public void cn.zhuzicc.reflect.User.setBirthday(java.time.LocalDate)
public java.time.LocalDate cn.zhuzicc.reflect.User.getBirthday()
private void cn.zhuzicc.reflect.User.thisSecret(java.lang.Boolean)
3.3 获取本类指定名称的方法:
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
4. 获取构造器
4.1 获取所有构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer,java.time.LocalDate)
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)
public cn.zhuzicc.reflect.User(java.lang.String)
public cn.zhuzicc.reflect.User()
4.2 获取所有构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer,java.time.LocalDate)
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)
public cn.zhuzicc.reflect.User(java.lang.String)
public cn.zhuzicc.reflect.User()
4.3 获取指定构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)

反射操作注解

代码示例:

public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("cn.zhuzicc.reflect.Person1");

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

        // 2. 获取注解中的属性值
        TableName annotation = (TableName) c1.getAnnotation(TableName.class);
        String value = annotation.value();
        System.out.println("2. 获取注解中的属性值:\n" + value);

        // 3. 获取字段注解
        Field[] declaredFields = c1.getDeclaredFields();
        System.out.println("3. 获取字段注解:");
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            ColunmName colunmName = declaredField.getAnnotation(ColunmName.class);
            System.out.print(colunmName.name() + ",");
            System.out.print(colunmName.length() + ",");
            System.out.print(colunmName.type());
            System.out.println("\n");
        }
    }
}


@TableName("person_info")
class Person1 {
    @ColunmName(name = "db_name", type = "varchar", length = 5)
    private String name;

    @ColunmName(name = "db_age", type = "int", length = 3)
    private int age;

    @ColunmName(name = "db_stature", type = "int", length = 3)
    private int stature;

    @ColunmName(name = "db_weight", type = "int", length = 3)
    private int weight;

    public Person1() {
    }

    public Person1(String name, int age, int stature, int weight) {
        this.name = name;
        this.age = age;
        this.stature = stature;
        this.weight = weight;
    }
// 省略 getter/setter...
// 省略 toString...
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface ColunmName {
    String name();

    String type();

    int length();
}
运行结果:
1. 通过反射获取注解:
@cn.zhuzicc.reflect.TableName(value=person_info)
2. 获取注解中的属性值:
person_info
3. 获取字段注解:
db_name,5,varchar

db_age,3,int

db_stature,3,int

db_weight,3,int
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhuzicc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值