Java-注解

之前一直以为注释=注解,em.....
Java5之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解。例如在方法覆盖中使用@Override,注解,注解都是用@符号开头的。
有的注解可以在运行时读写字节码文件信息

1.基本注解

无论哪一种注解,本质上都是一种数据类型,是一种接口类型。到Java8为止,Java提供了11种内置注解。其中有5种是基本注解,他们来自于java.lang包;
有六种是元注解(meta annotation),他们来自于java.lang.annotation包,自定义注解会用到元注解。
基本注解类型包括@Override,@Deprecated,@SuppressWarnings,@SafeVarargs,@FunctionalInterface。下面逐一介绍

1.1 @Override:

@Override只能用于方法,子类覆盖父类方法(或者实现接口方法)时可以@Override注解。编译器会检查被@Override的方法,确保该父类中存在的方法,否则会有编译错误。

1.2 @Deprecated

@Deprecated用来指示API已经过时了。@Deprecated可以用来注释类,接口,成员方法和变量。调用@Deprecated方法的代码会出现删除线

1.3@SuppressWarnnings

@SuppressWarnnings注释用来抑制编译器警告,如果确认程序中的警告没有问题,可以不用理会。若是不想看到相关警告,可以使用@SuppressWarnning方法来消除警告。

1.4@SafeVarargs

@SafeVarargs可以抑制将非范型变量值赋值给范型变量值

1.5@FunctionalInterface

@FunctionalInterface用于接口注释

2.元注解

元注解包括@Documented,@Tagget,@Retention,@Inherited,@Repeatable,@Native。元注解是为其他注解进行说明的注释。
当自定义一个新的注解类型时,其中可以使用元注解。

1.@Documented

如果在一个自定义注解中引用@Documented注解,那么该注解可以修饰代码元素(类、接口、成员变量和成员方法),javadoc等工具可以提取这些元素

2.@Target

@Target注解用来指定一个新注解的适用目标。@Target注解中有一个成员(value)用来设置目标,value是java.lang.annotation.ElementType枚举类型的数组,ElementType描述Java程序元素类型,他有10个枚举常量。
枚举常量 说明
ANNOTATION_TYPE 其他注释类型声明
CONSTRUCTOR 构造方法声明
FIELD 成员变量或常量声明
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口声明
TYPE_PARAMETER 用于泛型中类型参数的声明
TYPE_USE 用于任何类型的声明,Java8推出

3,@Retention

@Rentention注释用来指定一个新注解的有效范围,@Retention注解一个成员(value)用来设置保留策略,value是java.lang.annotation.RetentionPolicy枚举类型,RetentionPolicy描述注释保留类型策略,他有三个枚举常量
枚举常量 说明
SOURCE 只适用于Java源代码文件中,此范围最小
CLASS 编译器把注释信息记录在字节码文件中,此范围居中
RUNTIME 编译器把注释信息记录在字节码文件中,并在运行时可以读取这些信息,此范围最大

4.@Inherited

@Inherited注释用来指定一个新注解可以被继承。假定一个类A被该注释修饰,那么这个A类的子类会继承该新注解

5.@Repeatable

@Repeatable注解允许在相同的程序元素中重复注解。可重复的注解必须使用@Repeatable进行注解。

6.@Native 

@Native 注释一个成员变量,指示这个变量可以被本地代码引用。常常被代码生成工具使用

 3.自定义注解

1.声明注解

声明自定义注解可以使用@Interface关键字实现,最简单的形式的注释实例代码如下:
//Marker.java文件
package.com.a51work6;
public @Interface Marker{}
一个源文件中可以声明多个注解,但是只有一个是公有访问权限的,源文件命名与公有访问权限的注解名一致。
Marker注解中不包含任何成员,这种注解称为标记注解,基本注解中的@Override就属于标记注解。根据需要,注解中可以包含一些成员,实例代码
//Marker.java文件
package.com.a51work6;  //单值注解
@Interface MyAnnotation{
    String value();
} 
代码中声明MyAnnotation注解,他有一个成员value,注意value后面有对小括号。value前面的是数据类型。成员也可以有访问权限修饰符,但是只能是公有权限和默认权限。
注释中的成员也可以有默认值,示例代码如下:
//Marker.java文件
package.com.a51work6; 
//单值注解
@Interface MyAnnotation1{
    String value() default "注解信息";
    int count() default 0;   
}
2.案例:使用元注解
前面啰嗦了一大堆,直接写个例子吧,不然要睡着了。
例子:
import javax.xml.bind.Element;
import java.lang.annotation.*;
//指定MyAnnotation注解信息可以被javadoc工具读取
@Documented
//指定注解用于修饰类和接口等类型
@Target({ ElementType.TYPE })
//指定注释信息可以在运行时被读取
@Retention( RetentionPolicy.RUNTIME )
public @interface MyAnnotation {
    //注解的成员
    String description();
}
还是不知道这个用来干嘛。再来一个例子试试
//MemberAnnotation.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;

