Java中的注解/注释详解(Override/Deprecated/SuppressWarning)

Java中的注解/注释详解

Java中的注释主要有以下三种:Override/Deprecated/SuppressWarning,接下来我们主要讨论这3个注解的内容。

一,什么是注释(注解)

    在Java中,注解和注释,我们可以理解为同一个概念。说起注释,我们需要先知道什么是元metadata,这个单词我们国内一般翻译为元数据,
所谓元数据,就是数据的数据。也就是说,元数据存在的意义,就是为了描述数据。有了元数据的概念以后,我们就可以把注释称作Java代码的元
数据。

二,Override

    这个注释的作用,是用来标识当前的方法,是否覆盖了它的父类中的方法。为什么要用Override注释呢?个人的理解,Override注释与Java
语法糖有异曲同工之妙,它更多的起到的作用是规范Java代码的编写,方便程序员使用,减少产生BUG的机会。关于这一点,我们可以通过下面2种
方式来证明。第一种方式:源代码经过编译后,用反编译工具JD打开后,所有Override注释的部分都没有了;第二种方式:从JVM的角度来看,在
Class文件的结构中,没有注释内容的定义。

官方定义:
/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a superclass.  If a method is annotated with
 * this annotation type but does not override a superclass method,
 * compilers are required to generate an error message.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

三,Deprecated

    Deprecated注释是一个标记注释。所谓标记注释,就是已经过期或者即将过期的方法,不推荐我们使用。这个方法不推荐使用的原因,可能是
存在安全问题,或者是有了更好的方法来替代他。源代码中加入这个标记后,并不影响程序的编译,但加上这个标记后,编译器会显示一些警告信息。
例如当我们使用myeclipse写代码时,使用提示功能:
String str = "yangcq";
str.
当我们输入str.以后,myeclipse会提示String类的所有可用方法,这时,我们找到getBytes这个方法,发现有些不同,方法前面多了一个斜线。这
个就是过期的方法,说明getBytes方法使用了@Deprecated注释。

进入String类的源代码,找到getBytes,这里果然有一个注释@Deprecated



    @Deprecated
    public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > count) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        int j = dstBegin;
        int n = offset + srcEnd;
        int i = offset + srcBegin;
        char[] val = value;   /* avoid getfield opcode */

        while (i < n) {
            dst[j++] = (byte)val[i++];
        }
    }

官方定义:

/**
 * A program element annotated @Deprecated is one that programmers
 * are discouraged from using, typically because it is dangerous,
 * or because a better alternative exists.  Compilers warn when a
 * deprecated program element is used or overridden in non-deprecated code.
 *
 * @author  Neal Gafter
 * @version %I%, %G%
 * @since 1.5
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

四,SuppressWarnings

    SuppressWarnings的作用,通过英文单词的意思,就可以看出来。Suppress是阻止的意思,Suppress Warnings就
是阻止警告信息的提示。在Java中,如果一个方法存在未经检查或者不安全的操作,编译器会提示警告信息。例如我们
最常见的Map和List泛型的问题,请看下面的写法:
Map map = new HashMap();
Map<String,String> map = new HashMap<String,String>();
上面一行的写法,编译器就会提示警告信息,因为Map和HashMap的类型未知,程序执行期间可能存在类型转换的问题,
如果我们用下面这一行的写法,就不会出现警告。怎么不让编译器提示警告信息呢,就是使用SuppressWarnings注解。

    我们都知道,JDK1.5之后,Java开始支持泛型,泛型说白了,就是一种语法糖,只是为了方便程序员写程序而引入
的一个概念,是以一种规则的形式,告诉程序员,类似Map map = new HashMap();这种写法,在程序执行阶段,很有可
能存在强制类型转换的错误。更多关于Java语法糖的知识,可以参考我的另一篇博客:
http://blog.csdn.net/reggergdsg/article/details/51973701

SuppressWarnings注解的官方定义如下:

/**
 * Indicates that the named compiler warnings should be suppressed in the
 * annotated element (and in all program elements contained in the annotated
 * element).  Note that the set of warnings suppressed in a given element is
 * a superset of the warnings suppressed in all containing elements.  For
 * example, if you annotate a class to suppress one warning and annotate a
 * method to suppress another, both warnings will be suppressed in the method.
 *
 * <p>As a matter of style, programmers should always use this annotation
 * on the most deeply nested element where it is effective.  If you want to
 * suppress a warning in a particular method, you should annotate that
 * method rather than its class.
 *
 * @since 1.5
 * @author Josh Bloch
 */
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    /**
     * SuppressWarnings注解的不同之处在于,SuppressWarnings有一个属性value,这个属性对警告信息进行了分
     * 类,比如我们常见的值有:unused、unchecked,下面的2种写法是等价的:
     * @SuppressWarnings(value="unused")
     * @SuppressWarnings("unused")
     */
    String[] value();
}

