Java 注解和反射


一、注解

Annotation 是从 JDK 1.5 开始引入的新技术(@注释名)。

  1. 可以对程序作出解释(程序的注释comment)。
  2. 可以被其他程序读取。
  3. 可以通过反射机制编程实现对这些元数据的访问。

1. 内置注解

  • java.lang 包
  1. @Deprecated
    此注解可以用于修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它已弃用或者存在更好的选择。
  2. @FunctionalInterface:(JDK 1.8)
    此注解用于修饰 匿名函数 或 函数式接口
  3. @Override
    此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
  4. @SafeVarargs:(JDK 1.7)
    用来抑制编译时参数为泛型的方法或构造函数产生的警告。
  5. @SuppressWarnings(value = {...})alluncheckeddeprecationserial
    用来抑制编译时的警告。
  • java.lang.annotation 包
  1. @Native
    用于描述这个变量可以被本地代码引用,常被代码生成工具使用。
// 包含在 javadoc 中
@Documented
// 字段
@Target(ElementType.FIELD)
// 源码有效
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}

2. 元注解(ANNOTATION_TYPE元注解类型)

负责注释自定义注解,Java5 定义了四个标准的 meta-annotation 类型,用来对自定义 Annotation 类型做说明。

  • JDK 1.5
  1. @Documented
    用于判断是否生成到 javadoc 中。
  2. @Inherited
    用于描述注解是否具备继承传递性(不是注解与注解继承,是被注解修饰的父类,子类可以继承到父类修饰的注解)子类可以继承到父类中修饰的注解。
  3. @Retention
    用于描述注解的生命周期(SOURCE源码 < CLASS字节码 < RUNTIME运行时)。
  1. SOURCE:只保留在源文件,当 .java 文件编译成 .class文件时,注解被遗弃。
  2. CLASS:保留到 .class 文件,但 JVM 加载 .class 文件时,注解被遗弃。
  3. RUNTIME:保留在1、2中,并且运行时依然有效。
  1. @Target
    用于描述注解的使用范围(即:注解可以用在什么地方)。
  1. TYPE:类、接口(包括注解类型)、枚举。
  2. FIELD:字段。
  3. METHOD:方法。
  4. PARAMETER:参数。
  5. CONSTRUCTOR:构造。
  6. LOCAL_VARIABLE:局部变量。
  7. ANNOTATION_TYPE:注解类型。
  8. PACKAGE:包。
  9. TYPE_PARAMETER:参数类型(形式参数类型)(JDK 1.8)。
  10. TYPE_USE: 任何位置都可以(JDK 1.8)。
  • JDK 1.8
  1. @Repeatable
    可重复注解,在一个声明上使用同一个注解多次时(Java 8 版本以前,同一个元素上最多只能有一个相同类型的注解,如果需要,则必须使用 注解容器)。
// 包含在 javadoc 中
@Documented
// 运行时有效
@Retention(RetentionPolicy.RUNTIME)
// 元注解类型
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

2.1 Java8 之前重复注解
public @interface Roles {
    Role[] roles();
}

public @interface Role {
    String roleName();
}

public class RoleTest {

    @Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
    public String doString(){
        return "Java8 之前重复注解";
    }
}

2.2 Java8 之后重复注解

Java8 之后重复注解,是一种简化写法。

public @interface Roles {
    Role[] value();
}

// 指向存储注解 Roles
@Repeatable(Roles.class)
public @interface Role {
    String roleName();
}

public class RoleTest {

    @Role(roleName = "role1")
    @Role(roleName = "role2")
    public String doString(){
        return "Java8 之后重复注解";
    }
}

3. 自定义注解

自定义注解,默认自动继承了 java.lang.annotation.Annotation 接口。


3.1 @MyAnnotation、@QsAnnotation
import java.lang.annotation.*;

/**
 * @author: wy
 * describe: 3. 自定义注解
 */
