您的位置 首页 java

还不会Java的注解和反射?那您怎样成为大佬?

Java 高级特性之注解+反射

一、注解

1.什么是 注解

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

Annotation的作用:

  • 注解不是程序的本身,可以对程序作出解析,这点和注释没什么区别
  • 可以被其他程序读取,这一特性特别作用,因为我们通过反射的技术获取到注解里的相关信息,然后进行相关操作,如我们常常自定义的校验规则,api接口的权限值等等都可以通过注解实现。

annotation 的格式: @注解名 ,如:@SuppressWarnings(value=”unchecked”);注解的定义:@interface 注解名{}

Annotation使用的地方: 可以使用在 PACKAGE ,class.method,field等上面,相当于给他们添加了二外的辅助信息,我们可以通过 反射机制 对这些元数据的访问。

2.内置注解

在Java中有许多已经内置好的注解供我们使用,如@Override,@Deprecated,@SuppressWainings等等

3.元注解

元注解的作用就是负责注解其他注解,也就是对其他一般的注解的一种说明。Java提供4个元注解供使用 。

@Target: 用于描述注解的使用范围(即被描述的注解用在什么地方)。

类型

描述

TYPE

作用范围:类

FIELD

作用范围:变量字段

METHOD

作用范围:方法

PARAMETER

作用范围:参数

Constructor

作用范围: 构造器

LOCAL_VARIABLE

作用范围:本地变量

ANNOTATION_ Type

作用范围:注解

PACKAGE

作用范围:包

TYPE_PARAMETER

作用范围:任何声明类型的地方

TYPE_USE

作用范围:任意使用类型的地方

@Retention: 表示需要在什么级别保存该注解信息,用于描述注解的 生命周期 (SOURCE<CLASS<RUNTIME)

类型

描述

SOURCE

生命周期只保留在源码

CLASS

生命周期保留到.class编译阶段

RUNTIME

生命周期保留到 jvm 运行阶段

@Document: 说明该注解将被包换在javadoc中

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