例如下面的一段代码,因为定义的reentrantReadWriteLock锁在之后的程序中一直没有使用,因此编译器会提示警告信
息,使用@SuppressWarnings("unused")以后,编译器不再提示警告信息。我们可以把这个功能理解为Java编程语言的
一种规范,它会以显式的方式,提示程序员对可能出问题的代码进行一次确认。就像下面的代码中,编译器提示以后,
程序员就会去检查一下,然后怎么做,由程序员自己来决定。这样做的好处是可以有效减少一些常见的错误,提高代码
质量。


	@SuppressWarnings("unused")
	public static void main(String[] args) {
		// 创建可重入锁对象lock
		Lock lock = new ReentrantLock();
		ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();// 读写锁
		// 在执行一段代码之前获取锁
		lock.lock();	
		// 执行代码,修改余额的值,这里可以是我们项目中任何一个需要加锁的操作
		updateAccountBalance(account_balance);	
		// 在执行一段代码之后释放锁
		lock.unlock();
	}

五,如何自定义注解/注释

    在Java中,如果有需求,我们也可以自定义注解/注释。在自定义注解之前,我们需要先了解一下注解的相关语
法。最简单的方法,我们可以看一下Java给我们提供的三种注解是如何定义的,如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

1,@Ducumented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的
工具文档化。Documented是一个标记注解,没有成员。@Ducumented元注解的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

2,@Target
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation
类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在
Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以在哪些地方使用)
Target的取值(ElementType)有:
    CONSTRUCTOR:用于描述构造器
    FIELD:用于描述域
    LOCAL_VARIABLE:用于描述局部变量
    METHOD:用于描述方法
    PACKAGE:用于描述包
    PARAMETER:用于描述参数
    TYPE:用于描述类、接口(包括注解类型) 或enum声明
Target的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

3,@Retention
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却
被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取
(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对
 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
Retention取值(RetentionPoicy)有:
    SOURCE:在源文件中有效(即源文件保留)
    CLASS:在class文件中有效(即class保留)
    RUNTIME:在运行时有效(即运行时保留)
Retention的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

4,@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修
饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法
并不从它所重载的方法继承annotation。
  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种
继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将
展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
Inherited的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

5,如何自定义注解/注释呢?

    首先我们创建一个接口,interface,这个接口和普通的接口有点不一样的地方就是,interface前面需要加一个
@符号,不要担心,不会报错的。如下:
public @interface MyAnnotation  // 自定义注解时新建的接口
public interface MyAnnotation   // 我们平时使用的接口

源代码贴上(注解的生命周期,使用对象在注释中有详细说明):

package com.annotation.java;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author yangcq
 * @description 自定义注解 MyAnnotation
 *
 */
@Documented                           // 这是一个标识注解,标识当前注解是否可以被工具文档化
@Retention(RetentionPolicy.RUNTIME)   // 定义注解的生命周期  RUNTIME:在运行时有效(即运行时保留)
@Target(ElementType.METHOD)           // 定义注解的使用对象,这里定义这个注解只能在方法上使用
public @interface MyAnnotation {
    // 这就是自定义注解/注释,没有任何内容
}

package com.annotation.java;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author yangcq
 * @description 自定义注解 MyAnnotationWithValue
 *
 */
@Documented                           // 这是一个标识注解,标识当前注解是否可以被工具文档化
@Retention(RetentionPolicy.RUNTIME)   // 定义注解的生命周期  RUNTIME:在运行时有效(即运行时保留)
@Target(ElementType.METHOD)           // 定义注解的使用对象,这里定义这个注解只能在方法上使用
public @interface MyAnnotationWithValue {
    String[] value();
}

package com.annotation.java;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author yangcq
 * @description 自定义注解 MyAnnotationInVariable
 *
 */
@Documented                           // 这是一个标识注解,标识当前注解是否可以被工具文档化
@Retention(RetentionPolicy.RUNTIME)   // 定义注解的生命周期  RUNTIME:在运行时有效(即运行时保留)
@Target(ElementType.FIELD)            // 定义注解的使用对象,这里定义这个注解只能在Field上使用
public @interface MyAnnotationInVariable {

}

OK ,上面新建了3个自定义的注解,接下来,我们编写一个测试类来进行测试,看一下自定义注解都是如何使用
的。就以上面的三个注解为例:

package com.annotation.java;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * 
 * @author yangcq
 * @description 如何使用自定义注解
 *
 */
public class MyAnnotationTest {
    final Log log = LogFactory.getLog(MyAnnotationTest.class);
    
    /**
     * 注解1:只能在本地变量中使用的注解
     */
    @MyAnnotationInVariable  // 这个注解只能在Field上使用
    String str = "yangcq2016";
    
    /**
     * 注解2:只能在方法上使用的注解
     */
    @MyAnnotation // 这个注解只能再方法上使用,如果我们在类上使用就会报错
    public void eat() {
        log.info("eat方法使用了我们自定义的注解:MyAnnotation");
    }
    
    /**
     * 注解3:只能在方法上使用的注解(带value值)
     */
    @MyAnnotationWithValue(value="yangcq")  // 有Value值的注解
    public void say(){
        log.info("say方法使用了我们自定义的注解:MyAnnotationWithValue");
    }
}

六,自定义注解在实际中的应用

    自定义注解都有哪些应用场景呢?自定义注解/注释有什么用途呢?这个问题抛给读者朋友们,大家思考一下,
由于篇幅有限,这里不再详细说明,后面会继续分享关于这方面的文章。给大家一个思路,Hibernate、Spring和
Mybatis等ORM框架,在实现时,使用了大量的注解功能,我们可以阅读一下Hibernate、Spring或者Mybatis的源代码,
了解自定义注解在实际中的应用。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值