Java 内置注解
什么是Java内置注解?
Java内置注解是Java语言本身提供的、已经定义好的注解,开发者可以直接在代码中使用这些注解,而无需自行定义。这些注解为编译器提供了额外的信息,帮助进行代码检查,或者在运行时提供特定的处理逻辑。
内置注解是Java注解体系的基础部分,了解并正确使用这些注解能够提高代码的质量和可维护性。
Java 常用内置注解
Java提供了几种重要的内置注解,这些注解在日常编程中经常被使用:
1. @Override
@Override
注解用于标记一个方法是重写(覆盖)了父类中的方法。
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
使用@Override注解的好处:
- 编译器会检查被注解的方法是否真的重写了父类的方法
- 如果拼写错误或方法签名不匹配,编译器会报错
- 提高代码可读性,明确表示这是一个重写方法
2. @Deprecated
@Deprecated
注解表示被标记的元素(类、方法、字段等)已过时,不推荐使用。
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
注解用于抑制编译器警告。当你确定某个警告可以忽略时,可以使用此注解。
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
: 使用了废弃的APIunused
: 未使用的变量rawtypes
: 使用了原始类型resource
: 未关闭的资源all
: 抑制所有警告
虽然@SuppressWarnings可以消除编译警告,但应该谨慎使用。警告通常表示可能存在的问题,不应该无理由地忽略它们。
4. @SafeVarargs
@SafeVarargs
注解用于抑制与可变参数相关的未检查警告。它只能应用于无法被覆盖的方法(如final方法、静态方法或构造函数)。
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表达式来实现。
@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
注解指定被标记的注解的保留策略,即注解在什么级别可用。
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
注解指定被标记的注解可以应用的元素类型。
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中。
import java.lang.annotation.Documented;
@Documented
public @interface MyAnnotation {
String value() default "";
}
4. @Inherited
@Inherited
注解表示被标记的注解可以被子类继承。
import java.lang.annotation.Inherited;
@Inherited
public @interface MyAnnotation {
String value() default "";
}
5. @Repeatable
@Repeatable
注解(Java 8引入)表示被标记的注解可以在同一个声明上多次使用。
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确保方法正确重写
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并提供替代方案
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实现策略模式
@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语言提供的强大工具,可以帮助开发者:
- 通过
@Override
确保方法正确重写父类方法 - 使用
@Deprecated
标记过时API并提供替代方案 - 利用
@SuppressWarnings
抑制特定的编译器警告 - 通过
@FunctionalInterface
定义函数式接口,支持Lambda表达式 - 使用
@SafeVarargs
处理可变参数类型安全问题
元注解则允许开发者创建自定义注解时控制其行为:
@Retention
决定注解的保留策略@Target
指定注解可应用的元素类型@Documented
表示注解应包含在JavaDoc中@Inherited
允许注解被子类继承@Repeatable
使注解可以在同一元素上重复使用
正确理解和使用这些内置注解,可以提高代码质量、可读性和可维护性,是Java开发者的基本技能。
练习
- 创建一个
Vehicle
基类和Car
子类,在子类中正确使用@Override
注解重写父类的start()
方法。 - 创建一个带有已废弃方法的类,使用
@Deprecated
注解并提供适当的替代方法。 - 定义一个符合
@FunctionalInterface
要求的接口,并使用Lambda表达式实现它。 - 创建一个使用
@SuppressWarnings
的例子,合理抑制一些警告。