java注解(Annotation)

17 篇文章 0 订阅

1.描述

  • 官方:注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。Java 注解是从 Java5 开始添加到 Java 的。
  • 自述:代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。

2.定义

注解通过@interface关键字进行定义。

public @interface TestAnnotation {
}
  • 上面的代码就创建了一个名字为 TestAnnotaion 的注解。

3.元注解

  • 元注解是注解到注解上的注解,或者说元注解是一种基本注解。
  • 元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

3.1@Retention

	Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
我们指定 TestAnnotation 可以在程序运行周期被获取到,因此它的生命周期非常的长。

3.2@Documented

作用是能够将注解中的元素包含到 Javadoc 中去。

3.3@Target

Target 是目标的意思,@Target 指定了注解运用的地方。比如方法、变量、包、接口等等。
  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

3.4@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 `@Inherited` 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
Public class A {}
Public class B extends A {}
注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

3.5@Repeatable

Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
@interface Persons {
    Person[]  value();
}

@Repeatable(Persons.class)
@interface Person{
    String role default "";
}

@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
Public class SuperMan{
}
注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
容器注解:存放其他注解的地方。它本身也是一个注解。

4.注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
Public @interface TestAnnotation {
    int id();
    String msg();
}

上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。

赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。

需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    public int id() default -1;
    public String msg() default "Hi";
}

TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。
它可以这样应用。

@TestAnnotation()
public class Test {}

如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。

public @interface Check {
    String value();
}
@Check(value="hi")
int a;

注解没有任何属性

public @interface Perform {}

括号都可以省略。

@Perform
public void testMethod(){}

5.Java 内置的注解

Java 语言本身已经提供了几个现成的注解。

5.1 @Deprecated

这个元素是用来标记过时的元素,想必大家在日常开发中经常碰到。
编译器在编译阶段遇到这个注解时会发出提醒警告,
告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量。

5.2@Override

提示子类要复写父类中被 @Override 修饰的方法

5.3@SuppressWarnings

调用被 @Deprecated 注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过 @SuppressWarnings 达到目的。

@SuppressWarnings("deprecation")
public void test1(){
    Hero hero = new Hero();
    hero.say();
    hero.speak();
}

5.4@SafeVarargs

在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全的操作,可使用@SafeVarargs进行标记,这样的话,Java编译器就不会报unchecked警告。

5.5@FunctionalInterface

函数式接口注解
  • 函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。
  • 这个接口里面只能有一个抽象方法。
  • 它们主要用在Lambda表达式和方法引用(实际上也可认为是Lambda表达式)上。

6.注解的提取

java源码级编译器
上图中的【注解抽象语法树】会去解析注解,然后做处理逻辑,如果注解中@Retention设置为RetentionPolicy.SOURCERetentionPolicy.CLASS那么就需要继承并实现,否则加载到jvm就会被抹除

那么是怎样提取注解呢?答案是通过反射来提取注解

6.1注解与反射

  1. 通过反射获取Class对象
Class clazz = Class.forName("类");
  1. 判断Class对象是否使用注解

     通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
    
  2. 通过 getAnnotation() 方法来获取 Annotation 对象。或者同过getAnnotations()方法获取所有注解

//获取类的注解
if(clazz.isAnnotationPresent(Test.class)){
                Desc classDesc = (Desc)clazz.getAnnotation(Test.class);
                System.out.println(classTest.value());
//获取一个成员变量上的注解            }
Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            Check check = a.getAnnotation(Check.class);
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
// 获取方法中的注解
Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) { 
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
需要注意的是,如果一个注解要在运行时被成功提取,那么 @Retention(RetentionPolicy.RUNTIME) 是必须的。

7.注解应用实例

7.1JUnit 测试框架

public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }
}
@Test 标记了要进行测试的方法 addition_isCorrect().

7.2mybatis中的@Param()注解

@select( "select * from user")
List<User> getUsers();
//方法存在多个参数,所有的参数前面必须加上 @Param( "id")注解
@select("select * from user where id = #{id}")
User getUserByID(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
int adduser(User user);
@Update("update user set name=#{name},pwd=#{password} where id = #{id}")
int updateUser(User user);
@Delete( "delete from user where id = {uid}")
int deleteUser(@Param( "uid") int id);

通过@Param来标识成员变量id,在解析时可以通过标识来进行数据逻辑操作。
关于@Param()注解

  • 方法存在多个参数,所有的参数前面必须加上 @Param( “id”)注解,如果不加通过反射得到的数据就没有意义。

  • 基本类型的参数或者String类型,需要加上

  • 引用类型不需要加

  • 如果只有一个基本类型的话,可以忽略,但是建议都加上!

      我们在SQL中引用的就是我们这里的@Param()中设定的属性名!
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值