// 生成到 javadoc 中
@Documented
// 具备继承传递性
@Inherited
// 运行时有效
@Retention(value = RetentionPolicy.RUNTIME)
// 类、方法、属性
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface MyAnnotation {

    /**
     * 注解参数格式:'参数类型' '参数名'() default '参数默认值';
     * 1. 无默认值时,必须写参数
     * 2. 有默认值时,可以不写参数
     */
    String value() default "my";

    String[] values() default {"张三", "李四"};

    int count() default 0;
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
public @interface QsAnnotation {

    String value() default "qs";
}

3.2 @Roles、@Role

重复注解

import java.lang.annotation.*;

/**
 * @author: wy
 * describe: 重复注解
 */
@Repeatable(Roles.class)
public @interface Role {

    String value();
}

@Inherited
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
@interface Roles {
    Role[] value();
}

3.3 MyAnnotationMain、MyAnnotationTest
  1. boolean isAnnotation():当前元素是否是注解类型。
  2. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):当前元素及其父元素上的是否存在指定注解(父元素上的注解需要具备继承传递性)。
  3. Annotation[] getAnnotations():获取当前元素及其父元素上的所有注解(父元素上的注解需要具备继承传递性)。
  4. Annotation[] getDeclaredAnnotations():获取当前元素上的所有注解。
  5. <A extends Annotation> A getAnnotation(Class<A> annotationClass):获取当前元素及其父元素上的指定注解(父元素上的注解需要具备继承传递性)。
  6. <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):获取当前元素上的指定注解。
  7. <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass):获取当前元素及其父元素上的指定重复注解(父元素上的注解需要具备继承传递性)。
  8. <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass):获取当前元素上的指定重复注解.
/**
 * @author: wy
 * describe: 使用注解
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@MyAnnotation
public class MyAnnotationMain implements Serializable {

    private String name;

    public static void main(String[] args) throws NoSuchFieldException {
        Class<? extends MyAnnotationTest> clazz = MyAnnotationTest.class;

        // 1. 当前元素是否是注解类型
        System.out.println(clazz.isAnnotation());
        // false
        System.out.println(QsAnnotation.class.isAnnotation());
        // true
        // 2. 获取当前元素及其父元素上的是否存在指定注解
        System.out.println(clazz.isAnnotationPresent(MyAnnotation.class));
        // true(因为`MyAnnotation`注解被`Inherited`修饰,具备继承传递性)
        System.out.println(clazz.isAnnotationPresent(QsAnnotation.class));
        // true

        System.out.println("--------------------");
        // 3. 获取当前元素及其父元素上的所有注解(父元素上的注解需要具备继承传递性)
        System.out.println("getAnnotations");
        Annotation[] annotations = clazz.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);
        /*
        @com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
        @com.qs.javareflect.annotation.QsAnnotation(value=qs)
        @com.qs.javareflect.annotation.Roles(value=[@com.qs.javareflect.annotation.Role(value=DEV), @com.qs.javareflect.annotation.Role(value=TEST)])
         */

        // 获取当前元素上的所有注解
        System.out.println("getDeclaredAnnotations");
        Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
        Arrays.stream(declaredAnnotations).forEach(System.out::println);
        /*
        @com.qs.javareflect.annotation.QsAnnotation(value=qs)
        @com.qs.javareflect.annotation.Roles(value=[@com.qs.javareflect.annotation.Role(value=DEV), @com.qs.javareflect.annotation.Role(value=TEST)])
         */

        System.out.println("--------------------");
        // 获取当前元素及其父元素上的指定注解(父元素上的注解需要具备继承传递性)
        System.out.println("getAnnotation");
        MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(annotation);
        /*
        @com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
         */

        // 获取当前元素上的指定注解
        System.out.println("getDeclaredAnnotation");
        QsAnnotation declaredAnnotation = clazz.getDeclaredAnnotation(QsAnnotation.class);
        System.out.println(declaredAnnotation);
        /*
        @com.qs.javareflect.annotation.QsAnnotation(value=qs)
         */

        System.out.println("--------------------");
        // 获取当前元素及其父元素上的指定重复注解(父元素上的注解需要具备继承传递性)
        System.out.println("getAnnotationsByType");
        MyAnnotation[] annotationsByType = clazz.getAnnotationsByType(MyAnnotation.class);
        Arrays.stream(annotationsByType).forEach(System.out::println);
        /*
        @com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
         */

        // 获取当前元素上的指定重复注解
        System.out.println("getDeclaredAnnotationsByType");
        Role[] declaredAnnotationsByType = clazz.getDeclaredAnnotationsByType(Role.class);
        Arrays.stream(declaredAnnotationsByType).forEach(System.out::println);
        /*
        @com.qs.javareflect.annotation.Role(value=DEV)
        @com.qs.javareflect.annotation.Role(value=TEST)
         */
    }

    @Override
    public String toString() {
        return super.toString();
    }

    @Test
    public void test() {
        List list;
    }
}

