Java基础——注解(1)

本文深入讲解Java注解的概念及其用途,包括基础注解的使用、@Override与@Deprecated的作用及意义,以及如何利用@SafeVarargs和@FunctionalInterface解决实际编程问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注解其实是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序开发人员可以在不改变原有逻辑的情况下,在源文件当中嵌入一些补充的信息。代码分析工具、开发工具、和部署工具可以通过这些补充信息进行验证或者进行部署。

注解可以被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是,注解不影响程序代码的执行,无论增加、删除注解,代码都是始终如一的执行。如果希望程序中的注解在运行的时候起到一定的作用,只有通过某种配套的工具对注解中的信息进行访问和处理,访问和处理注解的工具统称APT(Annotation Processing Tool)。

基础注解

注解必须使用工具来处理,工具负责提取注解当中包含的元数据,工具还会根据这种元数据增加额外的功能。使用注解之前要在其前面增加@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。

5个基本注解如下:

  • @Override
  • @Deprecated
  • @SuppressWarnings
  • @SafeVarargs
  • @FunctionalInterface
    上面5个基本注解中@SafeVarargs是Java7新增的、@FunctionalInterface是Java8新增的。

限定重写父类方法:@Override

@Override就是用来指定方法覆载,它可以强制一个子类必须覆盖父类的方法。如下程序中使用@Override指定子类Apple的info()方法必须重写父类的方法。
注意: @Override只能修饰方法不能修饰其他程序元素。

@Deprecated

@Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时类、方法时,编译器就会给出警告。如下程序指定Apple类中的info()方法已过时,其他程序中使用Apple类的info()方法 时编译器将会给出警告。

package test;

class Apple{
	@java.lang.Deprecated()
	public void info()
	{
		
	}
}
public class Deprecated {
	public static void main(String[] args) {
		new Apple().info();
	}
}

抑制编译器警告:@SuppressWarnings

@SuppressWarnings指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显式指定的编译器警告。

@SuppressWarnings将会一直作用到该程序元素中的所有子元素。

堆污染警告

之前的博客在学习泛型擦除的时候,介绍了如下的代码将会导致运行时异常。

package test;

import java.util.ArrayList;
import java.util.List;

class Apple{
	@java.lang.Deprecated()
	public void info()
	{
		
	}
}
public class Deprecated {
	public static void main(String[] args) {
		new Apple().info();
		List list = new ArrayList<Integer>();
		list.add(20);
		//添加元素的引发uncheck异常
		List<String> ls = list;
		//这一步将会引起“未经检查的转换”,编译、运行时非常正常
		System.out.print(ls.get(0));
		//但是只要访问ls中的数据就会产生运行时的异常。
	}
}

Java把引发这种错误的原因称为“堆污染”(Heap pollution),当把一个不带泛型的对象赋给一个带泛型的变量时,往往就会发生这种“堆污染”。

对于形参个数可变的方法,该形参的类型又是泛型,这将更容易导致“堆污染”。例如以下的工具类。

package test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ErrorUtils {
	public static void faultyMethod(List<String>... listStrArray) {
		List[] listArray = listStrArray;
		List<Integer> myList = new ArrayList();
		myList.add(new Random().nextInt(100));
		listArray[0] = myList;
		String s = listStrArray[0].get(0);
	}
}

上面的程序中的粗体字代码已经发生了堆污染。由于该方法有个形参是List<String>…类型,个数可变的形参相当于数组,但Java又不支持泛型数组,因此程序只能List<String>…当成List[]处理,这里就发生了堆污染。

在Java6以及更早的版本的中,Java编译器认为faultyMethod()方法完全没有问题,既不是提示错误,也没有提示警告。

等使用该方法时,例如如下程序

package test;

import java.util.Arrays;

public class ErrorUtilsTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ErrorUtils.faultyMethod(Arrays.asList("hello1"),Arrays.asList("world!"));
		
	}

}

编译该程序将会引发一个unchecked警告。这个unchecked警告出现的比较“突兀”:定义faultyMethod()方法时没有警告,调用该方法时却引发了一个“警告”。

从Java7开始,Java编译器将会进行更严格的检查,Java编译器在编译ErrorUtils时就会发生一个如下的警告。

Type safety: Potential heap pollution via varargs parameter listStrArray

由此可见,Java7会在定义该方法的时候就发出堆污染警告,这样保证了开发者“更早”的注意到程序中可能出现的漏洞。

但有些时候,开发者不希望看到这个警告,则可以使用以下三种方法来“抑制”这个警告:

  • 使用@SafeVarags修饰引发该警告的方法或者构造器。Java9增强了该注解,允许使用该注解修饰私有实例方法。
  • 使用@SuppressWarning(“unchecked”)修饰
  • 编译时使用-Xlint:varargs选项。

很明显,第三种方法一般比较少用,通常可以选择第一种或者第二种方式,尤其使用第一种方式,他是Java7专门为抑制“堆污染”警告提供的。

如果使用@SafeVarags修饰ErrorUtils类中faultyMethod()方法,则编译上面两个程序时都不会发出警告。

函数式接口与@FunctionalInterface

上面已经提到,从java8开始:如果从程序中只有一个抽象方法(可以包含多个默认方法或者多个static方法),该接口就是函数式接口。
@FunctionalInterface就是用来指定某个接口必须是函数式接口。例如,如下程序使用@FunctionalInterface修饰了函数式接口。

函数式接口就是为了Java8的Lambda表达式准备的,Java8允许使用Lambda表达式创建函数式接口的实例,因此Java8专门增加了@FunctionalInterface

@FunctionalInterface
public interface FunInterface{
    static void foo(){
        System.out.println("1111");
    }
    default void bar(){
        System.out.print("2222");
    }
    void test();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值