•   欢迎来到21NN网.
  •   请记住本站网址www.21nn.cn

Java注解的细致引见(代码示例)【JAVA教程】,Java注解

摘要: 本篇文章给人人带来的内容是关于Java注解的细致引见(代码示例),有肯定的参考价值,有须要的朋侪可以参考一下,愿望对你有所协助。注解犹如标签初学者可以如许明白注解:想像代码具有生命,注解就是关...

本篇文章给人人带来的内容是关于Java注解的细致引见(代码示例),有肯定的参考价值,有须要的朋侪可以参考一下,愿望对你有所协助。

注解犹如标签

初学者可以如许明白注解:想像代码具有生命,注解就是关于代码中某些新鲜个别的贴上去的一张标签。简化来说,注解犹如一张标签。在未最先进修任何注解详细语法而言,你可以把注解算作一张标签。这有助于你疾速地明白它的大抵作用。假如初学者在进修历程有大脑放空的时刻,请不要慌张,对自身说:注解,标签。注解,标签。

注解语法

由于寻常开辟少见,置信有不少的职员会以为注解的职位不高。实在同 classs 和 interface 一样,注解也属于一种范例。它是在 Java SE 5.0 版本中最先引入的观点。注解的定义
注解经由过程 @interface关键字举行定义。

 public @interface TestAnnotation {
 }

它的情势跟接口很相似,不过前面多了一个 @ 标记。上面的代码就建立了一个名字为 TestAnnotaion 的注解。
你可以简朴明白为建立了一张名字为 TestAnnotation 的标签。

注解的应用

上面建立了一个注解,那末注解的的运用要领是什么呢。

@TestAnnotation
public class Test {
}

建立一个类 Test,然后在类定义的处所加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了。你可以简朴明白为将 TestAnnotation 这张标签贴到 Test 这个类上面。不过,要想注解可以一般事变,还须要引见一下一个新的观点那就是元注解。

元注解

元注解是什么意义呢?
元注解是可以注解到注解上的注解,或许说元注解是一种基础注解,然则它可以应用到别的的注解上面。
假如难于明白的话,你可以如许明白。元注解也是一张标签,然则它是一张特别的标签,它的作用和目的就是给其他一般的标签举行诠释申明的。
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

@Retention

Retention 的英文意为保存期的意义。当 @Retention 应用到一个注解上的时刻,它诠释申清楚明了这个注解的的存活时候。
它的取值以下:
RetentionPolicy.SOURCE 注解只在源码阶段保存,在编译器举行编译时它将被抛弃无视。
RetentionPolicy.CLASS 注解只被保存到编译举行的时刻,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保存到顺序运转的时刻,它会被加载进入到 JVM 中,所以在顺序运转时可以猎取到它们。
我们可以如许的体式格局来加深明白,@Retention 去给一张标签诠释的时刻,它指定了这张标签张贴的时候。@Retention 相当于给一张标签上面盖了一张时候戳,时候戳指清楚明了标签张贴的时候周期。

@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}

上面的代码中,我们指定 TestAnnotation 可以在顺序运转周期被猎取到,因而它的生命周期非常的长。

@Documented

望文生义,这个元注解肯定是和文档有关。它的作用是可以将注解中的元素包含到 Javadoc 中去。

@Target

Target 是目的的意义,@Target 指定了注解应用的处所。
你可以如许明白,当一个注解被 @Target 注解时,这个注解就被限制了应用的场景。
类比到标签,底本标签是你想张贴到哪一个处所就到哪一个处所,然则由于 @Target 的存在,它张贴的处所就非常详细了,比方只能张贴到要领上、类上、要领参数上等等。

@Target 有下面的取值

ElementType.ANNOTATION_TYPE 可以给一个注解举行注解
ElementType.CONSTRUCTOR 可以给组织要领举行注解
ElementType.FIELD 可以给属性举行注解
ElementType.LOCAL_VARIABLE 可以给局部变量举行注解
ElementType.METHOD 可以给要领举行注解
ElementType.PACKAGE 可以给一个包举行注解
ElementType.PARAMETER 可以给一个要领内的参数举行注解
ElementType.TYPE 可以给一个范例举行注解,比方类、接口、罗列

@Inherited

Inherited 是继续的意义,然则它并不是说注解自身可以继续,而是说假如一个超类被 @Inherited 注解过的注解举行注解的话,那末假如它的子类没有被任何注解应用的话,那末这个子类就继续了超类的注解。
说的比较笼统。代码来诠释。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}