@QsAnnotation
@Role("DEV")
@Role("TEST")
class MyAnnotationTest extends MyAnnotationMain {

    private String name;
}

二、Java 内存分析

在这里插入图片描述


1. 类加载器 ClassLoader

  • 类加载过程:
    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
    在这里插入图片描述

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

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

    static {
        System.out.println("ClassLoaderMain 类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 当虚拟机启动,先初始化 main() 方法所在的类。
        /*
        ClassLoaderMain 类被加载
         */

        System.out.println("--------------------");
        // 2. new 一个类的对象。
//        Son son = new Son();
        /*
        Father 类被加载
        Son 类被加载
         */

        System.out.println("--------------------");
        // 3. 调用类的静态成员(除了 final 常量)和静态方法。
//        System.out.println(Son.name2);
        /*
        Father 类被加载
        Son 类被加载
        Son
         */

        System.out.println("--------------------");
        // 4. 使用 java.lang.reflect 包的方法对类进行反射调用。
//        Class.forName("com.qs.javareflect.classloader.Son");
        /*
        Father 类被加载
        Son 类被加载
         */

        System.out.println("====================");
        // 1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化。
//        System.out.println(Son.name1);
        /*
        Father 类被加载
        Father
         */

        System.out.println("--------------------");
        // 2. 通过数组定义类引用,不会触发此类的初始化。
        Son[] sons = new Son[5];

		System.out.println("--------------------");
        // 3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。
        System.out.println(Son.age);
        /*
        18
         */
    }
}

class Father {

    static {
        System.out.println("Father 类被加载");
    }

    static String name1 = "Father";
}

class Son extends Father {

    static {
        System.out.println("Son 类被加载");
    }

    static String name2 = "Son";
    static final int age = 18;
}

2. 类加载器的作用

  1. 类加载过程:
    将 .class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的 java.lang.Class 对象, 作为方法区中类数据的访问入口。
  2. 类缓存:
    标准的 JavaSE 类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过 JVM 垃圾回收机制可以回收这些 Class 对象。
  3. 类加载器作用是用来把类 .class 文件装载进内存的。
    在这里插入图片描述

3. JVM 的类加载器

JVM 规范定义了如下类型的类加载器。
在这里插入图片描述

  1. 引导类加载器(根类加载器)。
  2. 扩展类加载器。
  3. 系统类加载器(应用类加载器)。
/**
 * 3. JVM 的类加载器
 */
@Test
public void test() {
    // 1. 获取系统类加载器(应用类加载器)
    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    System.out.println(systemClassLoader);// sun.misc.Launcher$AppClassLoader@18b4aac2
    System.out.println("--------------------");
    // 获取系统类加载器可以加载的路径
    System.out.println(System.getProperty("java.class.path"));

    System.out.println("--------------------");
    // 2. 获取扩展类加载器
    ClassLoader parent = systemClassLoader.getParent();
    System.out.println(parent);// sun.misc.Launcher$ExtClassLoader@27716f4

    System.out.println("--------------------");
    // 3. 获取根类加载器(C/C++写的,获取不到)
    ClassLoader parent2 = parent.getParent();
    System.out.println(parent2);// null
}
@Test
public void test2() throws ClassNotFoundException {
    // 1. 获取当前类的类加载器(应用类加载器)
    ClassLoader classLoader = Class.forName("com.qs.javareflect.classloader.ClassLoaderMain2").getClassLoader();
    System.out.println(classLoader);// sun.misc.Launcher$AppClassLoader@18b4aac2

    System.out.println("--------------------");
    // 2. Java内部类的类加载器(根类加载器)
    ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
    System.out.println(classLoader2);// null
}

