详解Java注解机制

public class Child extends Base {
@Override
public void action(){
System.out.println(“child action”);
}
//action1方法在Base中不存在
@Override
public void action1(){
System.out.println(“child class”);
}
}

很明显当我们加上注解的瞬间,编译器就会直接提示异常,要求我们删除当前注解。从这可以看出来,@Override注解有助于帮助编译器检查语法错误,执行更严格的代码检查。

@Deprecated

@Deprecated注解可以修饰的范围很广,不仅可以作用在方法上,还可以修饰类、字段以及参数等所有注解可以修饰的范围,此注解代表被修饰的元素已经过时,并且警告使用者建议使用其他方法,不再使用当前方法。例如,Date类中就有很多方法被标识已经过时了:

@Deprecated
public Date(int year, int month, int date){

}

@Deprecated
public int getYear(){

}

当我们在使用这些方法的时候,ide往往会给我们加上一个删除线用来辅助提示调用者此方法已经过时,随时都可能在下个版本删除,不建议使用。而在Java9中,Deprecated注解又多了可填的属性,分别是sinceforRemoval,since属性类型为String类型,表示被修饰的元素从哪一个版本号开始过时,不建议使用,而forRemoval属性为Boolean类型,表示将来在过时的时候是否会删除当前元素,标记为true代表将来会删除此元素,使用者请慎用。而在Java9中,Integer包装类的构造方法中就有使用此特性标识的,如下:

@Deprecated(since=“9”)
public Integer(int value) {
this.value = value;
}

@SuppressWarnings

@SuppressWarnings注解则是表示压制Java中的编译警告措施,使得编译器放弃被修饰元素的编译警告,此注解有一个必填参数,表示压制的是哪种类型的警告,此注解也可以使用在几乎所有元素中。比如我们在开发中使用了Date类中的一些过期或者有异常抛出风险的方法,ide往往就会添加一个警告的黄线,而我们在方法上添加@SuppressWarnings注解,则会发现警告消失不见,如下:

//添加在方法上,取消掉编译器的严格检查警告机制
@SuppressWarnings({“deprecation”,“unused”})
public static void main(String[] args) {
Date date = new Date(2019, 10, 12);
int year = date.getYear();
}

常见的库中的注解

日常开发使用的库中也有着大量的注解,例如Jackson、SpringMvc等,下面就简单介绍下常见库中的常见注解使用

Jackson

Jackson是一个通用的序列化库,程序员使用过程中可以使用它提供的注解机制对序列化进行定制化操作,比如:

.使用@JsonIgnore和@JsonIgnoreProperties配置序列化的过程中忽略部分字段

.使用@JsonManagedReference和@JsonBackReference可以配置实例之间的互相引用

.使用@JsonProperty和@JsonFormat配置序列化的过程中字段名称和属性字段的格式等

Servlet3.0

随着web开发技术的发展,Java web已经发展到了Servlet3.0,在早期使用Servlet的时候,我们只能在web.xml中配置,但是当我们使用Servlet3.0的时候开始,已经开始支持注解了,比如我们可以使用@WebServlet配置一个类为Servlet类,如下:

@WebServlet(urlPatterns = “/async”, asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
//…
}

SpringMvc

同样的,在web开发中,我们往往还会使用SpringMvc框架来简化开发,其框架的大量注解可以帮助我们减少大量的业务代码,例如一个请求的参数和字段/实例之间的映射关系,一个方法使用的是Http的什么请求方法,对应请求的某个路径,同样的请求如何解析,返回的响应报文格式定义等,这些都可以使用注解来简化实现,一个简单的Mvc操作如下:

@Controller
@RequestMapping(“/hello”)
public class HelloController {

@GetMapping(“/test”)
@ResponseBody
public String test(){
return “hello test”;
}
}

其中@Controller注解标明当前的类是SpringMvc接管的一个Bean实例,@RequestMapping(“/hello”)则是代表当前Bean的前置请求路径比如是/hello开头, @GetMapping(“/test”)则是表示test方法被访问必须是Http请求的get请求,并且路径必须是/hello/test为路径前置,@ResponseBody注解则是标明了当前请求的相应信息按照默认的格式返回(根据后缀名来确定格式)

注解的创建

从上面我们可以看到使用注解的确可以很方便的简化我们开发过程,因此很多库和开发过程中,也会使用大量的注解简化开发,那么这些注解我们如何实现呢?首先我们先看看最常见的注解@Override的创建:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

