Java 注解入门 元注解 注解解析 kaki的博客

Java 注解 注解解析 kaki的博客

  • 1. 注解的概述

  • 注解是JDK1.5的新特性。

  • 注解相当⼀一种标记,是类的组成部分,可以给类携带⼀一些额外的信息。

  • 标记(注解)可以加在包,类,字段,⽅方法,⽅方法参数以及局部变量量上。

  • 注解是给编译器器或JVM看的,编译器器或JVM可以根据注解来完成对应的功能。

注解 (Annotation) 相当于⼀一种标记,在程序中加⼊入注解就等于为程序打上某种标记,以后, javac 编译器器、开发⼯工具和其他程序可以通过反射来了了解你的类及各种元素上有⽆无何种
标记,看你的程 序有什什么标记,就去⼲干相应的事,标记可以加在包、类,属性、⽅方法,⽅方法的参数以及局部变量量 上

2.注解的作用

注解的作用就是给程序带入参数。
以下⼏几个常⽤用操作中都使⽤用到了了注解:

  • 编译检查:@Override
    @Override:⽤用来修饰⽅方法声明
    用来告诉编译器器该⽅方法是重写父类中的⽅方法,如果父类不不存在该方法,则编译失败。如 下图
    在这里插入图片描述
  • 框架的配置(框架=代码+配置)

3.常见注解

@author:⽤用来标识作者名,eclipse开发⼯工具默认的是系统⽤用户名。
@version:⽤用于标识对象的版本号,适⽤用范围:⽂文件、类、⽅方法。
@Override :⽤用来修饰⽅方法声明,告诉编译器器该⽅方法是重写⽗父类中的⽅方法,如果⽗父类不不存在该⽅方 法,则编译失败。

4.自定义注解

  • 定义格式
public @interface 注解名{

} 
如:定义⼀一个名为Student的注解 

public @interface Student {

} 

以上定义出来的注解就是⼀一个最简单的注解了,但这样的注解意义不大,因为注解中没有任何内 容,就好像我们定义⼀一个类而这个类中没有任何成员变量量和方法⼀样,这样的类意义也是不大的, 所以在定义注解时会在⾥里里⾯面添加⼀一些成员来让注解功能更加强⼤大,这些成员就是属性。接下来就看 看怎么给注解添加属性。

5.注解的属性

  • 属性的作用
    • 可以让用户在使用注解时传递参数,让注解的功能更加强⼤
  • 属性的格式
    • 格式1:数据类型 属性名();
    • 格式2:数据类型 属性名() default 默认值;
  • 属性定义示例
    在这里插入图片描述
  • 属性适用的数据类型
    • 八种基本数据类型(int,float,boolean,byte,double,char,long,short)
    • String类型,Class类型,枚举类型,注解类型
    • 以上所有类型的⼀维数组

6.使用自定义注解

  • 定义注解
    1.定义⼀一个注解:Book
           包含属性:String value() 书名
           包含属性:double price() 价格,默认值为 100
           包含属性:String[] authors() 多位作者
public @interface Book {        
// 书名        
String value();        
// 价格        
double price() default 100;        
// 多位作者        
String[] authors(); 

}

7.使用注解

  • 定义类在成员⽅方法上使⽤用Book注解
/** 
  * @author kaki
  * @version 1.0 
  * @description 书架类 
  * @date 2018/1/26 
*/ 
public class BookShelf {      
    @Book(value = "西游记",price = 998,authors = {"吴承恩","白求恩"})    
    public void showBook(){
         
    } 

}
  • 使用注意事项

8.特殊属性value

  • 当注解中只有⼀一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论 value是单值元素还是数组类型
// 定义注解Book 
public @interface Book {        
     // 书名
     String value(); 
}
// 使⽤用注解Book 
public class BookShelf {    
	