三、反射机制

反射机制允许程序在执行期,借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

  • 动态语言
  1. 在运行时可以改变其结构的语言。
  2. 如:新的函数、对象、甚至代码可以被引入,已有的函数可以被删除或结构上的改变。
  3. 主要的动态语言:Object-CC#JavaScriptPHPPython等。
  • 静态语言
  1. 在运行时结构不可变的就是静态语言。
  2. 如:JavaCC++
  3. Java 不是动态语言,但 Java 可以称之为 准动态语言(可以利用反射机制获得类似动态语言的特性)。
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(clazz.hashCode());

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


1. 反射的功能

  1. 在运行时,构造任意一个类的对象。
  2. 在运行时,判断任意一个类所具有的成员变量和方法。
  3. 在运行时,判断任意一个对象所属的类。
  4. 在运行时,调用任意一个对象的成员变量和方法。
  5. 在运行时,处理注解。
  6. 在运行时,获取泛型信息。
  7. 在运行时,生成动态代理。

2. 反射优缺点

  • 优点:
    可以动态的创建和编译对象,很大的灵活性。
  • 缺点:
    对性能有影响,使用反射是一种解释操作。
    我们可以告诉 JVM,希望做什么并且满足我们的要求。这类操作总是慢于直接执行相同的操作。

3. 反射API


3.1 java.lang.Class
  • Class 也是一个类:
  1. Class 对象只能由系统创建。
  2. 一个类在 JVM 内存中只有一个 Class 对象。
  3. 一个 Class 对象对应的是加载到 JVM 中的一个 .class 文件。
  4. 一个类的所有实例都是由同一个 Class 实例生成。
  5. 通过 Class 对象可以得到一个类的整个结构。
  6. Class 类是 Java Reflection 的根源(任何想动态加载运行的类,唯有先获得相应的 Class 对象)。

  • Class 类常用方法
方法名描述
static Class<?> forName(String className)返回指定 className(类全路径) 的 Class 对象
native Class<? super T> getSuperclass()返回 Class 对象的父类 Class 对象
ClassLoader getClassLoader()获取当前类的类加载器
Class<?>[] getInterfaces()获取 Class 对象的接口
T newInstance()创建 Class 对象所表示的元素(类、接口、数组类或void)对象
Constructor<?>[] getConstructors()获取当前类的 Constructor 对象数组
Method getMethod(String name, Class<?>... parameterTypes)获取当前类指定方法名(name)和参数类型(parameterTypes)的 Method 对象
Field[] getDeclaredFields()获取当前类的 Field 对象数组
/**
 * 3.1 java.lang.Class
 */
@Test
public void test() throws ClassNotFoundException {
    // 通过反射获取类的 Class 对象
    Class<?> clazz = Class.forName("com.qs.javareflect.clazz.Teacher");
    System.out.println(clazz);// class com.qs.javareflect.clazz.Teacher

    System.out.println("--------------------");
    // 获得父类类型
    Class<?> superclass = clazz.getSuperclass();
    System.out.println(superclass);// class com.qs.javareflect.clazz.Person

    System.out.println("--------------------");
    // 获取根类型
    Class<? extends Class> rootClass = clazz.getClass();
    System.out.println(rootClass);// class java.lang.Class
}

3.2 Class 对象的三种创建方式
/**
* 3.2 Class 对象的三种创建方式
 */
