JavaSE7️⃣注解 & 反射

1、注解

1.1、Annotation

JDK 1.5 引入

  1. 作用

    • 对程序做出解释(该作用类似 comment 注释)
    • 可以被程序读取(如编译器)。
  2. 格式@注解名 (参数)

    @Override
    @SuppressWarnings(value = "unchecked")
    
  3. 使用范围

    • 在 package、class、method、field 等的上方使用。
    • 可通过反射机制访问。

1.2、内置注解

  • @Override:重写超类方法。
  • @Deprecated:方法不建议使用。
  • @SuppressWarnings:抑制编译时的警告信息。

1.3、元注解

元注解 (meta-annotation):负责注解其它的注解

Java 的 4 个元注解

  • @Documented:生成 Java Doc 文档
  • @Retention:保留级别(有效期)
    • SOURCE:源码
    • CLASS:类
    • RUNTIME:运行时
  • @Target:可用范围
    • TYPE:类类型,如 Interface、Class
    • FILED:成员变量
    • METHOD
    • PARAMETER
    • CONSTRUCTOR
    • LOCAL_VARIABLE
    • ANNOTATION_TYPE
    • PACKAGE
    • 1.8 引入
      • TYPE_PARAMETER
      • TYPE_USE
  • @Inherited:子类可以继承父类中的该注解

1.4、自定义注解

1.4.1、@interface

使用 @interface 自定义注解

该注解会自动实现接口java.lang.annotation.Annotaion

@元注解
public @interface 注解名{
    参数类型 参数名();
    // 如:int count() default 0;
}
  • 定义参数:在注解内部定义的是参数,而不是方法。

    • 仅支持基本类型。
    • 可用 default 声明默认值,如 0 或空串。
  • value:若注解只有一个参数且名为 value,使用注解时可用字面量形式。

    @注解(value = "xxx")
    
    @注解("xxx")
    

1.4.2、case

case 1

  • 源码有效。

  • 方法级别可用。

  • 两个参数,且带有默认值。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface MyAnnotation1 {
        int id() default 0;
        String name() default "";
    }
    

case 2

  • 生成 Java Doc 文档。

  • 运行时有效。

  • 类级别、方法级别可用。

  • 单个参数,且名为 value。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface MyAnnotation2 {
        String value();
    }
    

2、反射

2.1、Reflection

Java 是静态语言,反射机制使 Java 具有动态性。

2.1、功能特点

  • 作用在运行时

    • 实例化对象
    • 访问成员变量和方法
    • 获取泛型信息,处理注解
    • 生成动态代理
  • 特点

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

    • 缺点影响性能。反射是一种解释操作(告诉 JVM 要做什么),比直接执行相应操作的效率低。

      image-20211220220022180

2.2、API

使用 Reflect API 获取类的内部信息,操作对象的内部属性和方法。

  • java.lang.Class:类的元数据

    • 在类加载阶段被载入方法区。
    • 采用 C++ 的 instanceKlass 描述。
  • java.lang.refect

    • Constructor:构造方法
    • Field:成员变量
    • Method:方法

2.2、Class

Class(反射对象)

类被加载之后,在堆内存的方法区中产生一个 Class 实例。

  • Class 对象

    1. Class 本身是一个类。
    2. Class 中包含类的所有结构:成员变量、方法、构造器、父类、实现的接口、注解等。
    3. Class 是反射机制的根源,只有获取到 Class 实例才能动态加载和运行对应的类。
  • Class 实例

    • 每个类的 Class 实例唯一。
    • 每个类的对象实例,都可以获取自己的 Class 实例。

2.2.1、常用方法

含义
forName(全限类名) 根据全限类名获取 Class 实例
newInstance() 创建该 Class 实例的对象
getSuperClass() 获取父类 Class 对象
getInterfaces() 获取实现的接口
getClassLoader() 获取类加载器
getName() 获取类名
getConstructors() 获取构造器
getDeclaredFields() 获取成员变量
getMethod() 获取方法

Hint:getClass() 在 Object 类中定义,所有类都可以调用。

2.2.2、实例获取方式

根据不同已知条件,获取 Class 实例。

获取方式
类名 类名.class
对象实例 实例名.getClass()
全类名 Class.forName(全限类名)
子类 Class 对象 子类.getSuperclass()
类加载器 -

e.g.

以 Person 类为例,其中 Student 是 Person 的子类

@Test
public void testGetInstance() throws ClassNotFoundException {
    Person p = new Person();
    // 1、已知类名
    Class<Person> class1 = Person.class;
    // 2、已知类的实例
    Class<? extends Person> class2 = p.getClass();
    // 3、已知全限类名
    Class<?> class3 = Class.forName("indi.jaywee.Person");
    // 4、已知子类Class:假设Student.class是已知的
    Class<? super Student> class4 = Student.class.getSuperclass();
}

2.2.3、常见 Class 实例

  1. 类 (class):Class 本身、外部类、内部类、局部内部类、匿名内部类
  2. 接口 (interface)
  3. 数组:一维、二维、...
  4. 枚举 (enum)
  5. 注解 (annotation)
  6. 基本数据类型 (primitive type)
  7. void

e.g.

// 类
Class<Class> class1 = Class.class;
Class<Person> class11 = Person.class;
// 接口
Class<Comparable> class2 = Comparable.class;
// 数组
Class<int[]> class3 = int[].class;
Class<int[][]> class33 = int[][].class;
// 枚举
Class<ElementType> class4 = ElementType.class;
// 注解
Class<Override> class5 = Override.class;
// 基本数据类型
Class<Double> class6 = Double.class;
// void
Class<Void> class7 = void.class;