从上面我们可以看到,Override类中使用了@Target元注解和@Retention元注解来定义整个注解,@Target表示需要注解的目标,@Retention则是标明当前注解的信息可以保留到Java的什么阶段,而除了这两个元注解以外,还有两个元注解@Documented和@Inherited,@Documented用来表示当前的注解信息是否包含到文档中,@Inherited注解则是和注解之间的继承有对应的关系,那么这些元注解具体有什么作用,以及具体有哪些参数可以选择呢?接下来我们便分别学习一下

@Target

@Target注解表示当前注解可以使用在什么类型的元素上,这里的值可以多选,即一个注解可以作用在多种不同类型的元素上,具体的可选值在ElementType枚举类中,值如下:

取值解释
TYPE表示作用在类、接口上
FIELD表示作用在字段,包括枚举常量中
METHOD表示作用在方法中
PARAMETER表示作用在方法中的参数中
CONSTRUCTOR表示作用在构造方法中
LOCAL_VARIABLE表示作用在本地常量中
MODULE表示作用在部分模块中(Java9引入的概念)
ANNOTATION_TYPE表示当前注解作用在定义其他注解中,即元注解
PACKAGE表示当前注解使用在包的申明中
TYPE_PARAMETER表明当前注解使用在类型参数的申明中(Java8新增)
TYPE_USE表明当前注解使用在具体使用类型中(Java8新增)

当使用多个作用域范围的时候,使用{}包裹多个参数,比如@SuppressWarnings注解的Target就有多个,在Java7中的定义为:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

即代表SuppressWarnings注解均可以作用在这七种元素上

@Retention

@Retention注解则是表明了当前注解可以保留到Java多个阶段的哪一个阶段,参数类型为RetentionPolicy枚举类,可取值如下:

取值解释
SOURCE此注解仅在源代码阶段保留,编译后即丢失注解部分
CLASS表示编译后依然保留在Class字节码中,但是加载时不一定会在内存中
RUNTIME表示不仅保留在Class字节码中,一直到内存使用时仍然存在

此注解有默认值,即当我们没有申明@Retention的时候,默认则是Class取值范围

@Documented

@Documented注解没有具体的参数,使用此元注解,则表示带有类型的注解将由javadoc记录

@Inherited

@Inherited注解与注解的继承有关系,具体关系为如果使用了当前的元注解,则表示此注解可以被其他的注解的子类直接继承,但是需要注意的是对已实现接口上的注解将没有作用。

我们通过一个案例来了解@Inherited注解的作用,首先我们定义一个Test注解:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

Test注解上有@Inherited元注解修饰,则表明Test注解会被继承,接着我们在Base类上使用Test注解:

@Test
public class Base {
}

这个时候实现一个子类Child,我们来通过反射类Class中的isAnnotationPresent方法打印,看此类中的Test是否存在且是来自于父类继承而来:

public class Child extends Base {
}

public static void main(String[] args) {
System.out.println(Child.class.isAnnotationPresent(Test.class));//true
}

最后输出的结果为true,则表明Child类中存在@Test注解,并且是由继承父类而来

使用注解实现简单定制序列化

上面我们已经学习了注解定义和创建相关的内容,接下来我们利用注解简单实现通用格式化输出的类SimpleFormatter,类中有一个方法format,方法参数为Object类型的实例对象,即表明了对象的序列化方式,类定义如下:

/**

  • 通用格式转换输出类
    /
    public class SimpleFormatter {
    /
    *
  • 通用格式化方法==>将obj对象输出为String
  • @param obj
  • @return
    */
    public static String format(Object obj){
    try{
    Class<?> cls = obj.getClass();
    StringBuilder builder = new StringBuilder();
    for (Field field : cls.getDeclaredFields()) {
    if(!field.isAccessible()){
    field.setAccessible(true);//放弃java安全检测,设置可以访问私有字段
    }
    //获取Label注解-输出的字段名称
    Label label = field.getAnnotation(Label.class);
    String name = null == label ? field.getName() : label.value();
    //获取字段对应的value
    Object value = field.get(obj);
    //如果是Date类型,走时间格式化
    if(null != value && field.getType() == Date.class){
    value = formatter(field,value);
    }
    builder.append(name + “?” + value + “\n”);
    }
    return builder.toString();

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

}
return builder.toString();

[外链图片转存中…(img-1KhCsTn0-1720130421246)]

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

[外链图片转存中…(img-vPPETWZp-1720130421247)]

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值