@Test
public void test2() throws ClassNotFoundException {
    Person person = new Student();
    System.out.println(person.name);// 学生

    System.out.println("--------------------");
    // 1. 通过`对象`获得
    Class<? extends Person> clazz1 = person.getClass();

    // 2. 通过`Class.forName()方法`获得
    Class<?> clazz2 = Class.forName("com.qs.javareflect.clazz.Student");

    // 3. 通过`类名.class`获得
    Class<Student> clazz3 = Student.class;

    System.out.println(clazz1.hashCode());// 1463801669
    System.out.println(clazz2.hashCode());// 1463801669
    System.out.println(clazz3.hashCode());// 1463801669

    System.out.println("--------------------");
    // 4. 基本类型的包装类,都有一个`内置类型 Type`属性
    Class<Integer> clazzInteger = Integer.TYPE;
    System.out.println(clazzInteger);// int
}

3.3 那些类型可以创建 Class 对象
  • 那些类型可以创建 Class 对象:
  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface:接口
  3. primitive type:基本数据类型
  4. []:数组
  5. annotation:注解@interface
  6. enum:枚举
  7. void
/**
 * 3.3 那些类型可以创建 Class 对象
 */
@Test
public void test3() {
    // 1. 类
    Class<Object> clazz1 = Object.class;
    System.out.println(clazz1);// class java.lang.Object
    Class<Class> clazz12 = Class.class;
    System.out.println(clazz12);// class java.lang.Class

    System.out.println("--------------------");
    // 2. 接口
    Class<Comparable> clazz2 = Comparable.class;
    System.out.println(clazz2);// interface java.lang.Comparable

    System.out.println("--------------------");
    // 3. 基本数据类型
    Class<Integer> clazz3 = Integer.class;
    System.out.println(clazz3);// class java.lang.Integer

    System.out.println("--------------------");
    // 4. 一维数组
    Class<String[]> clazz4 = String[].class;
    System.out.println(clazz4);// class [Ljava.lang.String;
    // 二维数组
    Class<int[][]> clazz41 = int[][].class;
    System.out.println(clazz41);// class [[I

    System.out.println("--------------------");
    // 5. 注解
    Class<Override> clazz5 = Override.class;
    System.out.println(clazz5);// interface java.lang.Override

    System.out.println("--------------------");
    // 6. 枚举
    Class<ElementType> clazz6 = ElementType.class;
    System.out.println(clazz6);// class java.lang.annotation.ElementType

    System.out.println("--------------------");
    // 7. void
    Class<Void> clazz7 = void.class;
    System.out.println(clazz7);// void
}

4. 获取运行时类的结构

  1. Interface:实现的全部接口。
  2. Superclass:所继承的父类。
  3. Annotation:运行时注解。
  4. Constructor:构造。
  5. Method:方法。
  6. Field:属性。

4.1 Interface、Superclass、java.lang.annotation.Annotation
/**
 * 4.1
 * Interface 实现的全部接口
 * Superclass 所继承的父类
 * java.lang.annotation.Annotation 运行时注解
 */
@Test
public void test() throws ClassNotFoundException {
    Class clazz = Class.forName("com.qs.javareflect.clazz.User");

    // 1. 获取全类名(包名 + 类名)
    System.out.println(clazz.getName());// com.qs.javareflect.clazz.User

    System.out.println("--------------------");
    // 2. 获取简单类名(类名)
    System.out.println(clazz.getSimpleName());// User

    System.out.println("--------------------");
    // 3. 实现的全部接口
    Class[] interfaces = clazz.getInterfaces();
    for (Class c : interfaces) {
        System.out.println(c);
    }
    /*
    interface java.io.Serializable
     */

    System.out.println("--------------------");
    // 4. 所继承的父类
    Class superclass = clazz.getSuperclass();
    System.out.println(superclass);// class java.lang.Object

    System.out.println("--------------------");
    // 5. 运行时注解
    Annotation[] annotations = clazz.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }
    /*
    @com.qs.javareflect.annotation.MyAnnotation(count=0, key=, values=[张三, 李四])
     */
}