@Documented
@Retention(RetentionPolicy.RUNTIME)
//指定MemberAnnotation注解用于修饰类中的成员
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface MemberAnnotation {
    //type类型是Class<T>,默认值是void.class
    //<?>表示不限定类型,用?作为占位符
    Class<?> type() default void.class;
    //description类型是String,没有默认值
    String description();
}
import javax.xml.bind.Element; import java.lang.annotation.*; //指定MyAnnotation注解信息可以被javadoc工具读取 @Documented //指定注解用于修饰类和接口等类型 @Target({ ElementType.TYPE}) //指定注释信息可以在运行时被读取 @Retention( RetentionPolicy.RUNTIME ) public @interface MyAnnotation {     //注解的成员     String description(); }
@MyAnnotation(description = "这是一个测试类") public class Person {     //修饰成员变量     @MemberAnnotation(type = String.class, description = "名字")     private String name;     @MemberAnnotation(type = int.class, description = "年龄")     private int age;     @MemberAnnotation(type = String.class, description = "获得名字")     public String getName() {         return name;     }     //修饰成员方法     @MemberAnnotation(type = int.class, description = "获得年龄")     public int getAge() {         return age;     }     @MemberAnnotation(description = "设置姓名和年龄")     public void setNameAndAge(String name, int age) {         this.name = name;         this.age = age;     }     @Override     public String toString() {         return "Person [name=" + name + ", age=" + age + "]";     } }
还是不知道要干嘛。。。。
3.案例读取运行时注解信息
注解是为工具读取信息而准备的。有些工具可以读取源代码文件中的注解信息;有的可以读取字节码文件中的注解信息;有的可以在运行时读取注解信息。但是读取这些注解信息都是一样的,区别只在于自定义注解中@Retention的保留策略不同。
读取注解信息要反射相关API,Class类有如下方法。
<A extends Annotation>A getAnnotation(Class<A> annotation Class):如果此元素存在annotationClass类型的注解,则返回注解,否则返回null。
Annotation[] getAnnotations():返回此元素上存在的所有注解。
Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。与getAnnotations()区别在于,该方法将不返回继承的注释。
boolbean isAnnotationPresent(Class<? extends Annotation> annotationClass):如果此元素存在annotationClass的注解,则返回true,否则返回false。
boolbean isAnnotation():如果此Class对象表示一个注解类型,则返回true。
运行时Person类中注解信息代码如下:
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class mainFun {
    public static void main(String[] args) {
        try {
            //创建Person类对应的对象
            Class<?> clz = Class.forName("Person");
            //读取类注解
            //判断Person类是否存在MyAnnotation注解
            if (clz.isAnnotationPresent(MyAnnotation.class)) {
                //返回注解实例
                MyAnnotation myAnnotation = (MyAnnotation) clz.getAnnotation(MyAnnotation.class);
                //读取注解中的表达式
                System.out.printf("类%s,读取注释描述:%s\n", clz.getName(), myAnnotation.description());
            }

            //读取成员方法的注解详情
            Method[] methods = clz.getDeclaredMethods();
            for (Method method : methods) {
                //判断方法是否存在MemberAnnotation注解
                if (method.isAnnotationPresent(MemberAnnotation.class)) {
                    //返回MemberAnnotation注解实例
                    MemberAnnotation memberAnnotation = method.getAnnotation(MemberAnnotation.class);
                    //读取MemberAnnotation实例的注解表达式
                    System.out.printf("方法%s,读取注解描述: %s\n", method.getName(), memberAnnotation.description());
                }
            }

            //读取成员变量的注解信息
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                //判断变量是否存在MemberAnnotation注解
                if (field.isAnnotationPresent(MemberAnnotation.class)) {
                    //返回MemberAnnotation注解实例
                    MemberAnnotation memberAnnotation = field.getAnnotation(MemberAnnotation.class);
                    //读取MemberAnnotation实例的注解表达式
                    System.out.printf("成员变量%s,读取注解描述:%s\n", field.getName(), memberAnnotation.description());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
执行结果:
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=55483:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\编程\eclipseJavaCode\IXml\out\production\IXml;E:\编程\eclipseJavaCode\IXml\lib\jaxws-api.jar;E:\编程\eclipseJavaCode\IXml\lib\jaxws-rt.jar;E:\编程\eclipseJavaCode\IXml\lib\jaxws-tools.jar;E:\编程\eclipseJavaCode\IXml\lib\activation.jar;E:\编程\eclipseJavaCode\IXml\lib\FastInfoset.jar;E:\编程\eclipseJavaCode\IXml\lib\gmbal-api-only.jar;E:\编程\eclipseJavaCode\IXml\lib\http.jar;E:\编程\eclipseJavaCode\IXml\lib\jaxb-api.jar;E:\编程\eclipseJavaCode\IXml\lib\jaxb-impl.jar;E:\编程\eclipseJavaCode\IXml\lib\jsr173_api.jar;E:\编程\eclipseJavaCode\IXml\lib\jsr181-api.jar;E:\编程\eclipseJavaCode\IXml\lib\jsr250-api.jar;E:\编程\eclipseJavaCode\IXml\lib\management-api.jar;E:\编程\eclipseJavaCode\IXml\lib\mimepull.jar;E:\编程\eclipseJavaCode\IXml\lib\policy.jar;E:\编程\eclipseJavaCode\IXml\lib\resolver.jar;E:\编程\eclipseJavaCode\IXml\lib\saaj-api.jar;E:\编程\eclipseJavaCode\IXml\lib\saaj-impl.jar;E:\编程\eclipseJavaCode\IXml\lib\stax-ex.jar;E:\编程\eclipseJavaCode\IXml\lib\streambuffer.jar;E:\编程\eclipseJavaCode\IXml\lib\woodstox.jar;E:\编程\eclipseJavaCode\IXml\lib\jaxb-xjc.jar" mainFun
类Person,读取注释描述:这是一个测试类
方法getName,读取注解描述: 获得名字
方法getAge,读取注解描述: 获得年龄
方法setNameAndAge,读取注解描述: 设置姓名和年龄
成员变量name,读取注解描述:名字
成员变量age,读取注解描述:年龄

Process finished with exit code 0
终于知道要干嘛了。。。



















全部评论

相关推荐

刘湘_passion:太强了牛肉哥有被激励到
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务