[Java5新特性]Annotation注解

Annotation概述

Annotation是JDK 5.0以后提供对元数据的支持,可以在编译、加载和运行时被读取,并执行相应的处理。所谓Annotation就是提供了一种为程序元素设置元数据的方法,可用于修饰包、类、构造器、方法、成员变量、参数和局部变量的声明,这些信息被存储在Annotation的“name=value”对中。

Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据,比如一段代码的作者或者告诉编译器禁止一些特殊的错误,不会影响代码的执行。

基本Annotation

在Java中提供了3个基本Annotation的用法,使用Annotation时要在其前面增加@符号,并把该Annotation当作一个修饰符使用,用于修饰它支持的程序元素。这3个基本Annotation都定义在java.lang包下,可以通过查看API文档来了解。

  • @Override:限定重写父类方法。@Override就是用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法。
public class Fruit {
    public void info(){
        System.out.println("这是一个水果,想吃吗?");
    }
}
public class Apple extends Fruit {
    @Override
    public void info() {
        System.out.println("这不仅是一个水果,它是苹果.");
    }
}

如果Apple类的info()方法名写成了inf()的话,编译器会报错。值得注意的是,@Override只能修饰方法,不能修饰其他程序元素。

  • @Deprecated:标示已过时。@Deprecated用于表示某个程序元素已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。
public class Fruit {
    @Deprecated
    public void info(){
        System.out.println("这是一个水果,想吃吗?");
    }
}
public class DeprecatedTest {
    public static void main(String[] args) {
        // 使用info()方法时将会出现划线,表示该方法已过时.
        new Fruit().info();
    }
}
  • @SuppressWarnings:抑制编译器警告。@SuppressWarnings表示被该Annotation修饰的代码取消显示指定的编译器警告。
public class SuppressWarningsTest {
    public static void main(String[] args) {
        @SuppressWarnings("rawtypes")
        /*
         * List集合在定义时,没有指定泛型类型.
         *  * 默认情况下,出现编译器警告.
         *  * 使用@SuppressWarnings注释后,取消警告信息.
         */
        List list = new ArrayList();
    }
}

自定义Annotation

自定义一个Annotation类型使用@interface关键字,定义一个新的Annotation类型与定义一个接口非常像(只是多了一个@符号)。

// 自定义一个Annotation类型
public @interface Test {
}

在自定义一个Annotation类型通常可以用于修饰程序中的类、方法、变量、接口等。一般情况下,使用Annotation会在代码之前使用。

// 自定义Annotation类型定义在类上.
@Test
public class AnnotationTest {
    // 自定义Annotation类型定义在成员变量上.
    @Test
    private int i;
    // 自定义Annotation类型定义在构造函数上.
    @Test
    public AnnotationTest(){}
    // 自定义Annotation类型定义在方法上.
    @Test
    // 自定义Annotation类型定义在方法参数上.
    public void fun(@Test String str){
        // 自定义Annotation类型定义在变量上.
        @Test
        int z;
    }
}

Annotation属性

自定义Annotation不仅可以是这种简单形式,还可以包含成员变量。自定义的Annotation的成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。

/**
 * 自定义带有username和password属性的Annotation
 *
 * @author 金云龙
 */
public @interface UserInfo {
    String username();
    String password();
}

使用带有属性的自定义Annotation时,必须使用其属性指定值,否则会报错。

@UserInfo(username="zhangwuji",password="123")
public class UserInfoTest {
}

自定义Annotation不仅可以设置属性,还可以为属性设置默认值,使用default关键字。

/**
 * 自定义带有username和password属性的Annotation
 *  * 为username属性设置默认值.
 * @author 金云龙
 */
public @interface UserInfo {
    String username() default "zhangwuji";
    String password();
}

如果为自定义Annotation的属性设置了默认值,则在使用时可以不为该属性指定值(使用默认值)。也可以在使用该Annotation时为其属性指定值,则默认值不会起作用。

自定义Annotation中具有名为value的属性,在使用该Annotation时如果只使用value属性的话,可以不写属性名直接指定值。

@UserInfo("jiaozhu")
public class UserInfoTest {
}

Annotation的属性类型只能是基本类型、String、Enum、Class及上述类型的一维数组类型。

@Target注解

@Target修饰自定义Annotation,指定该自定义Annotation可以用于修饰哪些程序单元,例如方法、成员变量等。@Target注解包含一个ElementType类型的value属性,该属性值只能是如下几个:

  • ElementType.ANNOTATION_TYPE:指定该策略的Annotation只能修饰Annotation。
  • ElementType.CONSTRUCTOR:指定该策略的Annotation只能修饰构造器。
  • ElementType.FIELD:指定该策略的Annotation只能修饰成员变量。
  • ElementType.LOCAL_VARIABLE:指定该策略的Annotation只能修饰局部变量。
  • ElementType.METHOD:指定该策略的Annotation只能修饰方法定义。
  • ElementType.PACKAGE:指定该策略的Annotation只能修饰包定义。
  • ElementType.PARAMETER:指定该策略的Annotation只能修饰参数。
  • ElementType.TYPE:指定该策略的Annotation可以修饰类、接口或枚举定义。