4.2 java.lang.reflect.Constructor 构造器
  1. Constructor<?>[] getConstructors():获取当前类的所有 public 构造器。
  2. Constructor<?>[] getDeclaredConstructors():获取当前类的所有构造器。
  3. Constructor<T> getConstructor(Class<?>... parameterTypes):获取当前类的指定 public 构造器
  4. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取当前类的指定构造器。
/**
 * 4.2 java.lang.reflect.Constructor 构造器
 */
@Test
public void test2() throws ClassNotFoundException, NoSuchMethodException {
    Class<?> clazz = Class.forName("com.qs.javareflect.clazz.Qs");

    // 1. 获取当前类的所有 public 构造器
    Constructor[] constructors = clazz.getConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
    /*
    public com.qs.javareflect.clazz.Qs(int,java.lang.String,int)
    public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
    public com.qs.javareflect.clazz.Qs(java.lang.String)
     */

    System.out.println("--------------------");
    // 2. 获取当前类的所有构造器
    Constructor[] constructors2 = clazz.getDeclaredConstructors();
    for (Constructor constructor : constructors2) {
        System.out.println(constructor);
    }
    /*
    public com.qs.javareflect.clazz.Qs(int,java.lang.String,int)
    public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
    public com.qs.javareflect.clazz.Qs(java.lang.String)
    private com.qs.javareflect.clazz.Qs()
     */

    System.out.println("--------------------");
    // 3. 获取当前类的指定构造器
    Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class, int.class, String.class);
    System.out.println(declaredConstructor);
    /*
    public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
     */
}

4.3 java.lang.reflect.Method 方法
  1. Method[] getMethods():获取当前类及其父类的所有 public 方法。
  2. Method[] getDeclaredMethods():获取当前类所有方法。
  3. Method getMethod(String name, Class<?>... parameterTypes):获取当前类及其父类的指定 public 方法(可能存在重载,所以要添加参数类型)。
  4. Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取当前类的指定方法。
/**
* 4.3 java.lang.reflect.Method 方法
 */
@Test
public void test3() throws ClassNotFoundException, NoSuchMethodException {
    Class<?> clazz = Class.forName("com.qs.javareflect.clazz.User");

    // 1. 获取当前类及其父类的所有 public 方法
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        System.out.println(method);
    }

    System.out.println("--------------------");
    // 2. 获取当前类所有方法
    Method[] methods2 = clazz.getDeclaredMethods();
    for (Method method : methods2) {
        System.out.println(method);
    }

    System.out.println("--------------------");
    // 3. 获取当前类及其父类的指定方法(可能存在重载,所以要添加参数类型)
    @SuppressWarnings("all")
    Method getName = clazz.getMethod("getName", null);
    System.out.println(getName);// public java.lang.String com.qs.javareflect.clazz.User.getName()
    Method setName = clazz.getMethod("setName", String.class);
    System.out.println(setName);// public void com.qs.javareflect.clazz.User.setName(java.lang.String)
    // 获取父类的指定方法
    @SuppressWarnings("all")
    Method wait = clazz.getMethod("wait", null);
    System.out.println(wait);// public final void java.lang.Object.wait() throws java.lang.InterruptedException
}

4.4 java.lang.reflect.Field 属性
  1. Field[] getFields():获取当前类及其父类的所有 public 属性。
  2. Field[] getDeclaredFields():获取当前类的所有属性。
  3. Field getField(String name):获取当前类及其父类的指定 public 属性。
  4. Field getDeclaredField(String name):获取当前类的指定属性。
/**
 * 4.4 java.lang.reflect.Field 属性
 */
