「这是我参与2022首次更文挑战的第34天,活动详情查看:2022首次更文挑战」。
概述
从JDK 5.0开始,Java增加了对元数据(MetaData)的支持,也就是==Annotation(注解)==。
在Java核心技术第2卷中对注解这样定义:
注解是那些插入到源代码中使用其他工具可以对其进行处理的标签。这些工具可以在源码层次上进行操作,或者可以处理编译器在其中放置了注解的类文件。 注解不会改变程序的编译方式。Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令。
注解其实就是代码里的特殊标记,这些标记可以被读取并执行相应的处理。
注解的一些可能的用法有:
- 附属文件的自动生成,例如部署描述符或者bean信息类;
- 测试、日志、事务语义等代码的自动生成。
注解是一种趋势,从某种角度来说:框架 = 注解 + 反射 + 设计模式。
Annotation注解示例
1、生成文档相关的注解
- @author 标明开发该类模块的作者,多个作者之间用逗号分割
- @date 标明开发该类模块的时间
- @version 标明该类模块的版本
- @see 参考转向,也就是相关主题
- @since
- 从哪个版本开始增加的
- @param 对方法中参数的说明,如果没有参数就不能写(可以并列多个)
- @return 对方法返回值的说明,如果方法的返回值是void就不能写
- @exception 对方法可能抛出的异常的说明,如果方法没有用throws显示抛出异常就不能写(可以并列多个)
/**
* @Description 这是注解的一个实例类
* @author 1+1 =王
* @date 2022/2/22 19:10
* @since version 2.0
*/
public class AnnotationTest {
/**
* @Description 获取指定两个数之和
* @param a
* @param b
* @return 两数之和
*/
public int add(int a, int b){
return a + b;
}
}
2、在编译时进行格式检查
JDK中内置的三个基本注解
- @Override 限定重写父类方法,只能用于方法
- @Deprecated 用于表示所修饰的类或方法已过时
- @SuppressWarnings 用于抑制编译器的警告
3、跟踪代码依赖性,实现替代配置文件功能
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.java.code.Servlet<servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
<servlet-mapping>
用注解代替配置文件
package com.java.code.Servlet;
import com.java.code.Jdbc.StudentHomeworkJdbc;
import com.java.code.Model.User;
import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
User user = new User();
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+password);
if(StudentHomeworkJdbc.selectByUsernameAndPassword(username,password) == 2){
req.getRequestDispatcher("/jsp/login.jsp").forward(req,resp);
}else {
if(StudentHomeworkJdbc.selectByUsernameAndPassword(username,password) == 0){
req.setAttribute("username",username);
req.getRequestDispatcher("/jsp/usualUser.jsp").forward(req,resp);
}else{
req.getRequestDispatcher("/jsp/manager.jsp").forward(req,resp);
}
}
}
}
自定义Annotation注解
创建一个注解类
==注解声明为@interface(注:这与interface接口没有任何关系)==
public @interface MyAnnotation {
}
内部定义成员通常用value表示
public @interface MyAnnotation {
String value();
}
使用
@MyAnnotation(value = "Hello")
public int add(int a, int b){
return a + b;
}
可以指定成员的默认值,使用default定义
public @interface MyAnnotation {
String value() default "Hello";
}
==如果自定义注解没有成员,表明是一个标识;如果注解有成员,在使用注解时必须指定成员的值。==
注
- 定义新的注解类型使用@interface
- 自定义注解自动继承java.lang.annotation.Annotation接口
- 注解的成员变量类型可以是8中基本数据类型、String类型、Class类型、Enum类型、Annotation类型或以上类型的数组。
- 自定义注解必须配上注解的信息处理流程(使用反射)才有意义
JDK中的元注解
元注解用于修饰其他Annotation的定义,它是对现有的注解进行解释说明的注解。
4个元注解:@Retention、@Target、@Documented、@Inherited
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。
进入@Retention注解源码看到它只有一个RetentionPolicy类型的成员。
然后再进入RetentionPolicy中可以看到,他是一个枚举类型,包括三个值(SOURCE、CLASS、RUNTIME)。
三个值分别表示的意义如下:
- RetentionPolicy.SOURCE:在源文件中有效,编译器直接丢弃这种策略的注释。
- RetentionPolicy.CLASS:在class文件中有效,当运行java程序时,JVM不会保留注释。 RetentionPolicy.RUNTIME:在运行时有效,当运行java程序时,JVM会保留注释。程序可以通过反射获取该注释。
- @Target:用于描述注解的适用范围(即可以用在什么地方)
进入@Target注解源码看到它只有一个ElementType数组类型的成员。
ElementType也是一种枚举类型:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
- @Documented:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
通过反射获取注解信息
@MyAnnotation(value = "hello")
class User{
private Integer id;
private String name;
}
public class AnnotationTest {
public static void main(String[] args) {
Class clazz = User.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annotation:annotations){
System.out.println(annotation);
}
}
}
JDK8中注解的新特性
可重复注解
如果我们需要定义重复注解,就必须给它定义容器类,还要使用 @Repeatable 注解修饰。
@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
/**
* 容器类
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
RepetitionAnnotation[] value();
}
测试重复定义注解
public class AnnotationTest {
/**
* @Description 获取指定两个数之和
* @param a
* @param b
* @return 两数之和
*/
@MyAnnotation(value = "Hello")
@MyAnnotation(value = "你好")
public int add(int a, int b){
return a + b;
}
}
类型注解
向 @Target 中添加两种类型 TYPE_PARAMETER和TYPE_USE。