4.自定义注解

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

  • @Interface用来声明一个注解,格式:public @ interface 注解名 {定义内容}
  • 其中的每个方法实际上是声明了一个配置参数,方法的名称就是参数的名称
  • 返回值类型就是参数的类型(返回值只能是基本类型、Class、 String enum
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员,一般参数名为value
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0来作为默认值
   package com.xiaoxian.missyou.annotion;
  
  import java.lang.annotation.*;
  
  @MyAnnotion(num = 0,friends = {"123","456"})
  public class AnnotionTest {
  
  }
  
  
  @Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Inherited
  @interface MyAnnotion{
      String name() default "";
  
      int num();
  
      String[] friends() default {"123","456"};
  }
    

二、反射

1.什么是反射

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

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

正常方式 :引入包名 —> new实例化 —-> 取得实例化对象

反射方式 :实例化对象 —–> getClass()方法 ——> 得到完整的包名

2.反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取 泛型 信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解

3.反射的优缺点

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

缺点: 对性能有影响,这类操作总是慢于直接执行相同的操作。通过invoke()调用方法的时候,可以先使用setAccessible(true)方法,取消java语言访问检查,可以提高一点点性能。

4.通过反射获取class对象

   package com.xiaoxian.missyou.inject;
  
  import lombok.Data;
  
  public class Test1 {
      public  static   void  main(String[] args) throws ClassNotFoundException {
          Class<?> c1 = Class.forName("com.xiaoxian.missyou.inject.Student");
          Class<?> c2 = Class.forName("com.xiaoxian.missyou.inject.Student");
  
          //获取的classd对象都是同一个对象,因为只有一个class对象
          System.out.println(c1.hashCode());
          System.out.println(c2.hashCode());
  
          Person person = new Student();
  
          //方式一:通过对象获取class对象
          Class c3 = person.getClass();
          System.out.println(c3.hashCode());
  
          //方式二:forname()获取class对象
          Class c4 = Class.forName("com.xiaoxian.missyou.inject.Student");
          System.out.println(c4.hashCode());
  
          //方式三:通过类名.class获取class对象
          Class c5 = Student.class;
          System.out.println(c5.hashCode());
  
          //方式四:基本类型的包装类可以通过Type属性获取class对象
          Class c6 = Integer.TYPE;
          System.out.println(c6.hashCode());
  
          //获取父类的class对象
          Class c7 = c3.getSuperclass();
          System.out.println(c7.hashCode());
          
      }
  }
  
  @Data
  class Person {
       private  String name;
  }
  
  @Data
  class Student  extends  Person{
      private Integer age;
  }
    

5.哪些类型可以有Class对象

  • class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface: 接口
  • []: 数组
  • enum: 枚举
  • annotation: 注解@interface
  • primitive type: 基本数据类型
  • void
   package com.xiaoxian.missyou.inject;
  
  import java.lang.annotation.ElementType;
  import java.util.Map;
  
  public class Test2 {
      public static void main(String[] args) {
          
          Class c1 = Object.class;//类
  
          Class c2 = Map.class;//接口
  
          Class c3 = String[].class;//一维数组
  
          Class c4 = String[][].class;// 二维数组 
  
          Class c5 = Override.class;//注解
  
          Class c6 = ElementType.class;//枚举
  
          Class c7 = Integer.class;//基本数据类型
  
          Class c8 = void.class;//void
  
          Class c9 = Class.class;//class
      }
  }
    

6.获取运行时类的完整结构

   package com.xiaoxian.missyou.inject;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  
  public class Test3 {
      public static void main(String[] args) throws NoSuchField Exception , NoSuchMethodException {
          Class c1 = Student.class;
  
          //获取类的名字
          System.out.println(c1.getName());//获取 包名+类名
          System.out.println(c1.getSimpleName());//获取类名
  
          //获取类的属性
          System.out.println("================================");
          Field[] fields = c1.getFields();//只能获取public的属性
          for (Field field : fields) {
              System.out.println(field);
          }
          System.out.println("================================");
          Field[] declaredFields = c1.getDeclaredFields();//获取全部属性
          for (Field declaredField : declaredFields) {
              System.out.println(declaredField);
          }
  
          //获取指定属性
          Field age = c1.getDeclaredField("age");
          System.out.println(age);
  
          //获取类的方法
          System.out.println("================================");
          Method[] methods = c1.getMethods();//获取本类+父类的全部public方法
          for (Method method : methods) {
              System.out.println(method);
          }
  
          Method[] declaredMethods = c1.getDeclaredMethods();//获取本类所有方法
          for (Method declaredMethod : declaredMethods) {
              System.out.println(declaredMethod);
          }
  
          //获取指定的方法
          Method getAge = c1.getMethod("getAge", null);
          Method setAge = c1.getMethod("setAge",  Integer .class);
          System.out.println(getAge);
          System.out.println(setAge);
  
          //获取 构造方法 
          System.out.println("================================");
          Constructor[] constructors = c1.getConstructors();//获取public的构造方法
          for (Constructor constructor : constructors) {
              System.out.println(constructor);
          }
          Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取全部的构造方法
          for (Constructor declaredConstructor : declaredConstructors) {
              System.out.println(declaredConstructor);
          }
  
          Constructor declaredConstructor = c1.getDeclaredConstructor();
          System.out.println("指定构造器:" + declaredConstructor);
      }
  }
    

7.动态创建对象执行方法

   package com.xiaoxian.missyou.inject;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  
  public class Test4 {
      public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
          Class c1 = Student.class;
  
          //方式一:本质调用了类的无参构造函数,并且要求该无参构造函数为public
          Student student = (Student)c1.newInstance();
          System.out.println(student);
  
          //方式二:通过构造器创建对象
          Constructor constructor = c1.getDeclaredConstructor(Integer.class);
          Student student2 = (Student) constructor.newInstance(18);
          System.out.println(student2);
  
          //通过反射调用普通方法
          Method setAge = c1.getDeclaredMethod("setAge", Integer.class);
          setAge.invoke(student, 19);
          System.out.println(student.getAge());
  
          //通过反射操作属性
          System.out.println("==========================================");
          Field age = c1.getDeclaredField("age");
  
          //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
          age.setAccessible(true);
  
          age.set(student, 20);
          System.out.println(student.getAge());
      }
  }
    