@Test
public void test4() throws ClassNotFoundException, NoSuchFieldException {
    Class clazz = Class.forName("com.qs.javareflect.clazz.User");

    // 1. 获取当前类及其父类的所有 public 属性
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    // public int com.qs.javareflect.clazz.User.age

    System.out.println("--------------------");
    // 2. 获取当前类的所有属性
    Field[] fields2 = clazz.getDeclaredFields();
    for (Field field : fields2) {
        System.out.println(field);
    }
    /*
    int com.qs.javareflect.clazz.User.count
    private java.lang.String com.qs.javareflect.clazz.User.name
    public int com.qs.javareflect.clazz.User.age
     */

    System.out.println("--------------------");
    // 3. 获取当前类的指定属性
    Field name = clazz.getDeclaredField("name");
    System.out.println(name);// private java.lang.String com.qs.javareflect.clazz.User.name
}

5. 创建运行时类的对象


5.1 反射创建对象
  1. T newInstance() 无参构造。
  1. 必须有一个无参数的构造器。
  2. 无参的构造器需要有足够的访问权限。
  1. Constructor<T> getConstructor(Class<?>... parameterTypes) 有参无参公有构造。
  1. parameterTypes:参数类型。
  1. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 有参无参公有私有构造。
  1. setAccessible(boolean flag) 操作私有构造(private),需要设置为可访问。
  1. Method、Field、Constructor 对象都有 setAccessible 方法。
  2. setAccessible 方法的作用是启动和禁用访问安全检查的开关。
  3. 参数值为 true,指示反射的对象在使用时应该取消 Java 语言访问检查。
  4. 提高反射的效率(如果代码中使用反射,而该代码需要频繁的被调用,那么请设置为 true)。
  5. 使原本无法访问的私有成员也可以访问。
/**
 * 5.1 反射创建对象 Constructor
 */
@Test
public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");

    // 1. 无参构造器
    Programmer programmer = (Programmer) clazz.newInstance();
    System.out.println(programmer);// Programmer(language=null)

    System.out.println("--------------------");
    // 2. 指定形参类型的构造器(公有构造)
    Constructor constructor = clazz.getConstructor(String.class);
    Programmer programmer2 = (Programmer) constructor.newInstance("Python");
    System.out.println(programmer2);// Programmer(name=null, language=Python)

    System.out.println("--------------------");
    // 3. 指定形参类型的构造器(公有私有构造)
    Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, String.class);
    // 不能直接操作私有构造,需要手动关闭程序的安全监测(设置为可访问)
    declaredConstructor.setAccessible(true);
    Programmer programmer3 = (Programmer) declaredConstructor.newInstance("qs", "Java");
    System.out.println(programmer3);// Programmer(name=qs, language=Java)
}

5.2 反射调用方法
  1. Method getMethod(String name, Class<?>... parameterTypes) 获取公有方法
  1. parameterTypes:参数类型。
  1. Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取公有私有方法
  1. setAccessible(boolean flag) 操作私有方法(private),需要设置为可访问。
  1. Object invoke(Object obj, Object... args)
  1. Object:方法返回值(无返回值为null)。
  2. obj:对象。
  3. args:方法参数列表(无参为null)。
    在这里插入图片描述
/**
 * 5.2 反射调用方法 Method
 */
@Test
public void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");
    // 创建一个对象
    Programmer programmer = (Programmer) clazz.newInstance();
    // 通过反射获取一个方法
    Method setName = clazz.getDeclaredMethod("setName", String.class);
    // 调用方法
    setName.invoke(programmer, "骑士");
    System.out.println(programmer.getName());// 骑士
}

5.3 反射操作属性 Field
/**
 * 5.3 反射操作属性 Field
 */
@Test
public void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
    Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");
    // 创建一个对象
    Programmer programmer = (Programmer) clazz.newInstance();
    // 通过反射获取一个属性
    Field language = clazz.getDeclaredField("language");
    System.out.println(language.getType());// class java.lang.String
    System.out.println(language.getName());// language
    // 不能直接操作私有属性,需要手动关闭程序的安全监测(设置为可访问)
    language.setAccessible(true);
    language.set(programmer, "Python");
    System.out.println(programmer.getLanguage());// Python
}

四、反射性能测试

/**
 * @author: wy
 * describe: 四、反射性能测试
 */
public class ClassMain4 {