以下是@Target注解的源码和ElementType的源码:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
public enum ElementType {
    TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE
}

@Retention注解

@Retention修饰自定义Annotation,指定自定义Annotation的生命周期。@Retention包含一个RetentionPolicy类型的value属性,该属性值只能是如下几个:

  • RetentionPolicy.CLASS:编译器将把Annotation记录在class文件中。当运行Java程序时,JVM不可获取Annotation信息。这时默认值。
  • RetentionPolicy.RUNTIME:编译器将把Annotation记录在class文件中。当运行Java程序时,JVM也可以获取Annotation信息,程序可以通过反射获取该Annotation信息。
  • RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器直接丢弃这种Annotation。

以下是@Retention注解的源码和RetentionPolicy的源码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
public enum RetentionPolicy {
    SOURCE, CLASS, RUNTIME
}

反射读取Annotation

使用Annotation修饰了类、方法、成员变量等之后,这些Annotation不会自己生效,必须通过相应程序提取并处理Annotation信息。Java提供的Annotation接口是所有注解的父接口,在JDK 5.0新增加AnnotatedElement接口,该接口提供读取运行时Annotation的方法。只有当自定义的Annotation使用了@Retention(RetentionPolicy.RUNTIME)时,该Annotation才会在运行可见,JVM才能读取保存在class文件的Annotation信息。

以下是AnnotatedElement接口提供的方法API:

方法摘要
<T extends Annotation> T getAnnotation(Class annotationClass)
Annotation[] getAnnotations()
Annotation[] getDeclaredAnnotations()
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

实际获取某类使用的Annotation信息的方式如下:

public class AnnotatedElementTest {
    public static void main(String[] args) throws Exception {
        // 获取对应类的Class对象.
        Class<UserInfoTest> clazz = UserInfoTest.class;
        // 获取对应类方法的Method对象.
        Method method = clazz.getMethod("fun");
        // 获取类上的注解.
        UserInfo anno1 = clazz.getAnnotation(UserInfo.class);
        // 打印该注解的username属性值.
        System.out.println(anno1.username());
        // 获取方法上的注解.
        UserInfo anno2 = method.getAnnotation(UserInfo.class);
        // 打印该注解的username属性值.
        System.out.println(anno2.password());
    }
}

注解配置JDBC案例

使用JDBC连接MySQL数据库时,需要driverClassName、url、username和password四个参数。而之前的做法是将这四个参数写入一个配置文件,在JDBCUtils工具类中读取配置文件。目前可以将四个参数定义为一个注解,在JDBCUtils工具类中通过反射获取对应注解定义的四个参数内容。具体做法如下:

  • 定义一个Annotation用于定义JDBC连接MySQL数据库所需的四个参数内容。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JDBCInfo {
    String driverClassName();
    String url();
    String username();
    String password();
}
  • 定义JDBCUtils工具类,使用Annotation配置四个参数内容,并使用反射进行读取。
public class JDBCUtils {
    @JDBCInfo(driverClassName = "com.mysql.jdbc.Driver", url = "jdbc:mysql://localhost:3306/jdbc", username = "root", password = "root")
    public static Connection getConnection() throws Exception {
        // 获取注解修饰目标对应的反射对象.
        Method method = JDBCUtils.class.getDeclaredMethod("getConnection");
        // 判断是否存在目前注解
        if (method.isAnnotationPresent(JDBCInfo.class)) {
            // 获取注解信息
            JDBCInfo jdbcInfo = method.getAnnotation(JDBCInfo.class);
            // 读取注解属性信息
            String driverClassName = jdbcInfo.driverClassName();
            String url = jdbcInfo.url();
            String username = jdbcInfo.username();
            String password = jdbcInfo.password();
            // Class类加载驱动
            Class.forName(driverClassName);
            // 返回连接对象
            return DriverManager.getConnection(url, username, password);
        }
        return null;
    }
}
  • 编写一个测试类用于测试JDBCUtils工具类是否正确。
public class JDBCTest {
    public static void main(String[] args) throws Exception {
        Connection conn = JDBCUtils.getConnection();
        String sql = "select * from products";
        PreparedStatement statement = conn.prepareStatement(sql);
        ResultSet rs = statement.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("name") + "," + rs.getDouble("price"));
        }

        rs.close();
        statement.close();
        conn.close();
    }
}


转载说明:请注明作者和原文链接,谢谢!

时间: 2024-12-19 14:56:24

[Java5新特性]Annotation注解的相关文章

主流的单元测试工具之-JAVA新特性-Annotation 写作者:组长 梁伟龙

