【JAVA CORE_API】Day22 Java的反射机制(2)、注解

变长参数

  • 在Java中,变长参数(varargs)使得方法能够接收不定数量的参数。你可以通过在参数类型后面加上三个点(...)来声明变长参数。例如:

             public static void doSome(int a, String... arg){
                 System.out.println(arg.length);
                 System.out.println(Arrays.toString(arg));
     
     
         }
    
  • 在调用这个方法时,你可以传入任意数量的int参数,甚至不传参数:

             doSome(1,"one");
             doSome(2,"one","two");
             doSome(3,"one","two","three");
    
  • 细节

    1. 变长参数在方法签名中的位置:变长参数必须是方法中最后一个参数,且一个方法只能有一个变长参数。

    2. 内部表现为数组:在方法内部,变长参数被当作数组处理,所以你可以用数组的方式来操作这些参数。

    3. 与重载的关系:如果有多个重载方法,Java会优先选择最具体的匹配,而不是直接使用变长参数的方法。

  • 使用变长参数让方法更灵活,特别是在不确定需要传递多少个参数的情况下。

利用反射机制调用Student的有参构造器进行实例化

 package day22;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 
 /**
  * 使用反射机制调用Student的有参构造器实例化
  * "李四",22,"女"
  */
 public class Test {
     public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {
         // 获取类对象
 //      Class studentClass = Student.class;
         Class studentClass = Class.forName("day22.Student");
         // Constructor实例化
         Constructor constructor = studentClass.getConstructor(String.class, int.class, char.class);
         Object object = constructor.newInstance("李四", 22, '女');
         System.out.println(object);
     }
 }
 