    /**
     * 1. 普通方式调用
     */
    @Test
    public void test() {
        User user = new User();

        Instant start = Instant.now();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        Instant end = Instant.now();

        Duration duration = Duration.between(start, end);
        System.out.printf("普通方式,执行十亿次: %s毫秒", duration.toMillis());
        // 普通方式,执行十亿次: 2744毫秒
    }

    /**
     * 2. 反射方式调用
     */
    @Test
    public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class<? extends User> clazz = user.getClass();
        Method getName = clazz.getMethod("getName", null);
        getName.invoke(user, null);

        Instant start = Instant.now();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        Instant end = Instant.now();

        Duration duration = Duration.between(start, end);
        System.out.printf("反射方式,执行十亿次: %s毫秒", duration.toMillis());
        // 反射方式,执行十亿次: 19毫秒
    }

    /**
     * 3. 反射方式调用(关闭安全监测)
     */
    @Test
    public void test3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        User user = new User();
        Class<? extends User> clazz = user.getClass();
        Method getName = clazz.getMethod("getName", null);
        getName.setAccessible(true);
        getName.invoke(user, null);

        Instant start = Instant.now();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        Instant end = Instant.now();

        Duration duration = Duration.between(start, end);
        System.out.printf("反射方式(关闭安全监测),执行十亿次: %s毫秒", duration.toMillis());
        // 反射方式(关闭安全监测),执行十亿次: 10毫秒
    }
}

五、反射获取泛型信息

  • Java 采用泛型擦除机制来引入泛型,Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换问题。但是,一旦编译完成,所有和泛型有关的类型全部被擦除。
  • 为了通过反射操作这些类型, Java 新增了下面几种类型来代表不能被归到 Class 类中的类型,但是又和原始类型齐名的类型。
  1. ParameterizedType:表示一种参数化类型(比如:Collection)。
  2. GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。
  3. TypeVariable:是各种类型变量的公共父接口。
  4. WildcardType:表示一种通配符类型表达式。
/**
 * 通过反射获取泛型信息
 */
public static void main(String[] args) throws NoSuchMethodException {
    Method method = ClassMain5.class.getMethod("test", Map.class, List.class);

    // 1. 获取泛型参数类型
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
        System.out.println("-------------------- " + genericParameterType);
        if (genericParameterType instanceof ParameterizedType) {
            // 转为结构化参数类型
            ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
            // 获取实际参数类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
    /*
    -------------------- java.util.Map<java.lang.String, com.qs.javareflect.clazz.User>
    class java.lang.String
    class com.qs.javareflect.clazz.User
    -------------------- java.util.List<com.qs.javareflect.clazz.User>
    class com.qs.javareflect.clazz.User
     */

    System.out.println("====================");
    // 2. 获取泛型返回值类型
    Type genericReturnType = method.getGenericReturnType();
    System.out.println("-------------------- " + genericReturnType);
    if (genericReturnType instanceof ParameterizedType) {
        // 转为结构化参数类型
        ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
        // 获取实际参数类型
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }
    }
    /*
    -------------------- java.util.Map<java.lang.String, com.qs.javareflect.clazz.User>
    class java.lang.String
    class com.qs.javareflect.clazz.User
     */
}

六、反射获取注解信息

  • getAnnotations()
  • getAnnotation()
/**
 * @author: wy
 * describe: 六、反射获取注解信息
 */
public class ClassMain6 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> clazz = Class.forName("com.qs.javareflect.clazz.User");

        // 1. 获取类上注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // @com.qs.javareflect.annotation.MyAnnotation(count=0, value=qs, key=, values=[张三, 李四])

        System.out.println("--------------------");
        // 2. 获取注解的 value 值
        MyAnnotation myAnnotation = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(myAnnotation.value());// qs

        System.out.println("--------------------");
        // 3. 获取属性上注解
        Field field = clazz.getDeclaredField("name");
        MyAnnotation myAnnotation2 = field.getAnnotation(MyAnnotation.class);
        System.out.println(myAnnotation2.value());// qshome
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

骑士梦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值