注意: Object invoke(Object obj,Object … args)

  • Object对应的原方法的返回值,若原方法无返回值,此时返回null
  • 若原方法为 静态方法 ,此时 形参 Object obj可为null
  • 若原方法形参列表为空,则Object[] args为null
  • 若原方法声明为private,则需要在调用invoke()方法前,显式调用setAccessible(true)方法

8.通过反射获取泛型

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

   package com.xiaoxian.missyou.inject;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Parameter;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.List;
  import java.util.Map;
  
  public class Test5 {
  
      public void test01(Map<String, Student> map, List<Student> list) {
          System.out.println("test01");
      }
  
      public Map<String, Student> test02() {
          System.out.println("test02");
          return null;
      }
  
      public static void main(String[] args) throws NoSuchMethodException {
          Class c1 = Test5.class;
  
          Method test01 = c1.getDeclaredMethod("test01", Map.class, List.class);
          Type[] genericParameterTypes = test01.getGenericParameterTypes();
          for (Type genericParameterType : genericParameterTypes) {
              System.out.println("===" + genericParameterType);
              if (genericParameterType  instanceof  ParameterizedType) {
                  Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                  for (Type actualTypeArgument : actualTypeArguments) {
                      System.out.println(actualTypeArgument);
                  }
              }
          }
  
  
          System.out.println("=======================================");
          Method test02 = c1.getDeclaredMethod("test02");
          Type genericReturnType = test02.getGenericReturnType();
          if (genericReturnType instanceof ParameterizedType) {
              Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
              for (Type actualTypeArgument : actualTypeArguments) {
                  System.out.println(actualTypeArgument);
              }
          }
  
      }
  }
    

9.通过反射操作注解

   package com.xiaoxian.missyou.inject;
  
  import lombok.Data;
  
  import java.lang.annotation.*;
  import java.lang.reflect.Field;
  
  public class Test6 {
      public static void main(String[] args) {
          Class c1 = User.class;
          Annotation[] annotations = c1.getAnnotations();
          for (Annotation annotation : annotations) {
              System.out.println(annotation);
          }
  
          //获取指定的注解的值
          MyTable myTable = (MyTable)c1.getAnnotation(MyTable.class);
          System.out.println(myTable.value());
  
          //获取类上字段的注解
          Field[] declaredFields = c1.getDeclaredFields();
          for (Field declaredField : declaredFields) {
              System.out.println("=======================");
              MyField myField = (MyField) declaredField.getAnnotation(MyField.class);
              System.out.println(myField.columnName());
              System.out.println(myField.type());
              System.out.println(myField.length());
  
          }
      }
  }
  
  @Data
  @MyTable("db_user")
  class User{
      @MyField(columnName = "db_user_id", type = "Long", length = 10)
      private Long id;
  
      @MyField(columnName = "db_user_name", type = "String", length = 128)
      private String name;
  }
  
  @Target(ElementType.TYPE)
  @Retention(RetentionPolicy.RUNTIME)
  @interface MyTable {
      String value();
  }
  
  @Target(ElementType.FIELD)
  @Retention(RetentionPolicy.RUNTIME)
  @interface MyField {
      String columnName();
  
      String type();
  
      int length();
  }
    

如果对您有一点点的帮助,请关注+转发哟,您的肯定是我继续编写文章的动力。由于本人工作比较忙碌,要学习后,再编写笔记,所以一周大概更新一次,创作不易,请大家多多支持,想要markdown版笔记,可以关注+私聊获取哟

文章来源:智云一二三科技

文章标题:还不会Java的注解和反射?那您怎样成为大佬?

文章地址:https://www.zhihuclub.com/193661.shtml

关于作者: 智云科技

热门文章

网站地图