JDK基本注解介绍
基本注解包括
@Override
@Deprecated
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
1、@Override
官方解释:
指示方法声明旨在覆盖超类型中的方法声明。如果使用此注解类型对方法进行注解,则编译器需要生成错误消息,除非至少满足以下条件之一:
该方法确实覆盖或实现了在超类型中声明的方法。
该方法的签名与Object中声明的任何公共方法的签名等效。
public class Father {
public String msg(){
return "";
}
}
public class Son extends Father{
@Override
public String msg() {
return "";
}
}
- 如果将子类中方法名
msg
改为mg
会发生如下编译错误
> java: 方法不会覆盖或实现超类型的方法
- 所以
@Override
的作用告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否者就会出错,这样可以帮助程序员避免一些低级错误。
2、 @Deprecated
官方解释:
@Deprecated 注释的程序元素是不鼓励程序员使用的程序元素,通常是因为它很危险,或者因为存在更好的替代方案。当在非弃用代码中使用或覆盖弃用的程序元素时,编译器会发出警告。
- 通俗的说被该注解修饰的目标项是已经过时的了,不推荐使用的。**
public class Son extends Father{
@Deprecated
@Override
public String msg() {
return "";
}
}
- 使用
@Deprecated
修饰了Son中的msg
方法后,调用该方法会出现删除线和编译警告。
3、 @SuppressWarnings
官方解释:
指示应在带注释的元素(以及带注释的元素中包含的所有程序元素)中抑制命名的编译器警告。请注意,给定元素中抑制的警告集是所有包含元素中抑制的警告的超集。例如,如果您注释一个类以抑制一个警告并注释一个方法以抑制另一个警告,则两个警告都将在方法中被抑制。
作为风格问题,程序员应该始终在最有效的嵌套元素上使用此注释。如果您想在特定方法中抑制警告,您应该注释该方法而不是它的类。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
*编译器要在带注释的元素中抑制的一组警告。允许重复名称。名称的第二次和连续出现将被忽略。存在无法识别的警告名称不是错误:编译器必须忽略它们无法识别的任何警告名称。但是,如果注释包含无法识别的警告名称,它们可以自由发出警告。
字符串"unchecked"用于抑制未经检查的警告。编译器供应商应结合此注释类型记录他们支持的其他警告名称。鼓励他们合作以确保相同的名称在多个编译器中工作。
回报:
要抑制的警告集
*/
String[] value();
}
Java中的@SuppressWarnings
注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。
如果你对于代码的规范不做要求又对编译器的警告感到烦躁那么你可以使用@SuppressWarnings
(仅仅只是取消显示,并没有消除),它可以让你免去这些烦恼,当然编译器报错他是无法帮你取消显示的。
- 添加前:
- 添加后:
注解的使用有以下三种:
- 抑制单类型的警告:
@SuppressWarnings("unchecked")
- 抑制多类型的警告:
@SuppressWarnings("unchecked","rawtypes")
- 抑制所有类型的警告:
@SuppressWarnings("unchecked")
抑制警告的关键字如下表所示:
4、 @SafeVarargs
官方解释:
程序员断言带注释的方法或构造函数的主体不会对其 varargs 参数执行潜在的不安全操作。将此注释应用于方法或构造函数会抑制有关不可具体化的变量 arity (vararg) 类型的未经检查的警告,并抑制有关在调用站点创建参数化数组的未经检查的警告。
除了@Target元注解施加的使用限制外,编译器还需要对该注解类型实施额外的使用限制;如果使用@SafeVarargs注释对方法或构造函数声明进行注释,则这是编译时错误,并且:
声明是一个固定的arity方法或构造函数
声明是一个既不是static也不是final的变量 arity 方法。
鼓励编译器在将此注释类型应用于方法或构造函数声明时发出警告,其中:
变量 arity 参数具有可具体化的元素类型,包括原始类型、 Object和String 。 (对于可具体化的元素类型,此注释类型抑制的未经检查的警告已经不会出现。)
方法或构造函数声明的主体执行潜在的不安全操作,例如对变量 arity 参数数组的元素的赋值会生成未经检查的警告。一些不安全的操作不会触发未经检查的警告。例如,别名在
@SafeVarargs // 实际上并不安全!
static void m(List<String>... stringLists) {
Object[] array = stringLists;
List<Integer> tmpList = Arrays.asList(42);
array[0] = tmpList; // 语义上无效,但可以编译
String s = stringLists[0].get(0); // 哦不,运行时的 ClassCastException!
}
在运行时导致ClassCastException 。
该平台的未来版本可能会要求此类不安全操作出现编译器错误。
在学习@SafeVarargs
之前先来看看下面有一段代码
public class main {
public static void main(String[] args) {
display("10",20,30);
}
public static <T> void display(T ...array){
for (T arg : array){
System.out.println(arg.getClass().getName()+":"+arg);
}
}
}
这段代码中我设计了一个接收可变参数的方法public static void display(T ...array){}可变参数方法中的参数类型相同,为此声明参数是需要指定泛型。
但是调用可变参数方法时,应该提供相同类型的参数,但是代码中传入了不同类型的参数集合,此时可以看到display签名处有如下警告
翻译过来就是参数化可变参数类型可能造成的堆污染并且提示添加@SafeVarargs注解(仅仅起一个取消显示的作用,某种方面上来说是和 @SuppressWarnings 作用相同的)。
这个警告是 unchecked(未检查不安全代码),就是因为将非泛型变量赋值给泛型变量所发生的。
可以发现加上了@SafeVarargs注解后编译器警告没有显示了,你肯定会说我使用@SuppressWarnings效果也是一样的,效果虽然一样,但是两者相较来说这里使用@SafeVarargs注解更合适
注意:@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。
5、 @FunctionalInterface
官方解释:
一种信息性注解类型,用于指示接口类型声明旨在成为 Java 语言规范定义的功能接口。从概念上讲,函数式接口只有一个抽象方法。由于默认方法有一个实现,它们不是抽象的。如果接口声明了一个覆盖java.lang.Object的公共方法之一的抽象方法,这也不会计入接口的抽象方法计数,因为接口的任何实现都将具有来自java.lang.Object或其他地方的实现(接口的实现是类,所有类的父类都是Object)。
请注意,函数式接口的实例可以使用 lambda 表达式、方法引用或构造函数引用来创建。
如果使用此注解类型对类型进行注解,则编译器需要生成错误消息,除非:
该类型是接口类型,而不是注解类型、枚举或类。
带注解的类型满足功能接口的要求。
但是,无论接口声明中是否存在FunctionalInterface注释,编译器都会将满足功能接口定义的任何接口视为功能接口。
在学习Lambda表达式时,我们了解过函数式接口(接口中只有个一个抽象方法可以存在多个默认方法或多个static方法)。
@FunctionalInterface
作用就是用来指定某一个接口必须是函数式接口的,所以@FunctionalInterface
只能修饰接口。 这里我写了两个抽象方法出现了编译器报错
- 这里我只写了一个抽象方法 一个static方法和一个默认方法 符合要求没有报错
注意:如果接口声明了一个覆盖java.lang.Object
的公共方法之一的抽象方法,这也不会计入接口的抽象方法计数
得出结论:@FunctionalInterface
只是告诉编译器去检查这个接口是不是函数式接口,保证该接口只能包含一个抽象方法,否者就会出现编译错误。
写一个自己的注解
注解的格式就是
> - 元注解
> - public @interface 注解名
> - 注解内容体
@Documented //这里我希望我的注解能够生成在JavaDoc生成的文档中
@Retention(RetentionPolicy.CLASS) //这里我希望我的注解能够在字节码文件中保留
@Target({ElementType.TYPE,ElementType.METHOD}) //这里我希望我的注解可以修饰于类、接口、抽象类和方法上
public @interface MyAnnotation { //我声明了一个名为MyAnnotation的注解
//定义带两个成员变量的注解
//注解中的成员以方法的形式命名(注解的本质是接口) 并且可以带有默认值
String name() default "";
int age() default 0;
}
想要获取注解中的成员变量需要使用反射的知识、首先需要获取类的Class对象,我们就可以通过的这个Class对象反射得到注解的成员变量了。
反射相关的可以参考这篇博客反射
public class main {
public static void main(String[] args) throws NoSuchMethodException {
new Test().testMyAnnotation();
}
}
class Test{
@MyAnnotation(name = "dyw",age = 20)//这里我们使用了我们刚才自定义的注解
public void testMyAnnotation() throws NoSuchMethodException {
//这里获取Test类的Class对象 当类被编译后会当jvm加载时会生成该class文件的Class对象 通过这个Class对象可以完成反射相关的操作
Method testMyAnnotation = this.getClass().getMethod("testMyAnnotation");
//通过Class对象的getAnnotation()方法反射得到我们的MyAnnotation对象
MyAnnotation annotation = testMyAnnotation.getAnnotation(MyAnnotation.class);
//通过该对象可以获取注解类的成员
System.out.println("name :"+annotation.name());
System.out.println("age :"+annotation.age());
}
}
输出: