跳到主要内容

Java 内置注解

什么是Java内置注解?

Java内置注解是Java语言本身提供的、已经定义好的注解,开发者可以直接在代码中使用这些注解,而无需自行定义。这些注解为编译器提供了额外的信息,帮助进行代码检查,或者在运行时提供特定的处理逻辑。

内置注解是Java注解体系的基础部分,了解并正确使用这些注解能够提高代码的质量和可维护性。

Java 常用内置注解

Java提供了几种重要的内置注解,这些注解在日常编程中经常被使用:

1. @Override

@Override注解用于标记一个方法是重写(覆盖)了父类中的方法。

java
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}

public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
提示

使用@Override注解的好处:

  1. 编译器会检查被注解的方法是否真的重写了父类的方法
  2. 如果拼写错误或方法签名不匹配,编译器会报错
  3. 提高代码可读性,明确表示这是一个重写方法

2. @Deprecated

@Deprecated注解表示被标记的元素(类、方法、字段等)已过时,不推荐使用。

java
public class Calculator {
/**
* @deprecated 此方法存在精度问题,请使用 {@link #divideExact(double, double)} 代替
*/
@Deprecated
public double divide(int a, int b) {
return a / b;
}

public double divideExact(double a, double b) {
return a / b;
}
}

使用@Deprecated注解的元素在IDE中通常会显示为删除线,并在编译时生成警告。

备注

从Java 9开始,@Deprecated注解增加了两个属性:

  • forRemoval:表示该API在未来版本是否会被移除
  • since:表示从哪个版本开始被废弃

3. @SuppressWarnings

@SuppressWarnings注解用于抑制编译器警告。当你确定某个警告可以忽略时,可以使用此注解。

java
public class WarningExample {
@SuppressWarnings("unchecked")
public List<String> createLegacyList() {
// 使用原始类型,通常会产生未检查的类型转换警告
List list = new ArrayList();
list.add("Hello");
list.add("World");
return list;
}

@SuppressWarnings({"unused", "deprecation"})
public void multipleSuppress() {
// 抑制多种类型的警告
// ...
}
}

常见的警告类型包括:

  • unchecked: 未检查的类型转换
  • deprecation: 使用了废弃的API
  • unused: 未使用的变量
  • rawtypes: 使用了原始类型
  • resource: 未关闭的资源
  • all: 抑制所有警告
警告

虽然@SuppressWarnings可以消除编译警告,但应该谨慎使用。警告通常表示可能存在的问题,不应该无理由地忽略它们。

4. @SafeVarargs

@SafeVarargs注解用于抑制与可变参数相关的未检查警告。它只能应用于无法被覆盖的方法(如final方法、静态方法或构造函数)。

java
public class SafeVarargsExample {
@SafeVarargs
public final <T> List<T> asList(T... elements) {
List<T> result = new ArrayList<>();
for (T element : elements) {
result.add(element);
}
return result;
}
}

5. @FunctionalInterface

@FunctionalInterface注解表示一个接口是函数式接口,即只包含一个抽象方法的接口。这样的接口可以使用Lambda表达式来实现。

java
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);

// 允许定义默认方法
default void printInfo() {
System.out.println("这是一个计算器接口");
}

// 允许定义静态方法
static Calculator addition() {
return (a, b) -> a + b;
}
}

// 使用Lambda表达式实现函数式接口
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
Calculator add = (a, b) -> a + b;
Calculator subtract = (a, b) -> a - b;

System.out.println("10 + 5 = " + add.calculate(10, 5));
System.out.println("10 - 5 = " + subtract.calculate(10, 5));
}
}

输出:

10 + 5 = 15
10 - 5 = 5
备注

如果一个接口被@FunctionalInterface注解标记,但它包含多个抽象方法,编译器会报错。

Java 元注解

元注解是用来注解其他注解的注解。Java提供了几个重要的元注解:

1. @Retention

@Retention注解指定被标记的注解的保留策略,即注解在什么级别可用。

java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}

RetentionPolicy有三个可能的值:

  • SOURCE: 注解仅在源代码中保留,编译时会被丢弃
  • CLASS: 注解在class文件中可用,但会被JVM忽略(默认值)
  • RUNTIME: 注解在运行时可用,可以通过反射API访问

2. @Target

@Target注解指定被标记的注解可以应用的元素类型。

java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
String value() default "";
}