	@Book("西游记")    
	public void showBook(){
	    
    }
     
}public class BookShelf {    

    @Book(value="西游记")    
    public void showBook(){
    
    } 

}

  • 如果注解中除了value属性还有其他属性,且至少有⼀一个属性没有默认值,则在使⽤用注解给属性赋 值时,value属性名不不能省略。

在这里插入图片描述

9.问题分析

现在我们已经学会了如何定义注解以及如何使用注解了了,可能细⼼心的同学会发现⼀一个问题:我们定义的 注解是可以使⽤用在任何成员上的,比如刚刚Book注解的使用:

在这里插入图片描述

  1. 此时Book同时使用在了了类定义上或成员方法上,编译器也没有报错,因为默认情况下,注解可以 用在任何地方,比如类,成员方法,构造方法,成员变量等地⽅方
  2. 如果要限制注解的使⽤用位置怎么办?那就要学习⼀一个新的知识点:元注解。接下来就来看看什么是 元注解以及怎么使用

10. 注解之元注解

  • 元注解的概述
    1.Java API提供的注解
    2.专门用来定义注解的注解。
    3.任何Java官⽅方提供的非元注解的定义中都使⽤用到了元注解。

  • 常用元注解
    @Target
    @Retention

  • 元注解之@Target

    • 作用:指明此注解用在哪个位置,如果不写默认是任何地⽅方都可以使用。
    • 可选的参数值在枚举类ElemenetType中包括

TYPE: 用在类,接口上
FIELD:用在成员变量上
METHOD: 用在方法上
PARAMETER:用在参数上
CONSTRUCTOR:用在构造方法上
LOCAL_VARIABLE:用在局部变量上

  • 元注解之@Retention
    • 作用:定义该注解的生命周期(有效范围)。
      • 可选的参数值在枚举类型RetentionPolicy中包括

SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了。
CLASS:注解存在于Java源代码、编译以后的字节码文件中,运⾏行的时候内存中没有,默认 值。
RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以 通过反射获取该注解

  • 元注解使用示例

例 1在这里插入图片描述
例 2在这里插入图片描述

11.注解解析

  • 什么是注解解析 ?

    通过Java技术获取注解数据的过程则称为注解解析

  • 与注解解析相关的接口
    Anontation:所有注解类型的公共接口,类似所有类的父类是 Object。

    AnnotatedElement:定义了了与注解解析相关的⽅方法,常⽤用⽅方法以下四个:

boolean  isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注 解,有则返回true,否则返回false。
T   getAnnotation(Class annotationClass); 获得当前对象上指定的注解对象。
Annotation[]  getAnnotations(); 获得当前对象及其从⽗父类上继承的所有的注解对象
Annotation[]   getDeclaredAnnotations();**获得当前对象上所有的注解对象,不不包括⽗父类 的。

  • 获取注解数据的原理
    注解作用在那个成员上,就通过反射获得该成员的对象来得到它的注解。
          如注解作用在方法上,就通过方法(Method)对象得到它的注解
    在这里插入图片描述
          如注解作⽤用在类上,就通过Class对象得到它的注解
    在这里插入图片描述

  • 使用反射获取注解的数据
    需求说明
    1. 定义注解Book,要求如下:

    • 包含属性:String value() 书名
    • 包含属性:double price() 价格,默认值为 100
    • 包含属性:String[] authors() 多位作者
    • 限制注解使⽤用的位置:类和成员⽅方法上
    • 指定注解的有效范围:RUNTIME

    2. 定义BookStore类,在类和成员⽅方法上使⽤用Book注解
    3. 定义TestAnnotation测试类获取Book注解上的数据
    代码实现
    1. 注解Book
    在这里插入图片描述
    2. BookStore类
    在这里插入图片描述
    3. TestAnnotation类

/** 
  * @author kaki
  * @version 1.0 
  * @description com.kaki.annotation 
  * @date 2018/1/26 
  */ 