1:什么是Annotation?Annotation,即“@xxx”(如@Before,@After,@Test(timeout=xxx),@ignore),这个单词一般是翻译成元数据,是JAVA的一个新特性. 主流的单元测试工具之-JAVA新特性-Annotation - groupthreetogether - group博客 2:元数据的简单介绍: @Before:使用了该元数据的方法在每个测试方法执行之前都要执行一次. @After:使用了该元数据的方法在每个测试方法执行之后要执行一次.

spring4.0.6最新稳定版新特性学习,注解自动扫描bean,自动注入bean(二)

Spring4.0的新特性我们在上一章已经介绍过了.包括它对jdk8的支持,Groovy Bean Definition DSL的支持,核心容器功能的改进,Web开发改进,测试框架改进等等.这张我们主要介绍spring4.0的自动扫描功能,以及对bean的过滤等特性进行学习. 好吧,废话少说,我们来看看代码吧. package com.herman.ss.test; import org.springframework.context.ApplicationContext; import org

Python3新特性 类型注解 以及 点点点

Python3新特性 类型注解 以及 点点点 ... Python3 的新特性 Python 是一种动态语言,变量以及函数的参数是 不区分类型 的 在 函数中使用类型注解 相当于 给 形参的 类型 设置了一个备注 # 使用类型注解 a b 参数需要 int 类型的 变量 def func(a: int = ..., b: int = ...): return a + b 使用 PyCharm 编写python代码时 函数调用会有默认参数的 提示 如果传递的 参数不是 指定的类型 正常使用也不会报

主流的单元测试工具之-JAVA新特性-Annotation

写作者:组长 梁伟龙 1:什么是Annotation? Annotation,即“@xxx”(如@Before,@After,@Test(timeout=xxx),@ignore),这个单词一般是翻译成元数据,是JAVA的一个新特性. 2:元数据的简单介绍: @Before:使用了该元数据的方法在每个测试方法执行之前都要执行一次. @After:使用了该元数据的方法在每个测试方法执行之后要执行一次. 注意:@Before和@After标示的方法只能各有一个.这个相当于取代了JUnit以前版本中的

Java5新特性

"JDK1.5/Java5"的一个重要主题就是通过新增一些特性来简化开发. 这些特性包括泛型,for-each循环,自动装包/拆包,枚举,可变参数, 静态导入,注解. 使用这些特性有助于我们编写更加清晰,精悍,安全的代码. 1.泛型(Generic)C++通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功能.一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制得类型转换.猛虎引入了泛型,它允许指定集合里元素的类型,这样你可以得到

java5 新特性

1.静态导入方法 Java代码   package com.java.new_features_jdk5; /** * * 一般我们导入一个类都用 import com.....ClassName;而静态导入是这样:import static com.....ClassName.*; * 这里的多了个static,还有就是类名ClassName后面多了个 .* ,意思是导入这个类里的静态方法.当然,也可以只导入某个静态方法,只要把 .* 换成静态方法名就行了. * 然后在这个类中,就可以直接用方

Java5新特性之静态导入、可变参数、增强for循环、自动拆装箱

JDK1.5已经发布很长时间,之所以还拿出来是因为它增加了很多个重要的特性,使用这些特性有助于我们简化开发,编写的代码更加简洁清晰安全,主要有以下几个特性: ?  静态导入 ?  可变参数 ?  增强for循环 ?  自动拆装箱 ? 泛型 ? 枚举 由于泛型.枚举内容比较多,也最重要,之后单拿出来讲.这里先介绍前面四个简单而又实用的小特性. 1. 静态导入 所谓"静态导入"只不过是在普通的import语句中加入关键字static,例如: ?  非静态导入:import java.lan

JDK1.5新特性之注解

时间:2017-1-2 20:14 --注解的概述 注释是给人看的,而注解是给程序(框架)看的.    在Servlet3.0中可以使用注解来替代配置文件,开发者就不用再写配置文件了,而是写注解,然后Tomcat来读取注解. 注解也是类,需要定义了才能使用.    在Servlet3.0中又一个注解类是@WebServlet,然后我们就可以在Servlet中使用@WebServlet这个注解了,这个朱姐就是用来替代<servlet>,然后Tomcat会通过反射来读取注解中的信息. --Java

java5新特性(简述十大新特性)

1.泛型 所谓类型擦除指的就是Java源码中的范型信息只允许停留在编译前期,而编译后的字节码文件中将不再保留任何的范型信息.也就是说,范型信息在编译时将会被全部删除,其中范型类型的类型参数则会被替换为Object类型,并在实际使用时强制转换为指定的目标数据类型.而C++中的模板则会在编译时将模板类型中的类型参数根据所传递的指定数据类型生成相对应的目标代码. Map<Integer, Integer> squares = new HashMap<Integer, Integer>()