注解 Test 被 @Inherited 润饰,以后类 A 被 Test 注解,类 B 继续 A,类 B 也具有 Test 这个注解。

可以如许明白:
  老子非常有钱,所以人们给他贴了一张标签叫做富豪。
  老子的儿子长大后,只需没有和老子拒却父子关系,虽然他人没有给他贴标签,然则他天然也是富豪。
  老子的孙子长大了,天然也是富豪。
这就是人们口中戏称的富一代,富二代,富三代。虽然叫法差别,彷佛好多个标签,但实在事变的实质也就是他们有一张配合的标签,也就是老子身上的那张富豪的标签。

@Repeatable

Repeatable 天然是可反复的意义。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特征。
什么样的注解会屡次应用呢?通常是注解的值可以同时取多个。
举个例子,一个人他既是顺序员又是产物司理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}


@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
    
}

注重上面的代码,@Repeatable 注解了 Person。而 @Repeatable 背面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来寄存别的注解的处所。它自身也是一个注解。
我们再看看代码中的相干容器注解。

@interface Persons {
    Person[]  value();
}

根据划定,它内里必需要有一个 value 的属性,属性范例是一个被 @Repeatable 注解过的注解数组,注重它是数组。

假如不好明白的话,可以如许明白。Persons 是一张总的标签,上面贴满了 Person 这类同范例但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了顺序员、产物司理、画家的标签。

我们能够关于 @Person(role=“PM”) 括号内里的内容感兴致,它实在就是给 Person 这个注解的 role 属性赋值为 PM ,人人不明白一般,立时就讲到注解的属性这一块。

注解的属性

注解的属性也叫做成员变量。注解只要成员变量,没有要领。注解的成员变量在注解的定义中以“无形参的要领”情势来声明,其要领名定义了该成员变量的名字,其返回值定义了该成员变量的范例。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    
    int id();
    
    String msg();
}

上面代码定义了 TestAnnotation 这个注解中具有 id 和 msg 两个属性。在运用的时刻,我们应当给它们举行赋值。
赋值的体式格局是在注解的括号内以 value="" 情势,多个属性之前用 ,离隔。

@TestAnnotation(id=3,msg="hello annotation")
public class Test {

}

须要注重的是,在注解中定义属性时它的范例必需是 8 种基础数据范例外加 类、接口、注解及它们的数组。
注解中属性可以有默认值,默认值须要用 default 关键值指定。比方:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    
    public int id() default -1;
    
    public String msg() default "Hi";
}

TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。
它可以如许应用。

@TestAnnotation()
public class Test {}

由于有默认值,所以无须要再在 @TestAnnotation 背面的括号内里举行赋值了,这一步可以省略。
别的,另有一种状况。假如一个注解内仅仅只要一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。

public @interface Check {
    String value();
}

上面代码中,Check 这个注解只要 value 这个属性。所以可以如许应用。

@Check("hi")
int a;

这和下面的效果是一样的

@Check(value="hi")
int a;

末了,还须要注重的一种状况是一个注解没有任何属性。比方

public @interface Perform {}

那末在应用这个注解的时刻,括号都可以省略。

@Perform
public void testMethod(){}

Java 预置的注解

进修了上面相干的学问,我们已可以自身定义一个注解了。实在 Java 言语自身已供应了几个现成的注解。

@Deprecated

这个元素是用来标记过期的元素,想必人人在一样平常开辟中常常碰到。编译器在编译阶段碰到这个注解时会发出提醒正告,通知开辟者正在挪用一个过期的元素比方过期的要领、过期的类、过期的成员变量。

public class Hero {
    
    @Deprecated
    public void say(){
        System.out.println("Noting has to say!");
    }
    
    
    public void speak(){
        System.out.println("I have a dream!");
    }
    
}

定义了一个 Hero 类,它有两个要领 say() 和 speak() ,个中 say() 被 @Deprecated 注解。然后我们在 IDE 中离别挪用它们。
可以看到,say() 要领上面被一条直线划了一条,这实在就是编译器识别后的提醒效果。

@Override
这个人人应当很熟习了,提醒子类要复写父类中被 @Override 润饰的要领

@SuppressWarnings
阻挠正告的意义。之前说过挪用被 @Deprecated 注解的要领后,编译器会正告提醒,而有时刻开辟者会疏忽这类正告,他们可以在挪用的处所经由过程 @SuppressWarnings 到达目的。

@SuppressWarnings("deprecation")
public void test1(){
    Hero hero = new Hero();
    hero.say();
    hero.speak();
}