public class TestAnnotation {    
     public static void main(String[] args)  throws Exception{        
         System.out.println("---------获取类上注解的数据----------");        
         test01();        
         System.out.println("---------获取成员⽅方法上注解的数据----------");        
         test02();    
}
    /**     
      * 获取BookStore类上使⽤用的Book注解数据     
      */    
     public static void test01(){        
	     // 获得BookStore类对应的Class对象        
	     Class c = BookStore.class;        
	     // 根据注解Class对象获取注解对象        
	     Book book = (Book) c.getAnnotation(Book.class); 
	     // 输出book注解属性值                	        
         System.out.println("书名:" + book.value()); 
         System.out.println("价格:" + book.price());        
         System.out.println("作者:" + Arrays.toString(book.authors()));            
   
     }
    /**     
      * 获取BookStore类成员⽅方法buyBook使用的Book注解数据       */    
     public static void test02() throws Exception{       
	      // 获得BookStore类对应的Class对象        
	      Class c = BookStore.class;       
	      // 获得成员⽅方法buyBook对应的Method对象      
	      Method m = c.getMethod("buyBook");        
	      // 根据注解Class对象获取注解对象        
	      Book book = (Book) m.getAnnotation(Book.class);        // 输出book注解属性值        
          
          System.out.println("书名:" + book.value());         
          System.out.println("价格:" + book.price());   
          System.out.println("作者:" +  Arrays.toString(book.authors()));   
      } 
}

输出结果
在这里插入图片描述
存在问题分析

  • TestAnnotation类在获取注解数据时处理得不够严谨,假如出现下面的其中⼀种情况:
      1. 把BookStore类或成员⽅方法buyBook上的注解删除。
      2. 将Book注解的有效范围改为:CLASS。
    再运行TestAnnotation类代码则会出现空指针异常,如下图所示:
    在这里插入图片描述
    原因分析如下图
    在这里插入图片描述
    在这里插入图片描述

     解决方案

  • 在获取注解对象时,先判断是否有使⽤用注解,如果有,才获取,否则就不用获取
  • 修改TestAnnotation类的代码,修改后如下
public class TestAnnotation {    

	public static void main(String[] args)  throws Exception{   
	
		System.out.println("---------获取类上注解的数据----------");        
		test01();     
		
		System.out.println("---------获取成员方法上注解的数据----------");        
		test02();    
	}
	
    /**     
	  * 获取BookStore类上使用的Book注解数据     
	  */    
	public static void test01(){   
	
	    // 获得BookStore类对应的Class对象        
		Class c = BookStore.class;        
		
		// 判断BookStore类是否使用了Book注解        
		if(c.isAnnotationPresent(Book.class)) { 
		
			// 根据注解Class对象获取注解对象            
			Book book = (Book) c.getAnnotation(Book.class);  
			
			// 输出book注解属性值            
			System.out.println("书名:" + book.value());            
			System.out.println("价格:" + book.price());            
			System.out.println("作者:" + Arrays.toString(book.authors()));        
		}    
	}
	
    /**     
	  * 获取BookStore类成员方法buyBook使⽤用的Book注解数据     
	  */    
	public static void test02() throws Exception{        
	    // 获得BookStore类对应的Class对象
        Class c = BookStore.class;       
		
		// 获得成员方法buyBook对应的Method对象   		
		Method m = c.getMethod("buyBook");        
		
		// 判断成员方法buyBook上是否使用了Book注解        
		if(m.isAnnotationPresent(Book.class)) {   
		
			// 根据注解Class对象获取注解对象            
			Book book = (Book) m.getAnnotation(Book.class);    
			
			// 输出book注解属性值            
			System.out.println("书名:" + book.value());           
			System.out.println("价格:" + book.price());           
			System.out.println("作者:" + Arrays.toString(book.authors()));       
		}   
	} 
}

欢迎点赞关注收藏哦 ,码云搜索KakiNakajima

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章