image-20211221005232180

2.3、类加载机制

👉 JVM 类加载技术

2.4、获取类的结构

反射机制可在运行时获取类的结构

@Test
public void test() throws NoSuchFieldException, NoSuchMethodException {
    Class<Person> personClass = Person.class;

    /* 类名
        getName():全限类名
        getSimpleName():简单类名
     */
    String name = personClass.getName();
    String simpleName = personClass.getSimpleName();

    /* 成员变量
        getFields():所有public权限的成员变量
        getDeclaredFields():所有任意权限的成员变量
        getField():指定public权限的成员变量
        getDeclaredField():指定任意权限的成员变量
     */
    Field[] fields = personClass.getFields();
    Field[] declaredFields = personClass.getDeclaredFields();
    // Field field1 = personClass.getField("name");
    Field declaredField = personClass.getDeclaredField("name");

    /* 类的方法
        getMethods():所有public权限的方法:包括父类
        getDeclaredMethods():所有任意权限的方法:不包括父类
        getMethod():指定public权限的方法:包括父类(需要传入参数类型)
        getDeclaredMethod():指定任意权限的方法:包括父类(需要传入参数类型)

     */
    Method[] methods = personClass.getMethods();
    Method[] declaredMethods = personClass.getDeclaredMethods();
    Method setName = personClass.getMethod("setName", String.class);
    Method setAge = personClass.getDeclaredMethod("setAge", int.class);

    /* 构造器:同上
        getConstructors():所有public权限的构造器:包括父类
        getDeclaredConstructors():所有任意权限的构造器:不包括父类
        getConstructor():指定public权限的构造器:包括父类(需要传入参数类型)
        getDeclaredConstructor():指定任意权限的构造器:包括父类(需要传入参数类型)
     */
    Constructor<?>[] constructors = personClass.getConstructors();
    Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
    Constructor<Person> constructor = personClass.getConstructor(null);
    // Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);

}

Constructor-创建实例

知识点:通过反射创建实例,会调用构造方法。

通过Class对象的 newInstance() 方法,可以创建类的实例对象。

  1. 该方式默认使用类的无参构造器
  2. 要求类必须有无参构造器,且访问权限足够。

通过构造器的 newInstance() 方法,也可以创建类的实例化对象

  1. 获取本类的指定形参的构造器:getDeclaredConstructor (Class<?>... parameterTypes);
  2. 实例化对象:调用构造器的 newInstance (Object ... initargs) 方法,传递参数。

e.g.

@Test
public void testGetInstance() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Class<?> personClass = Person.class;

    // 无参构造
    Person person1 = (Person) personClass.newInstance();
    // 无参构造
    Constructor<?> constructor = personClass.getDeclaredConstructor(null);
    Person person2 = (Person) constructor.newInstance(null);
    // 有参构造
    Constructor<?> constructor1 = personClass.getDeclaredConstructor(String.class, int.class);
    Person person3 = (Person) constructor1.newInstance("Jaywee", 7);

    System.out.println(person1);
    System.out.println(person2);
    System.out.println(person3);
}

Method-调用方法

  1. 获取本类的指定形参的方法:通过 Class对象的 getDeclaredMethod (Class<?>... parameterTypes) 方法获取;
  2. 调用方法:调用方法的 invoke (Object obj, Object... args) 方法
    • obj:被调用方法所属的对象;
    • args:方法参数;
    • 返回值:对应原方法的返回值,若原方法无返回值则为null;
  3. 注意
    • 原方法为静态方法,形参 Object obj 可为 null;
    • 原方法形参列表为空, Object[] args 可为 null;
    • 原方法权限为 private,需要先调用 setAccessible (true) 方法开启访问权限,才可访问 invoke() 方法。

e.g.

分别以 getter 和 setter 为例

@Test
public void testInvokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Class<Person> personClass = Person.class;
    // 创建实例
    Person person = personClass.newInstance();
    // 获取并调用getter
    Method getAge = personClass.getDeclaredMethod("getAge", null);
    int age = (int) getAge.invoke(person);
    // 获取并调用setter
    Method setName = personClass.getDeclaredMethod("setName", String.class);
    setName.invoke(person, "Jaywee");
}

Field-获取成员变量

  1. 获取本类的指定成员变量:通过 Class对象的 getDeclaredField (String name) 方法获取;
  2. 调用成员变量的相关方法
    • set (Object obj, Object value):相当于 obj.setXxx (value)
    • get (Object obj):相当于 obj.getXxx()
  3. 注意:如果成员变量的权限为 private,需要先调用 setAccessible (true) 方法开启访问权限,才可访问私有变量的方法。

e.g.

@Test
public void testGetField() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
    Class<Person> personClass = Person.class;
    // 创建实例
    Person person = personClass.newInstance();
    // 获取name属性
    Field name = personClass.getDeclaredField("name");
    name.setAccessible(true);

    // 相当于调用person.setName("Jaywee")
    name.set(person,"Jaywee");
    // 相当于调用person.getName()
    Object o = name.get(person);
    System.out.println(o);
}

setAccessible()

Constructor、Method、Field对象都有 setAccessible() 方法

public void setAccessible(boolean flag)
  • 作用:启动或禁用访问安全检查;
  • true:允许访问,即禁用安全检查
    • 使得私有成员得以访问;
    • 提高反射效率,如果有使用到反射的代码需要被频繁使用,需设置 true
  • false:禁止访问,即开启安全检查(默认值
posted @ 2023-03-13 00:45  Jaywee  阅读(24)  评论(0编辑  收藏  举报

👇