常见的ElementType值包括:

  • TYPE: 类、接口、枚举
  • FIELD: 字段
  • METHOD: 方法
  • PARAMETER: 方法参数
  • CONSTRUCTOR: 构造函数
  • LOCAL_VARIABLE: 局部变量
  • ANNOTATION_TYPE: 注解类型
  • PACKAGE: 包

3. @Documented

@Documented注解表示被标记的注解应该包含在JavaDoc中。

java
import java.lang.annotation.Documented;

@Documented
public @interface MyAnnotation {
String value() default "";
}

4. @Inherited

@Inherited注解表示被标记的注解可以被子类继承。

java
import java.lang.annotation.Inherited;

@Inherited
public @interface MyAnnotation {
String value() default "";
}

5. @Repeatable

@Repeatable注解(Java 8引入)表示被标记的注解可以在同一个声明上多次使用。

java
import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}

public @interface Schedules {
Schedule[] value();
}

// 使用可重复注解
public class RepeatableDemo {
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour=23)
public void doPeriodicCleanup() {
// ...
}
}

实际应用案例

案例1:使用@Override确保方法正确重写

java
public class DatabaseConnector {
public boolean connect(String url, String username, String password) {
System.out.println("使用标准连接方式连接到数据库");
return true;
}
}

public class SecureDatabaseConnector extends DatabaseConnector {
@Override
public boolean connect(String url, String username, String password) {
System.out.println("使用加密连接方式连接到数据库");
// 实现安全连接逻辑
return true;
}

// 如果方法签名写错,比如下面这样,编译器会报错
// @Override
// public boolean connect(String url, String userName, String password) { ... }
}

案例2:使用@Deprecated标记过时API并提供替代方案

java
public class UserService {
/**
* @deprecated 此方法不安全,请使用 {@link #authenticateUser(String, char[])} 代替
*/
@Deprecated(since="2.0", forRemoval=true)
public boolean authenticateUser(String username, String password) {
// 旧的实现
return true;
}

public boolean authenticateUser(String username, char[] password) {
// 更安全的实现,密码作为char数组可以在使用后清除
// ...
return true;
}
}

案例3:使用@FunctionalInterface实现策略模式

java
@FunctionalInterface
interface PaymentStrategy {
void pay(double amount);
}

public class PaymentProcessor {
public void processPayment(double amount, PaymentStrategy strategy) {
strategy.pay(amount);
}

public static void main(String[] args) {
PaymentProcessor processor = new PaymentProcessor();

// 使用Lambda表达式实现不同的支付策略
PaymentStrategy creditCard = amount ->
System.out.println("使用信用卡支付 $" + amount);

PaymentStrategy paypal = amount ->
System.out.println("使用PayPal支付 $" + amount);

PaymentStrategy bankTransfer = amount ->
System.out.println("使用银行转账支付 $" + amount);

processor.processPayment(100.50, creditCard);
processor.processPayment(50.75, paypal);
processor.processPayment(250.00, bankTransfer);
}
}

输出:

使用信用卡支付 $100.5
使用PayPal支付 $50.75
使用银行转账支付 $250.0

总结

Java内置注解是Java语言提供的强大工具,可以帮助开发者:

  1. 通过@Override确保方法正确重写父类方法
  2. 使用@Deprecated标记过时API并提供替代方案
  3. 利用@SuppressWarnings抑制特定的编译器警告
  4. 通过@FunctionalInterface定义函数式接口,支持Lambda表达式
  5. 使用@SafeVarargs处理可变参数类型安全问题

元注解则允许开发者创建自定义注解时控制其行为:

  1. @Retention决定注解的保留策略
  2. @Target指定注解可应用的元素类型
  3. @Documented表示注解应包含在JavaDoc中
  4. @Inherited允许注解被子类继承
  5. @Repeatable使注解可以在同一元素上重复使用

正确理解和使用这些内置注解,可以提高代码质量、可读性和可维护性,是Java开发者的基本技能。

练习

  1. 创建一个Vehicle基类和Car子类,在子类中正确使用@Override注解重写父类的start()方法。
  2. 创建一个带有已废弃方法的类,使用@Deprecated注解并提供适当的替代方法。
  3. 定义一个符合@FunctionalInterface要求的接口,并使用Lambda表达式实现它。
  4. 创建一个使用@SuppressWarnings的例子,合理抑制一些警告。

扩展阅读