Java反射与注解

一、Annotation(注解)

1、Annotation介绍

        Annotation,是Java语言中的一种特殊的元数据语法,可以被添加到Java代码中。类,方法,变量,参数,包都可以被标注。与Javadoc的标签不同,注解是可以被反射的,因为他们被编译器生成嵌入在 编译后文件,并保留在虚拟机中以便在运行时被索引。Annotation类型是一种接口,不会直接影响到程序的语义,只是作为注解(标识)存在,能够通过java反射API的方式提供对其信息的访问。


2、Annotation工作原理


         在java5.0 中Java.lang.reflect 提供的反射API被扩充了读取运行时annotation的能力。一个annotation类型被定义为runtime retention后,它才是在运行时可见,当class文件被装载时被保存在class文件中的annotation才会被虚拟机读取。  其中java.lang.reflect.AnnotatedElement是重要的接口,它代表了提供查询annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现,并间接地被Method类、Constructor类、 java.lang.reflect的Field类实现。而annotation中的方法参数可以通过Method类、Constructor类的 getParameterAnnotations()方法获得。


3、系统内置标准注解


@Override:用于修饰此方法覆盖了父类的方法,也就是子类要重写(override)父类的对应方法;
@Deprecated:用于修饰已经过时的方法,不建议被使用;
@SuppressWarnnings:用于通知java编译器禁止特定的编译警告。


4、元注解


元注解也就是注解的注解,java提供了四种元注解,其中两种是常用的,分别是Retention和Target。

@Retention元注解,表示需要在什么级别保存该注释信息(生命周期)。可选的RetentionPoicy参数包括:RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息

@Retention(RetentionPolicy.RUNTIME)
// 编译程序将Annotation存储于class文件中,可以由虚拟机读入
public @interface MyAnnotation {
    String hello() default "hello";
    String world();
}


@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
ElementType.CONSTRUCTOR: 构造器声明
ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
ElementType.LOCAL_VARIABLE: 局部变量声明
ElementType.METHOD: 方法声明
ElementType.PACKAGE: 包声明
ElementType.PARAMETER: 参数声明
ElementType.TYPE: 类、接口(包括注解类型)或enum声明

demo如下:

   自定义一个用于方法的注解

@Target(ElementType.METHOD)
public @interface MyTarget {
    String hello() default "hello";
}
package annotationVSreflect;
@MyTarget //注解到这编译器会报错
public class MyTargetTest {  
    @MyTarget //该注解(@MyTarget)已指明只能注解方法
    public void fun() {        
    }
}

@Documented将注解包含在JavaDoc中

@Inheried允许子类继承父类中的注解


5、自定义注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(ElementType.METHOD)//如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。
@Inherited
@Retention(RetentionPolicy.RUNTIME)

//使用@interface来声明一个注解(实际上是自动继承了java.lang.annotation.Annotation接口)
public @interface AnnotationTest {
    String value1() default "hello";//为注解设置String类型的属性Value1,并使用defalut关键字设置默认值
    String[] value3();              //设置数组类型的value3
}

自定义注解需要注意的细节:

1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。

2. 参数成员只能用public或默认(default)这两个访问权修饰。

3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组。

4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法。

5. 注解也可以没有定义成员, 只是这样注解就没有作用。


二、反射

       JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

       通俗说:可以通过一个类名来获取关于这个类的信息,比如:属性名、属性名的修饰符、方法名、方法的返回值、方法的修饰符等。反射还可以生成类的实例,通过这个实例定义属性、调用方法。

import java.lang.reflect.Method;
public class Reflect {
    public static void main(String[] args) {       
        try {
            Class c=Class.forName("java.util.HashSet");
            System.out.println("Test======"+c);
            Object o=c.newInstance();
            Method[] methods=c.getDeclaredMethods();
            for(Method method:methods){
            System.out.println("------------------------------------");
            System.out.println(method);
            }
            Method m1=c.getMethod("add", Object.class);
            m1.invoke(o, "cyq");
            m1.invoke(o, "hello");
            m1.invoke(o, "java");
            System.out.println(o);
            } catch (Exception e) {
            e.printStackTrace();
            }
    }
}

控制台打印信息:

Test======class java.util.HashSet
------------------------------------
public boolean java.util.HashSet.add(java.lang.Object)
------------------------------------
public boolean java.util.HashSet.remove(java.lang.Object)
------------------------------------
public java.lang.Object java.util.HashSet.clone()
------------------------------------
public void java.util.HashSet.clear()
------------------------------------
public boolean java.util.HashSet.contains(java.lang.Object)
------------------------------------
public boolean java.util.HashSet.isEmpty()
------------------------------------
public int java.util.HashSet.size()
------------------------------------
public java.util.Iterator java.util.HashSet.iterator()
------------------------------------
private void java.util.HashSet.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException
------------------------------------
private void java.util.HashSet.writeObject(java.io.ObjectOutputStream) throws java.io.IOException
[hello, java, cyq]


三、综合实例:

首先自定义两个注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String hello() default "hello";
    String world();
}
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation2 {

    String hello() default "hello";
}

然后做一个测试类MyTest

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class MyTest {
 
    @SuppressWarnings("unchecked")//java自带的注解Retention的policy为SOURCE
    @Deprecated                   //java自带的注解Retention的policy为RUNTIME
    @MyAnnotation(world = "jia")  //自定义的注解Retention的policy为RUNTIME
    @MyAnnotation2                //自定义的注解Retention的policy为CLASS
    public void TestMethod() {
        System.out.println("this is a method");
    }
    
    public static void main(String[] args) throws Exception {
        MyTest myTest=new MyTest();
        Class<MyTest> clazz=MyTest.class;
        Method method =clazz.getMethod("TestMethod",new Class[]{});//返回一个 Method 对象,
                                                                   //它反映此 Class 对象所表示的类或接口的指定公共成员方法。
        
        System.out.println("test==========="+clazz);
        System.out.println("test==========="+method);
        //AnnotatedElement接口中的方法isAnnotationPresent(),判断传入的注解类型是否存在
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            method.invoke(myTest,new Object[]{});
            //AnnotatedElement接口中的方法getAnnotation(),获取传入注解类型的注解
            MyAnnotation myAnnotation=method.getAnnotation(MyAnnotation.class);
            String hello=myAnnotation.hello();
            String world=myAnnotation.world();
            System.out.println("hello:"+hello+"world:"+world);
        } 
        System.out.println("-----------------------------------");

        //AnnotatedElement接口中的方法getAnnotations(),获取所有注解
        Annotation[] annotations = method.getAnnotations();
        //循环注解数组打印出注解类型的名字
        for (Annotation annotation : annotations) {
          System.out.println(annotation.annotationType().getName());
       }
      }
}

控制台打印信息:

test===========class annotationVSreflect.MyTest
test===========public void annotationVSreflect.MyTest.TestMethod()
this is a method
hello:helloworld:jia
-----------------------------------
java.lang.Deprecated
annotationVSreflect.MyAnnotation

分割线上:介绍了如何使用AnnotatedElement接口中的方法和反射去调用注解

分割线下:证明了只有定义了Retention的Policy为Runtime的注解才可以被反射读取出来


转载于:https://my.oschina.net/u/2416019/blog/479476

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值