@SafeVarargs
参数平安范例注解。它的目的是提醒开辟者不要用参数做一些不平安的操纵,它的存在会阻挠编译器发作 unchecked 如许的正告。它是在 Java 1.7 的版本中到场的。

@SafeVarargs // Not actually safe!
    static void m(List<String>... stringLists) {
    Object[] array = stringLists;
    List<Integer> tmpList = Arrays.asList(42);
    array[0] = tmpList; // Semantically invalid, but compiles without warnings
    String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}

上面的代码中,编译阶段不会报错,然则运转时会抛出 ClassCastException 这个非常,所以它虽然通知开辟者要妥善处置惩罚,然则开辟者自身还是搞砸了。
Java 官方文档说,将来的版本会受权编译器对这类不平安的操纵发作毛病正告。

@FunctionalInterface
函数式接口注解,这个是 Java 1.8 版本引入的新特征。函数式编程很火,所以 Java 8 也实时添加了这个特征。
函数式接口 (Functional Interface) 就是一个具有一个要领的一般接口。
比方

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

我们举行线程开辟中经常使用的 Runnable 就是一个典范的函数式接口,上面源码可以看到它就被 @FunctionalInterface 注解。
能够有人会迷惑,函数式接口标记有什么用,这个原因是函数式接口可以很轻易转换为 Lambda 表达式。这是别的的主题了,有兴致的同砚请自身搜刮相干学问点进修。

注解的提取

博文前面的部份讲了注解的基础语法,如今是时刻检测我们所学的内容了。
我经由过程用标签来比作注解,前面的内容是讲怎样写注解,然后贴到哪一个处所去,而如今我们要做的事变就是校阅阅兵这些标签内容。 抽象的比方就是你把这些注解标签在适宜的时刻撕下来,然后校阅阅兵上面的内容信息。
要想准确校阅阅兵注解,离不开一个手腕,那就是反射。
注解与反射。
注解经由过程反射猎取。起首可以经由过程 Class 对象的 isAnnotationPresent() 要领推断它是不是应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后经由过程 getAnnotation() 要领来猎取 Annotation 对象。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或许是 getAnnotations() 要领。

public Annotation[] getAnnotations() {}

前一种要领返回指定范例的注解,后一种要领返回注解到这个元素上的一切注解。

假如猎取到的 Annotation 假如不为 null,则就可以挪用它们的属性要领了。比方

@TestAnnotation()
public class Test {
    
    public static void main(String[] args) {
        
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}
顺序的运转效果是:
  id:-1
  msg:

这个恰是 TestAnnotation 中 id 和 msg 的默认值。

上面的例子中,只是校阅阅兵出了注解在类上的注解,实在属性、要领上的注解还是是可以的。一样还是要假手于反射。

@TestAnnotation(msg="hello")
public class Test {
    
    @Check(value="hi")
    int a;
    
    
    @Perform
    public void testMethod(){}
    
    
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }


    public static void main(String[] args) {
        
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //猎取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        
        
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //猎取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            
            if ( testMethod != null ) {
                // 猎取要领中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        
        

    }

}

 它们的效果以下
 id:-1
 msg:hello
 check value:hi
 method testMethod annotation:Perform

须要注重的是,假如一个注解要在运转时被胜利提取,那末 @Retention(RetentionPolicy.RUNTIME) 是必需的。

注解的运用场景

我置信博文讲到这里人人都很熟习了注解,然则有不少同砚肯定会问,注解到底有什么用呢?
对啊注解到底有什么用?
我们无妨将眼光放到 Java 官方文档上来。
文章最先的时刻,我用标签来类比注解。但标签比方只是我的手腕,而不是目的。为的是让人人在首次进修注解时可以不被那些笼统的新观点搞懵。既然如今,我们已对注解有所相识,我们无妨再仔细阅读官方最严谨的文档。
注解是一系列元数据,它供应数据用来诠释顺序代码,然则注解并非是所诠释的代码自身的一部份。注解关于代码的运转效果没有直接影响。
注解有很多用途,重要以下:
供应信息给编译器: 编译器可以应用注解来探测毛病和正告信息
编译阶段时的处置惩罚: 软件东西可以用来应用注解信息来生成代码、Html文档或许做别的响应处置惩罚。
运转时的处置惩罚: 某些注解可以在顺序运转的时刻接收代码的提取
值得注重的是,注解不是代码自身的一部份。

假如难于明白,可以如许看。标签只是某些人关于其他事物的评价,然则标签不会转变事物自身,标签只是特定人群的手腕。所以,注解一样没法转变代码自身,注解只是某些东西的东西。
还是回到官方文档的诠释上,注解重要针对的是编译器和别的东西软件(SoftWare tool)。
当开辟者运用了Annotation 润饰了类、要领、Field 等成员以后,这些 Annotation 不会自身见效,必需由开辟者供应响应的代码来提取并处置惩罚 Annotation 信息。这些处置惩罚提取和处置惩罚 Annotation 的代码统称为 APT(Annotation Processing Tool)。

如今,我们可以给自身答案了,注解有什么用?给谁用?给 编译器或许 APT 用的。
假如,你还是没有搞清楚的话,我亲身写一个好了。
亲手自定义注解完成某个目的
我要写一个测试框架,测试顺序员的代码有没有显著的非常。
—— 顺序员 A : 我写了一个类,它的名字叫做 NoBug,由于它一切的要领都没有毛病。
—— 我:自信是功德,不过为了防备不测,让我测试一下怎样?
—— 顺序员 A: 怎样测试?
—— 我:把你写的代码的要领都加上 @Jiecha 这个注解就好了。
—— 顺序员 A: 好的。

NoBug.java
package ceshi;
import ceshi.Jiecha;
public class NoBug {
    