Method类

  • 在Java中,Method类是反射机制的一部分,用于在运行时获取方法的信息并调用方法。通过反射,你可以调用某个类的私有或公有方法,而无需在编译时知道方法的名称或签名。下面是具体步骤:

    1. 获取Class对象:首先,通过类名或对象获取该类的Class对象。

       Class clazz = Class.forName("day22.Person");
       Object object = clazz.newInstance();  // 等价于new Person
      
    2. 获取Method对象:使用getMethodgetDeclaredMethod来获取表示方法的Method对象。getMethod用于获取公有方法,getDeclaredMethod可以获取包括私有方法在内的所有方法。

       // 反射机制调用方法
       // 1) 通过类对象获取要调用的方法
       Method method = clazz.getMethod("sayHello");
      
    3. 调用方法:使用Method对象的invoke方法来调用这个方法。需要传入方法的对象实例(静态方法可以传入null)和实际参数。

       // 2) Method类上的invoke方法,用于执行方法,参数1为该方法所属的对象
       method.invoke(object);  // 等价于obj.sayHello();
      
  • 示例: 类Person,它有一个方法sayHello

     Class clazz = Class.forName("day22.Person");
     Object object = clazz.newInstance();  // 等价于new Person
     
     // 反射机制调用方法
     // 1) 通过类对象获取要调用的方法
     Method method = clazz.getMethod("sayHello");
     // 2) Method类上的invoke方法,用于执行方法,参数1为该方法所属的对象
     method.invoke(object);  // 等价于obj.sayHello();
    
  • 通过反射来调用这个私有方法:

    package day22;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * 利用反射机制调方法
     */
    public class ReflectDemo4 {
        public static void main(String[] args) {
            // 通过new实例化对象
            Person person = new Person();
            person.sayHello();
            person.setName("张三");
            System.out.println(person.getName());
    
            // 通过反射机制实例化对象
            try {
                Class clazz = Class.forName("day22.Person");
                Object object = clazz.newInstance();  // 等价于new Person
    
                // 反射机制调用方法
                // 1) 通过类对象获取要调用的方法
                Method method = clazz.getMethod("sayHello");
                // 2) Method类上的invoke方法,用于执行方法,参数1为该方法所属的对象
                method.invoke(object);  // 等价于obj.sayHello();
    
                // 如果方法有返回值,那么invoke方法的返回值就是实际调用该方法后的返回值
                Method method1 = clazz.getMethod("getName");
                Object returnValue = method1.invoke(object);
                System.out.println(returnValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
  • 我们也可以在不改变源代码的情况下,调用任意包的任意类的任意方法,这样我们就可以在实际业务中,进行补丁更新,直接在源代码中使用即可。

     package day22;
     
     import java.lang.reflect.InvocationTargetException;
     import java.lang.reflect.Method;
     import java.util.Scanner;
     
     /**
      * 利用反射机制调方法
      */
     public class ReflectDemo4 {
         public static void main(String[] args) {
                 // 感受动态机制 ----- 于是我们可以在不改变源代码的情况下,通过反射机制调用任意类的任意方法,就算跨包也能实现
                 System.out.println("-------------------");
                 Scanner scanner = new Scanner(System.in);
                 System.out.println("请输入类名:");
                 String className = scanner.next();
     
                 System.out.println("请输入方法名:");
                 String methodName = scanner.next();
     
                 Class clazz1 = Class.forName(className);
                 Object object1 = clazz1.newInstance();
                 Method method2 = clazz1.getMethod(methodName);
                 method2.invoke(object1);
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }
     
    
  • 同理,在使用反射机制调用有参方法也可以:

     package day22;
     
     import java.lang.reflect.Method;
     
     public class ReflectDemo5 {
         public static void main(String[] args) {
             // 原来的写法
             Person person = new Person();
             person.sayHello("Tony");
     
             try {
                 // 通过反射机制调用方法
                 Class clazz = Class.forName("day22.Person");
                 Object object = clazz.newInstance();
     
                 Method method = clazz.getMethod("sayHello");
                 method.invoke(object);
     
                 Method method1 = clazz.getMethod("sayHello", String.class);
                 method1.invoke(object, "Tom");
     
                 Method method2 = clazz.getMethod("sayHello", String.class, int.class);
                 method2.invoke(object, "Tom", 18);
             } catch (Exception e) {
                 e.printStackTrace();
             }
     
         }
     }
     
    

Method类常用API

 package day22;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
 /**
  * 反射对象都提供了若干的get方法,用于获取其内部表示内容的相关信息
  * 比如Method类,它的每一个实例用于表示一个方法,其提供了若干的get方法用于获取其表示的方法的信息
  */
 public class ReflectDemo6 {
     public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
         // 实例化Person类,调用sayHello(String name, int age)方法
         Class clazz = Class.forName("day22.Person");
         Method method = clazz.getMethod("sayHello", String.class, int.class);
 
         // getName():获取方法名
         String name = method.getName();
         System.out.println(name);
 
         // getReturnType():获取方法返回值类型
         Class returnType = method.getReturnType();
         System.out.println("该方法的返回值类型为:" + returnType);
 
         // getParameterCount():获取方法有多少参数
         int count = method.getParameterCount();
         System.out.println("方法共有:" + count + "个返回值!");
 
         // getParameterTypes():获取方法参数类型
         Class[] parameterTypes = method.getParameterTypes();
         for (Class parameterType : parameterTypes) {
             System.out.println("参数类型为:" + parameterType);
         }
 
         // getModifiers():获取方法修饰符
         int modifiers = method.getModifiers();
         switch (modifiers) {
             case Modifier.PUBLIC:
                 System.out.println("该方法是public修饰的!");
                 break;
             case Modifier.PRIVATE:
                 System.out.println("该方法是private修饰的!");
                 break;
             case Modifier.PROTECTED:
                 System.out.println("该方法是protected修饰的!");
                 break;
             default:
                 System.out.println("该方法是default修饰的!");
         }
     }
 }
 

练习

  • 准备工作(在Person类中添加如下代码方法):

         public void sayHello(){
             System.out.println(name + ":Hello!");
         }
         public void sayHello(String name){
             System.out.println(name + ":Hello!");
         }
         public void sayHello(String name, int age){
             System.out.println(name + ":Hello!我今年" + age + "岁了");
         }
         public void sayHi() {
             System.out.println(name + ":Hi!");
         }
         public void doSome(){
             System.out.println(name + ":做某事");
         }
         public void sleep(){
             System.out.println(name + ":睡了!");
         }
         public void watchTV(){
             System.out.println(name + ":在看电视!");
         }
         public void study(){
             System.out.println(name + ":在学习!");
         }
         public void playGame(){
             System.out.println(name + ":在玩游戏!");
         }
         public void sing(){
             System.out.println(name + " :在唱歌!");
         }
         public void say(String info){
             System.out.println(name + ":" + info);
         }
         public void say(String info, int count){
             for (int i = 0; i < count; i++) {
                 System.out.println(name + ":" + info);
             }
    
  • 需求:调用Person类中所有无参且公开,并且方法名中含有“字母s”的方法:

    1. 加载类对象;

    2. 通过类对象获取该类的所有方法;

    3. 判断给方法是否无参且公开,并且方法名中是否含有字母“s”;

    4. 如果满足,则调用该方法。

     package day22;
     
     import java.lang.reflect.InvocationTargetException;
     import java.lang.reflect.Method;
     import java.lang.reflect.Modifier;
     
     /**
      * 利用反射机制,调用Person类中所有无参且公开的方法
      */
     public class Test2 {
         public static void main(String[] args) throws Exception {
             // 1)加载类对象及方法
             Class clazz = Class.forName("day22.Person");
             Object object = clazz.newInstance();
             // 2)获取类中所有的方法
             Method[] methods = clazz.getMethods();
             // 3)输出类中所有的方法的方法名
             for (Method method : methods) {
                 System.out.println(method.getName());
             }
             // 4)判断该方法是否无参且公开,并且防范各种是否含有字母”s“
             int modifiers = Modifier.PUBLIC;
             for (Method method : methods) {
                 if (method.getParameterCount() == 0
                         &&
                         method.getName().indexOf('s') != -1  // 或写成 method.getName().contains("s");
                         && 
                         method.getModifiers() == modifiers) {
                     System.out.println("方法:" + method.getName() + "无参且公开!正在调用...");
                     // 调用方法
                     method.invoke(object);
                 }
             }
     
         }
     }
     
    

暴力反射

  • 反射可以强行访问私有的方法,随意在Person类中创建一个私有方法,然后在测试类的代码示例如下:

     package day22;
     
     import java.lang.reflect.InvocationTargetException;
     import java.lang.reflect.Method;
     
     public class Test3 {
         public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
             // 正常调用一下试试
             // Person person = new Person();
             // person.privateMethod();  // 会产生错误,因为privateMethod()是私有方法,不能在类外访问
     
             // 用反射的方式来试试呢
             Class clazz = Class.forName("day22.Person");
             Object object = clazz.newInstance();
     //        Method method = clazz.getMethod("privateMethod");  // 报错,找不到该方法,是因为是私有方法,不能在类外访问
     //        method.invoke(object);    // 报错,访问错误,非法权限
     
             System.out.println();
     
             // 强行访问 - 暴力反射
             Method method = clazz.getDeclaredMethod("privateMethod");
             method.setAccessible(true);  // 设置方法为可访问,以允许私有或受保护的方法在反射调用时被访问
             method.invoke(object);
             method.setAccessible(false);  // 不推荐这样使用,因为暴力反射破坏了Java的封装性,如果迫不得已需要使用,养成好习惯,用完关掉
         }
     }
     
    

通过反射特性操作属性

  • 创建一个Teacher类:

     package day22;
     
     import java.lang.reflect.Field;
     
     /**
      * 使用反射机制操作属性
      *
      * 实际开发中我们直接操作属性已经非常少了,因为按照标准Java Bean的写法,属性通常携程私有的,
      * 然后再通过getter和setter方法来操作属性。
      */
     public class ReflectDemo8{
         public static void main(String[] args) throws Exception {
             // 老方式,类名加点调方法
             Teacher teacher = new Teacher();
             teacher.name = "张三";  // 通常使用: teacher.setName("张三");
     
             // 反射方式,通过反射机制来操作属性
             Class clazz = Class.forName("day22.Teacher");
             Object object = clazz.newInstance();
     
             // java.lang.reflect.Field类
             // 反射对象之一,他的每一个实例用于表示一个属性的信息
             Field field = clazz.getField("name");
             // 为属性赋值
             field.set(object, "李四");
             System.out.println(object);
         }
     }
     
    
     package day22;
     
     public class Teacher {
         public String name;
     
         @Override
         public String toString() {
             return "Teacher{" +
                     "name='" + name + '\'' +
                     '}';
         }
     }
     
    
  • 再创建一个ReflectDemo8,我们来演示一下:

     package day22;
     
     import java.lang.reflect.Field;
     
     /**
      * 使用反射机制操作属性
      *
      * 实际开发中我们直接操作属性已经非常少了,因为按照标准Java Bean的写法,属性通常携程私有的,
      * 然后再通过getter和setter方法来操作属性。
      */
     public class ReflectDemo8{
         public static void main(String[] args) throws Exception {
             // 老方式,类名加点调方法
             Teacher teacher = new Teacher();
             teacher.name = "张三";  // 通常使用: teacher.setName("张三");
     
             // 反射方式,通过反射机制来操作属性
             Class clazz = Class.forName("day22.Teacher");
             Object object = clazz.newInstance();
     
             // java.lang.reflect.Field类
             // 反射对象之一,他的每一个实例用于表示一个属性的信息
             Field field = clazz.getField("name");
             // 为属性赋值
             field.set(object, "李四");
             System.out.println(object);
     
             // 获取指定对象中该属性的值
             Object value = field.get(object);
             System.out.println(value);
         }
     }
     
    

通过反射机制强行改变字符串

 package day22;
 
 import java.lang.reflect.Field;
 import java.util.Arrays;
 
 public class ReflectDemo9 {
     public static void main(String[] args) throws Exception {
         String s1 = "hello";//创建一个String对象表示"hello".然后将其存入常量池
         String s2 = "hello";//再次使用"hello"字面量时会重用对象,s2指向s1创建的对象(常量池中的对象)
         System.out.println("s1:"+s1);//s1:hello
         System.out.println("s2:"+s2);//s2:hello
 
         Class cls = String.class;
         //获取String内部用于保存字符串的char数组value
         Field filed = cls.getDeclaredField("value");
         filed.setAccessible(true);//打开访问权限
         char[] array = (char[])filed.get(s1);//获取s1字符串中value属性的值
         System.out.println(Arrays.toString(array));//[h,e,l,l,o]
 
         char[] array2 = new char[]{'j','a','v','a','!','!'};//"java!!"
         filed.set(s1,array2);
 
         System.out.println("s1:"+s1);//s1:java!!
         System.out.println("s2:"+s2);//s2:java!!
 
 
         String s3 = "hello";
         System.out.println("s3:"+s3);//s3:java!!
         System.out.println("hello");//java!!
         System.out.println("hello"+"world");
         String s = ",";
         System.out.println("hello"+s+"world");
     }
 }
 

注解

注解概述

什么是注解

  • Java中的注解(Annotation)是一种元数据(metadata),用于在代码中添加补充信息。注解不直接影响代码的执行,而是为编译器、开发工具或运行时提供一些指示或配置。它们通常用在类、方法、字段、参数、包等位置,位于声明之前,以@符号开头。例如,@Override注解表示一个方法重写了其父类中的方法。
- 注解本质上是为代码添加标签,以便在编译时或运行时可以被工具或框架读取,并执行相应的操作。Java提供了一些内置的注解(如`@Override`、`@Deprecated`),开发者也可以自定义注解来满足特定需求。

- 总结来说,Java的注解是一种提供代码补充信息的机制,它们不会改变代码的逻辑,但会被编译器或其他工具用来做特定处理。

注解的定义

  • 在Java中,注解的定义是通过使用@interface关键字来创建一个自定义的注解类型。注解定义可以包括元素(类似于方法),这些元素可以有默认值,也可以在使用注解时显式赋值。注解的定义本质上是一种接口,所有的注解都隐式继承自java.lang.annotation.Annotation接口。

  • 以下是一个简单的注解定义示例:

 public @interface MyAnnotation {
 }
  • 在这个示例中,MyAnnotation是一个注解类型,包含两个元素:valuenumber。这两个元素都有默认值,使用注解时可以选择性地为它们赋值。

注解的应用

  • 在Java中,注解可以应用在以下位置:

    1. 类、接口、枚举:可以为类、接口或枚举添加注解,以提供元数据或指示特殊行为。例如:

       @Deprecated
       public class MyClass {
           // ...
       }
      
    2. 方法:可以为方法添加注解,通常用于指示方法的特性或提供额外的行为。例如:

       @Override
       public void myMethod() {
           // ...
       }
      
    3. 字段(成员变量):可以为类的字段添加注解,通常用于依赖注入或映射关系。例如:

       @Autowired
       private MyService myService;
      
    4. 方法参数:可以为方法的参数添加注解,用于验证、绑定请求参数等。例如:

       public void myMethod(@RequestParam String param) {
           // ...
       }
      
    5. 构造函数:可以为构造函数添加注解,通常用于依赖注入或标识特殊构造行为。例如:

       @Inject
       public MyClass(MyDependency dependency) {
           // ...
       }
      
    6. 局部变量:可以为局部变量添加注解,通常用于简化或指定特定的编译行为。例如:

       @SuppressWarnings("unchecked")
       List<String> list = (List<String>) new ArrayList();
      
    7. :可以为整个包添加注解,这通常在包级别提供信息或行为。例如,在package-info.java文件中:

       @Deprecated
       package com.example.myapp;
      
    8. 类型参数:可以为泛型的类型参数添加注解,通常用于类型安全检查或元数据。例如:

       public class MyClass<@NonNull T> {
           // ...
       }
      
    9. 类型使用(Type Use):可以在任何使用类型的地方添加注解,包括类型转换、泛型实例化等。例如:

       MyClass<@NonNull String> myObject = (@NonNull MyClass<String>) new MyClass<>();
      

@Target()注解:

  • @Target 注解用于指定其他注解可以应用的 Java 元素的类型。它定义了一个注解的使用范围,确保注解只用于特定类型的程序元素(如类、方法、字段等)。

  • @Target 注解的初始值(即它定义的可能的目标)包括:

    • @Target(ElementType.TYPE): 应用于类、接口、或枚举。

    • @Target(ElementType.FIELD): 应用于字段。

    • @Target(ElementType.METHOD): 应用于方法。

    • @Target(ElementType.PARAMETER): 应用于方法或构造函数的参数。

    • @Target(ElementType.CONSTRUCTOR): 应用于构造函数。

    • @Target(ElementType.LOCAL_VARIABLE): 应用于局部变量。

    • @Target(ElementType.ANNOTATION_TYPE): 应用于注解类型。

    • @Target(ElementType.PACKAGE): 应用于包。

@Retention()注解

  • @Retention 注解用于指定注解的生命周期,也就是注解在程序运行时是否可用。它定义了注解保留的时间范围,影响注解的可见性。

  • @Retention 注解的初始值包括:

    • RetentionPolicy.SOURCE: 注解仅存在于源代码中,编译后会被丢弃。适用于只在编译时使用的注解,例如生成代码或进行编译时检查。

    • RetentionPolicy.CLASS: 注解存在于编译后的 .class 文件中,但在运行时不可用。适用于编译时检查或其他目的,但不需要在运行时访问注解。

    • RetentionPolicy.RUNTIME: 注解在运行时也可用。这是最常用的策略,适用于需要在运行时通过反射机制读取注解信息的情况。

练习

  • 输入任意一个类,如果该类被注解@AutoRunClass,则实例化

    • day22.Person -> 实例化

    • day22.Student -> 提示没有被标注,不实例化

 package day22;
 
 import day22.annotation.AutoRunClass;
 
 import java.util.Scanner;
 
 /**
  * 输入任意一个类,如果该类被注解@AutoRunClass,则实例化
  *
  * day22.Person -> 实例化
  * day22.Student -> 提示没有被标注,不实例化
  */
 public class Test4 {
     public static void main(String[] args) throws Exception {
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入一个类名:");
         String className = scanner.nextLine();
         Class clazz = Class.forName(className);
         if (clazz.isAnnotationPresent(AutoRunClass.class)) {
             System.out.println(className + "类被注解@AutoRunClass标注");
             Object obj = clazz.newInstance();
             System.out.println(className + "类被实例化!");
             System.out.println(obj);
         } else {
             System.out.println(className + "类没有被注解@AutoRunClass标注");
         }
     }
 }
 

方法注解

  • 创建一个新的注解@AutoRunMethod,并且只能修饰方法,并在Person类中的sayHello()方法前进行修饰。

  • 要求:

    • 查看Person类上的方法sayHello()是否被注解@AutoRunMethod修饰
 package day22;
 
 import day22.annotation.AutoRunMethod;
 
 import java.lang.reflect.Method;
 
 /**
  * 访问方法上的注解
  */
 public class ReflectDemo11 {
     public static void main(String[] args) throws Exception {
         // 查看Person类上的方法sayHello()是否被注解@AutoRunMethod注解标注
         // 1.获取Person类
         Class clazz = Class.forName("day22.Person");
         // 2.获取sayHello()方法
         Method method = clazz.getMethod("sayHello");
         // 3.获取方法上的注解
         boolean mark = method.isAnnotationPresent(AutoRunMethod.class);
         // 4.判断注解是否为AutoRunMethod类型
         // 5.如果为AutoRunMethod类型,则输出"sayHello()方法被注解@AutoRunMethod注解标注"
         // 6.否则,输出"sayHello()方法未被注解@AutoRunMethod注解标注"
         if (mark){
             System.out.println("sayHello()方法被注解@AutoRunMethod注解标注");
         }else {
             System.out.println("sayHello()方法未被注解@AutoRunMethod注解标注");
         }
     }
 }
 

练习

  • 输入一个类名,如果该类有被逐节@AutoRunClass注解的类名,再进行后续操作,调用Person类中所有被注解@AutoRunMethod标注的方法

     package day22;
     
     import day22.annotation.AutoRunClass;
     import day22.annotation.AutoRunMethod;
     
     import java.lang.reflect.Method;
     import java.util.Scanner;
     
     /**
      * 输入一个类名,如果该类有被逐节@AutoRunClass注解的类名,再进行后续操作
      * 调用Person类中所有被注解@AutoRunMethod标注的方法
      */
     public class Test5 {
         public static void main(String[] args) throws Exception {
             Scanner scanner = new Scanner(System.in);
             System.out.println("请输入一个类名:");
             String className = scanner.nextLine();
             Class clazz = Class.forName(className);
             if (clazz.isAnnotationPresent(AutoRunClass.class)) {
                 Object object = clazz.newInstance();  // 在if里实例化减少开销
                 Method[] methods = clazz.getDeclaredMethods();
                 for (Method method : methods) {
                     // 1.判断方法上是否有注解
                     boolean mark = method.isAnnotationPresent(AutoRunMethod.class);
                     // 2.判断注解是否为AutoRunMethod类型
                     // 3.如果为AutoRunMethod类型,则调用方法
                     if (mark) {
                         System.out.println("调用了" + method.getName() + "方法");
                         method.invoke(object);
                     }
                 }
             } else {
                 System.out.println("该类没有被逐节@AutoRunClass注解");
             }
         }
     }
     
    

定义注解参数

  • 注解参数(Annotation Parameters)是指在定义注解时,可以给注解指定一些参数,这些参数可以用来在运行时或编译时提供额外的元数据。注解参数是在定义注解时,通过定义方法来声明的。方法名即为注解的参数名,方法的返回类型即为参数的类型。

  • 例如,定义一个注解时,可以包含参数如下:

 public @interface MyAnnotation {
     String value(); // 注解参数
     int count() default 0; // 带有默认值的注解参数
 }
  • 在这个例子中,MyAnnotation注解有两个参数:valuecountvalue是一个必须指定的参数,而count有一个默认值。

  • 使用注解参数的作用

    1. 提供附加信息:成员变量允许在使用注解时提供额外的元数据,这些信息可以被处理工具或框架用来执行特定的操作。例如,@Entity(name = "User") 注解中的 name 参数提供了实体的名称信息。

    2. 控制行为:成员变量可以用于控制注解的行为或配置。例如,@RequestMapping(method = RequestMethod.GET) 中的 method 参数指定了处理请求的HTTP方法类型。

    3. 增强灵活性:通过注解参数,可以使注解更具通用性和灵活性。注解的使用者可以根据需要传递不同的参数值来调整注解的行为或配置,而不需要修改注解本身的实现。

    4. 简化配置:注解参数可以减少显式配置的需要,将配置信息直接嵌入到注解中,使得配置过程更加简洁和直观。例如,使用 @Cacheable(value = "books") 注解,可以直接指定缓存名称而无需额外的配置文件。

    5. 提升代码可读性:通过注解参数,开发人员可以在代码中直接表达意图和配置,增加了代码的自说明性。例如,@Transactional(timeout = 30) 明确指出了事务超时时间,使得代码的意图更容易理解。

  • 通过使用注解参数,可以使代码更加灵活、可维护,同时提高开发效率。

注解传参

  • 定义注解参数:

    • 注解可以定义参数,格式如下:

       类型 参数名() [default 默认值]
      

      注:默认值是可选的,如果不指定,使用注解时必须传递参数。

  • 单参数注解:

    • 如果注解只有一个参数,推荐将该参数命名为 value。使用时可以忽略参数名:

       public @interface AutoRunMethod {
           int value() default 1;
       }
      

      使用示例:

       @AutoRunMethod(3) // 使用了默认值1以外的参数值
       public void sayHello() {
           System.out.println("Hello!");
       }
      
    • 如果注解参数名不是 value,则在使用时需要指定参数名:

       public @interface AutoRunMethod {
           int age();
       }
      

      使用示例:

       @AutoRunMethod(age=3) // 指定了参数名
       public void sayHello() {
           System.out.println("Hello!");
       }
      
  • 多参数注解:

    • 注解可以定义多个参数。使用注解时每个参数都需要指定参数名:

       public @interface AutoRunMethod {
           int age() default 1;
           String name();
       }
      

      使用示例:

       @AutoRunMethod(age=2, name="张三")
       public void sayHello() {
           System.out.println("Hello!");
       }
      
    • 多参数的传递顺序可以与定义时的顺序不一致:

       @AutoRunMethod(name="张三", age=2)
       public void sayHello() {
           System.out.println("Hello!");
       }
      
  • 忽略参数名:

    • 当注解有多个参数时,即使其中一个参数名为 value,实际使用时也不可以忽略参数名:

       @AutoRunMethod(name="张三", value=2) // 正确
       @AutoRunMethod(value=2, name="张三") // 正确
       @AutoRunMethod(name="张三", 2)       // 不允许
       @AutoRunMethod(2, name="张三")       // 不允许
      
    • 如果某些参数有默认值,可以在使用时忽略这些参数:

       @AutoRunMethod(name="张三") // 省略了默认值参数
       public void sayHello() {
           System.out.println("Hello!");
       }
      

查询注解参数练习

 package day22;
 
 import day22.annotation.AutoRunMethod;
 
 import java.lang.reflect.Method;
 
 public class ReflectDemo12 {
     public static void main(String[] args) throws Exception {
         // 获取Person类上sayHello()方法的注解@AutoRunMethod中的参数值
         Class clazz = Class.forName("day22.Person");
         // 获取sayHello()方法
         Method method = clazz.getMethod("sayHello");
         // 判断该方法上是否有对应注解
         if (method.isAnnotationPresent(AutoRunMethod.class)) {
             // 通过反射对象获取该注解
             AutoRunMethod annotation = method.getAnnotation(AutoRunMethod.class);
             // 获取对应参数value的值
             System.out.println("注解参数的值为:" + annotation.value());
         }
     }
 }
 

在注解中创建成员变量(也称为注解参数)主要有以下几个目的:

  1. 提供附加信息:成员变量允许在使用注解时提供额外的元数据,这些信息可以被处理工具或框架用来执行特定的操作。例如,@Entity(name = "User") 注解中的 name 参数提供了实体的名称信息。

  2. 控制行为:成员变量可以用于控制注解的行为或配置。例如,@RequestMapping(method = RequestMethod.GET) 中的 method 参数指定了处理请求的HTTP方法类型。

  3. 增强灵活性:通过注解参数,可以使注解更具通用性和灵活性。注解的使用者可以根据需要传递不同的参数值来调整注解的行为或配置,而不需要修改注解本身的实现。

  4. 简化配置:注解参数可以减少显式配置的需要,将配置信息直接嵌入到注解中,使得配置过程更加简洁和直观。例如,使用 @Cacheable(value = "books") 注解,可以直接指定缓存名称而无需额外的配置文件。

  5. 提升代码可读性:通过注解参数,开发人员可以在代码中直接表达意图和配置,增加了代码的自说明性。例如,@Transactional(timeout = 30) 明确指出了事务超时时间,使得代码的意图更容易理解。

在注解中创建成员变量(也称为注解参数)主要有以下几个目的:

  1. 提供附加信息:成员变量允许在使用注解时提供额外的元数据,这些信息可以被处理工具或框架用来执行特定的操作。例如,@Entity(name = "User") 注解中的 name 参数提供了实体的名称信息。

  2. 控制行为:成员变量可以用于控制注解的行为或配置。例如,@RequestMapping(method = RequestMethod.GET) 中的 method 参数指定了处理请求的HTTP方法类型。

  3. 增强灵活性:通过注解参数,可以使注解更具通用性和灵活性。注解的使用者可以根据需要传递不同的参数值来调整注解的行为或配置,而不需要修改注解本身的实现。

  4. 简化配置:注解参数可以减少显式配置的需要,将配置信息直接嵌入到注解中,使得配置过程更加简洁和直观。例如,使用 @Cacheable(value = "books") 注解,可以直接指定缓存名称而无需额外的配置文件。

  5. 提升代码可读性:通过注解参数,开发人员可以在代码中直接表达意图和配置,增加了代码的自说明性。例如,@Transactional(timeout = 30) 明确指出了事务超时时间,使得代码的意图更容易理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值