    @Jiecha
    public void suanShu(){
        System.out.println("1234567890");
    }
    @Jiecha
    public void jiafa(){
        System.out.println("1+1="+1+1);
    }
    @Jiecha
    public void jiefa(){
        System.out.println("1-1="+(1-1));
    }
    @Jiecha
    public void chengfa(){
        System.out.println("3 x 5="+ 3*5);
    }
    @Jiecha
    public void chufa(){
        System.out.println("6 / 0="+ 6 / 0);
    }
    
    public void ziwojieshao(){
        System.out.println("我写的顺序没有 bug!");
    }
}

上面的代码,有些要领上面应用了 @Jiecha 注解。

这个注解是我写的测试软件框架中定义的注解。

package ceshi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {
}

然后,我再编写一个测试类 TestTool 就可以测试 NoBug 响应的要领了。

package ceshi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;



public class TestTool {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        NoBug testobj = new NoBug();
        
        Class clazz = testobj.getClass();
        
        Method[] method = clazz.getDeclaredMethods();
        //用来纪录测试发作的 log 信息
        StringBuilder log = new StringBuilder();
        // 纪录非常的次数
        int errornum = 0;
        
        for ( Method m: method ) {
            // 只要被 @Jiecha 标注过的要领才举行测试
            if ( m.isAnnotationPresent( Jiecha.class )) {
                try {
                    m.setAccessible(true);
                    m.invoke(testobj, null);
                
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    //e.printStackTrace();
                    errornum++;
                    log.append(m.getName());
                    log.append(" ");
                    log.append("has error:");
                    log.append("\n\r  caused by ");
                    //纪录测试历程当中,发作的非常的称号
                    log.append(e.getCause().getClass().getSimpleName());
                    log.append("\n\r");
                    //纪录测试历程当中,发作的非常的详细信息
                    log.append(e.getCause().getMessage());
                    log.append("\n\r");
                }
            }
        }
        
        
        log.append(clazz.getSimpleName());
        log.append(" has  ");
        log.append(errornum);
        log.append(" error.");
        
        // 生成测试报告
        System.out.println(log.toString());

    }

}
  测试的效果是:
  1+1=11
  1-1=0
x 5=15" has  "          log.append(" error."                       }

chufa has error:
caused by ArithmeticException
/ by zero
NoBug has 1 error.

提醒 NoBug 类中的 chufa() 这个要领有非常,这个非常称号叫做 ArithmeticException,原因是运算历程当中举行了除 0 的操纵

所以,NoBug 这个类有 Bug。

如许,经由过程注解我完成了我自身的目的,那就是对他人的代码举行测试。
所以,再问我注解什么时刻用?我只能通知你,这取决于你想应用它干什么用。

总结

假如注解难于明白,你就把它类同于标签,标签为了诠释事物,注解为了诠释代码。
注解的基础语法,建立犹如接口,然则多了个 @ 标记。
注解的元注解。
注解的属性。
注解重要给编译器及东西范例的软件用的。
注解的提取须要借助于 Java 的反射手艺,反射比较慢,所以注解运用时也须要郑重计算时候本钱。

以上就是Java注解的细致引见(代码示例)的细致内容,更多请关注ki4网别的相干文章!

分享到:

发表评论

评论列表

还没有评论,快来说点什么吧~

公众号二维码

微信公众号