`

Java的反射与注解

 
阅读更多

                                                          Java反射与注解

转载:https://www.daidingkang.cc/2017/07/18/java-reflection-annotations/

1.Java反射:

         我们知道Java编译器编译后会把.class文件装入虚拟机,而通过Java反射机制()就可以在程序中访问已经装载到JVM中的java对象的描述,实现访问和修改描述Java对象本身信息的功能。在java.lang.reflect包中提供了对该功能的支持。

 

2.反射机制:

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

用一句话总结就是反射可以实现在运行时可以知道任意一个类属性和方法

 

3.反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理(ps:这个知识点也很重要,后续会为大家讲到)

4.反射机制应用场景:

  • 逆向代码 ,例如反编译
  • 与注解相结合的框架 例如Retrofit
  • 单纯的反射机制应用框架 例如EventBus
  • 动态生成类框架 例如Gson

5.反射机制的优缺点:

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念

  • 静态编译:在编译时确定类型,绑定对象,即通过。

  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

优点

  • 可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

缺点

  • 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

6.类和类类型

想要了解反射首先理解一下Class类,它是反射实现的基础。
类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)
对于普通的对象,我们一般都会这样创建和表示:

1
Code code1 = new Code();

上面说了,所有的类都是Class的对象,那么如何表示呢,可不可以通过如下方式呢:

1
Class c = new Class();

但是我们查看Class的源码时,是这样写的:

1
2
3
private  Class(ClassLoader loader) { 
    classLoader = loader; 
}

可以看到构造器是私有的,只有JVM可以创建Class的对象,因此不可以像普通类一样new一个Class对象,虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:

 

这说明任何一个类都有一个隐含的静态成员变量class,
这种方式是通过获取类的静态成员变量class得到的
Class c1 = Code.class;
code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 
Class c2 = code1.getClass(); 
这种方法是Class类调用forName方法,通过一个类的全量限定名获得
Class c3 = Class.forName("com.trigl.reflect.Code");

这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做Code的类类型(class type)。
这里就让人奇怪了,前面不是说Code是Class的对象吗,而c1、c2、c3也是Class的对象,那么Code和c1、c2、c3不就一样了吗?为什么还叫Code什么类类型?这里不要纠结于它们是否相同,只要理解类类型是干什么的就好了,顾名思义,类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。

举个简单例子代码:

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种:Class c1 = Code.class;
        Class class1=ReflectDemo.class;
        System.out.println(class1.getName());

        //第二种:Class c2 = code1.getClass();
        ReflectDemo demo2= new ReflectDemo();
        Class c2 = demo2.getClass();
        System.out.println(c2.getName());

        //第三种:Class c3 = Class.forName("com.trigl.reflect.Code");
        Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
        System.out.println(class3.getName());
    }
}

 

执行结果:

1
2
3
com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo

7.反射相关操作

在这里先看一下sun为我们提供了那些反射机制中的类:
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;

前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
总结如下:

  • 获取成员方法Method
  • 获取成员变量Field
  • 获取构造函数Constructor

下面来具体介绍

  1. 获取成员方法信息

    两个参数分别是方法名和方法参数类的类类型列表。

// 得到该类所有的方法,不包括父类的 
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
// 得到该类所有的public方法,包括父类的
public Method getMethod(String name, Class<?>... parameterTypes) 
//具体使用
Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法 
Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法 
//返回次Class对象对应类的、带指定形参列表的public方法 
Method method = class1.getMethod("info", String.class);
//返回次Class对象对应类的、带指定形参列表的方法
Method declaredMethod = class1.getDeclaredMethod("info", String.class);

 

举个例子:

例如类A有如下一个方法:

1
2
3
public void fun(String name,int age) {
        System.out.println("我叫"+name+",今年"+age+"岁");
    }

现在知道A有一个对象a,那么就可以通过:

Class c = Class.forName("com.tengj.reflect.Person");  //先生成class
Object o = c.newInstance();                           //newInstance可以初始化一个实例
Method method = c.getMethod("fun", String.class, int.class);//获取方法
 //通过invoke调用该方法,参数第一个为实例对象,后面为具体参数值
method.invoke(o, "tengj", 10);                        

 

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Person {
    private String name;
    private int age;
    private String msg="hello wrold";
 public String getName() {
        return name;
  }

    public void setName(String name) {
        this.name = name;
  }

    public int getAge() {
        return age;
  }

    public void setAge(int age) {
        this.age = age;
  }

    public Person() {
    }

    private Person(String name) {
        this.name = name;
  System.out.println(name);
  }

    public void fun() {
        System.out.println("fun");
  }

    public void fun(String name,int age) {
        System.out.println("我叫"+name+",今年"+age+"岁");
  }
}

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Object o = c.newInstance();
            Method method = c.getMethod("fun", String.class, int.class);
            method.invoke(o, "tengj", 10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

我叫tengj,今年10岁

怎样,是不是感觉很厉害,我们只要知道这个类的路径全称就能玩弄它于鼓掌之间。

有时候我们想获取类中所有成员方法的信息,要怎么办。可以通过以下几步来实现:

1.获取所有方法的数组:

1
2
3
4
Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods(); // 得到该类所有的方法,不包括父类的
或者:
Method[] methods = c.getMethods();// 得到该类所有的public方法,包括父类的

2.然后循环这个数组就得到每个方法了:

1
for (Method method : methods)

完整代码如下:
person类跟上面一样,这里以及后面就不贴出来了,只贴关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Method[] methods = c.getDeclaredMethods();
            for(Method m:methods){
                String  methodName= m.getName();
                System.out.println(methodName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

getName
setName
setAge
fun
fun
getAge

这里如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:

getName
setName
getAge
setAge
fun
fun
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
  1. 获取成员变量信息

想一想成员变量中都包括什么:成员变量类型+成员变量名

类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。

单独获取某个成员变量,通过Class类的以下方法实现:

参数是成员变量的名字

public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量

//具体实现
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性 
Field[] publicFields = class1.getFields();//获取class对象的public属性 
Field ageField = class1.getDeclaredField("age");//获取class指定属性 
Field desField = class1.getField("des");//获取class指定的public属性

 

举个例子:

例如一个类A有如下成员变量:

1
private int n;

如果A有一个对象a,那么就可以这样得到其成员变量:

1
2
Class c = a.getClass();
Field field = c.getDeclaredField("n");

完整代码如下:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            //获取成员变量
             //因为msg变量是private的,所以不能用getField方法
            Field field = c.getDeclaredField("msg"); 
            Object o = c.newInstance();
            //设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,
            //如果msg是public的就不需要这行了。
            field.setAccessible(true);
            Object msg = field.get(o);
            System.out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

hello wrold

同样,如果想要获取所有成员变量的信息,可以通过以下几步

1.获取所有成员变量的数组:

1
Field[] fields = c.getDeclaredFields();

2.遍历变量数组,获得某个成员变量field

1
for (Field field : fields)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Field[] fields = c.getDeclaredFields();
            for(Field field :fields){
                System.out.println(field.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

name
age
msg
  1. 获取构造函数

最后再想一想构造函数中都包括什么:构造函数参数
同上,类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。

单独获取某个构造函数,通过Class类的以下方法实现:

这个参数为构造函数参数类的类类型列表

//  获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
// 获得该类所以public构造器,包括父类
public Constructor<T> getConstructor(Class<?>... parameterTypes) 

//具体
//获取class对象的所有声明构造函数 
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();
//获取class对象public构造函数 
Constructor<?>[] publicConstructors = class1.getConstructors();
//获取指定声明构造函数 
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);
//获取指定声明的public构造函数
Constructor publicConstructor = class1.getConstructor(String.class);

举个例子:

例如类A有如下一个构造函数:

1
2
3
public A(String a, int b) {
    // code body
}

那么就可以通过:

1
Constructor constructor = a.getDeclaredConstructor(String.class, int.class);

来获取这个构造函数。

完整代码:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            //获取构造函数
            Constructor constructor = c.getDeclaredConstructor(String.class);
             //设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,
            //如果构造器是public的就不需要这行了。
            constructor.setAccessible(true);
            constructor.newInstance("tengj");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

tengj

注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:

1
fromClass.getDeclaredConstructor(String.class).newInstance("tengj");

获取所有的构造函数,可以通过以下步骤实现:

1.获取该类的所有构造函数,放在一个数组中:

1
Constructor[] constructors = c.getDeclaredConstructors();

2.遍历构造函数数组,获得某个构造函数constructor:

1
for (Constructor constructor : constructors)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
public class ReflectDemo {
    public static void main(String[] args){
            Constructor[] constructors = c.getDeclaredConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

public com.tengj.reflect.Person()
public com.tengj.reflect.Person(java.lang.String)
  1. 其他方法

注解需要用到的

//获取class对象的所有注解 
Annotation[] annotations = (Annotation[]) class1.getAnnotations(); 
//获取class对象指定注解 
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);
//获取class对象的直接超类的 
Type genericSuperclass = class1.getGenericSuperclass();
//获取class对象的所有接口的type集合
Type Type[] interfaceTypes = class1.getGenericInterfaces();

获取class对象的信息

 

boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型 
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类 
boolean isInterface = class1.isInterface();//判断是否是接口类 
boolean isEnum = class1.isEnum();//判断是否是枚举类 
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类 
//判断是否被某个注解类修饰
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);
String className = class1.getName();//获取class名字 包含包名路径 
Package aPackage = class1.getPackage();//获取class的包信息 
String simpleName = class1.getSimpleName();//获取class类名 
int modifiers = class1.getModifiers();//获取class访问权限 
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类 
Class<?> declaringClass = class1.getDeclaringClass();//外部类

getSuperclass():获取某类的父类  
getInterfaces():获取某类实现的接口

通过反射了解集合泛型的本质

首先下结论:

Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。

下面通过一个实例来验证:

 

/**
 * 集合泛型的本质
 */
public class GenericEssence {
    public static void main(String[] args) {
        List list1 = new ArrayList(); // 没有泛型 
        List<String> list2 = new ArrayList<String>(); // 有泛型


        /*
         * 1.首先观察正常添加元素方式,在编译器检查泛型,
         * 这个时候如果list2添加int类型会报错
         */
        list2.add("hello");
//      list2.add(20); // 报错!list2有泛型限制,只能添加String,添加int报错
        System.out.println("list2的长度是:" + list2.size()); // 此时list2长度为1


        /*
         * 2.然后通过反射添加元素方式,在运行期动态加载类,首先得到list1和list2
         * 的类类型相同,然后再通过方法反射绕过编译器来调用add方法,看能否插入int
         * 型的元素
         */
        Class c1 = list1.getClass();
        Class c2 = list2.getClass();
        System.out.println(c1 == c2); // 结果:true,说明类类型完全相同

        // 验证:我们可以通过方法的反射来给list2添加元素,这样可以绕过编译检查
        try {
            Method m = c2.getMethod("add", Object.class); // 通过方法反射得到add方法
            m.invoke(list2, 20); // 给list2添加一个int型的,上面显示在编译器是会报错的
            // 结果:2,说明list2长度增加了,并没有泛型检查
            System.out.println("list2的长度是:" + list2.size()); 
        } catch (Exception e) {
            e.printStackTrace();
        }

        /*
         * 综上可以看出,在编译器的时候,泛型会限制集合内元素类型保持一致,
         * 但是编译器结束进入
         * 运行期以后,泛型就不再起作用了,即使是不同类型的元素也可以插入集合。
         */
    }
}

 

执行结果:

list2的长度是:1
true
list2的长度是:2

思维导图

有助于理解上述所讲的知识点

 

 

JAVA注解

概念及作用

  1. 概念
  • 注解即元数据,就是源代码的元数据
  • 注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
  • Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
  1. 作用
  • 生成文档
  • 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
  • 在编译时进行格式检查,如@Override等
  • 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。

什么是java注解?

在java语法中,使用@符号作为开头,并在@后面紧跟注解名。被运用于类,接口,方法和字段之上,例如:

1
2
3
4
@Override
void myMethod() { 
......
}

这其中@Override就是注解。这个注解的作用也就是告诉编译器,myMethod()方法覆写了父类中的myMethod()方法。

java中内置的注解

java中有三个内置的注解:

  • @Override:表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。
  • @Deprecated:如果使用此注解,编译器会出现警告信息。
  • @SuppressWarnings:忽略编译器的警告信息。

 

元注解

自定义注解的时候用到的,也就是自定义注解的注解;(这句话我自己说的,不知道对不对)

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

Java5.0定义的4个元注解:

  1. @Target

  2. @Retention

  3. @Documented

  4. @Inherited

java8加了两个新注解,后续我会讲到。

这些类型和它们所支持的类在java.lang.annotation包中可以找到。

@Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

类型 用途
CONSTRUCTOR 用于描述构造器
FIELD 用于描述域
LOCAL_VARIABLE 用于描述局部变量
METHOD 用于描述方法
PACKAGE 用于描述包
PARAMETER 用于描述参数
TYPE 用于描述类、接口(包括注解类型) 或enum声明

比如说这个注解表示只能在方法中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target({ElementType.METHOD})
public @interface MyCustomAnnotation {

}

//使用
public class MyClass {
   @MyCustomAnnotation
   public void myMethod()
{
    ......
   }
}

@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

类型 用途 说明
SOURCE 在源文件中有效(即源文件保留) 仅出现在源代码中,而被编译器丢弃
CLASS 在class文件中有效(即class保留) 被编译在class文件中
RUNTIME 在运行时有效(即运行时保留) 编译在class文件中(可通过反射调用)

使用示例:

/***
 * 字段注解接口
 */@Target(value = {ElementType.FIELD})//注解可以被添加在属性上
    //注解保存在JVM运行时刻,能够在运行时刻通过反射API来获取到注解的信息
@Retention(value = RetentionPolicy.RUNTIME)public @interface Column {    String name();//注解的name属性}

@Documented

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

作用:将注解包含在javadoc中

示例:

1
2
3
java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}

@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类型被发现,或者到达类继承结构的顶层。

作用:允许子类继承父类中的注解

示例,这里的MyParentClass 使用的注解标注了@Inherited,所以子类可以继承这个注解信息:

1
2
3
4
java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
}
1
2
3
4
@MyCustomAnnotation
public class MyParentClass { 
  ... 
}
1
2
3
public class MyChildClass extends MyParentClass { 
   ... 
}

自定义注解

格式

1
2
3
public @interface 注解名{
  定义体
}

注解参数的可支持数据类型:

  • 所有基本数据类型(int,float,double,boolean,byte,char,long,short)
  • String 类型
  • Class类型
  • enum类型
  • Annotation类型
  • 以上所有类型的数组

规则

  • 修饰符只能是public 或默认(default)
  • 参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和String,Enum,Class,annotations及这些类型的数组
  • 如果只有一个参数成员,最好将名称设为”value”
  • 注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常使用空字符串或0作默认值
  • 在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * test注解
 * @author ddk
 *
 */ 
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
    /**
     * id
     * @return
     */
    public int id() default -1;
    /**
     * name
     * @return
     */
    public String name() default "";
}

注解处理器类库

java.lang.reflect.AnnotatedElement

Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:

  •  Class:类定义
  •  Constructor:构造器定义
  •  Field:累的成员变量定义
  •  Method:类的方法定义
  •  Package:类的包定义

java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:

  • 方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
  • 方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
  • 方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
  • 方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
/***********注解声明***************/

/**
 * 水果名称注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

/**
 * 水果颜色注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};

    /**
     * 颜色属性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}

/**
 * 水果供应者注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;

    /**
     * 供应商名称
     * @return
     */
    public String name() default "";

    /**
     * 供应商地址
     * @return
     */
    public String address() default "";
}

/***********注解使用***************/

public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor=Color.RED)
    private String appleColor;

    @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
    private String appleProvider;

    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }

    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }

    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

/***********注解处理器***************/
//其实是用的反射


public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){

        String strFruitName=" 水果名称:";
        String strFruitColor=" 水果颜色:";
        String strFruitProvicer="供应商信息:";

        Field[] fields = clazz.getDeclaredFields();

        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

/***********输出结果***************/
public class FruitRun {

    /**
     * @param args
     */
    public static void main(String[] args) {

        FruitInfoUtil.getFruitInfo(Apple.class);

    }

}

====================================
 水果名称:Apple
 水果颜色:RED
 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦

Java 8 中注解新特性

  • @Repeatable 元注解,表示被修饰的注解可以用在同一个声明式或者类型加上多个相同的注解(包含不同的属性值)
  • @Native 元注解,本地方法
  • java8 中Annotation 可以被用在任何使用 Type 的地方
1
2
3
4
5
6
7
8
9
10
11
12
 //初始化对象时
String myString = new @NotNull String();
//对象类型转化时
myString = (@NonNull String) str;
//使用 implements 表达式时
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
...
}
//使用 throws 表达式时
public void validateValues() throws @Critical ValidationFailedException{
...
}

 

 

分享到:
评论

相关推荐

    Java自定义注解使用反射获取字段注解

    总结一下,Java的自定义注解结合反射机制,为我们提供了强大的元数据功能,可以用来增强代码的可读性、可维护性,甚至实现一些动态的功能。在实际开发中,自定义注解常用于框架的实现、AOP切面编程、配置管理等领域...

    利用java反射、注解及泛型模拟ORM实现

    这篇博文“利用java反射、注解及泛型模拟ORM实现”旨在探讨如何结合这三种技术来实现对象关系映射(ORM),这是一种将数据库表与Java对象之间进行绑定的技术,简化了数据操作。 首先,我们来理解一下这三个概念: ...

    java 通过反射获取类上注解,方法上注解,注解里的值及方法参数

    在Java编程中,注解...这个示例涵盖了Java中使用反射获取注解的基本操作,包括类、方法、注解的值和方法参数。通过实践这个例子,你可以更好地理解和掌握这些概念,从而在实际项目中更有效地利用注解和反射。

    java注解和反射的个人学习笔记

    java注解和反射的个人学习笔记

    Java基础入门四泛型反射注解.pdf

    注解是Java SE 5中的另一个新特性,它提供了一种机制,用于将任何信息或元数据与程序中的元素(如类、方法、变量等)关联起来。注解不会直接影响代码的操作,但可以被编译器读取,以执行某些特定任务,比如生成额外...

    java注解、java反射机制 jdbc 封装

    接着,我们来看Java反射机制。反射允许程序在运行时检查类、接口、字段和方法的信息,甚至可以动态调用方法和访问字段。在JDBC封装中,反射非常有用,因为它允许在运行时创建和执行SQL语句,而不必在代码中硬编码...

    java泛型反射注解

    先说一下遇到的问题:通过使用GSON泛型进行报文转换的时候想要对部分关键字段加密,...解决过程:首先通过反射获取到bean下的对象名称。 对象名称获取到了之后需要获取对应的值 对值进行加密,然后再重新赋值到该对象

    java自定义注解和通过反射获取注解

    Java自定义注解和通过反射获取注解是Java编程中重要的高级特性,它们极大地增强了代码的可读性和可维护性。注解(Annotation)是一种元数据,提供了在编译时和运行时对代码进行标记的方法,而反射(Reflection)则是...

    Java注解与反射原理说明

    当注解与反射结合时,我们可以利用反射机制来读取和处理注解中的信息。在Java中,`AnnotatedElement`接口提供了获取注解的方法,包括`getAnnotations()`和`getAnnotation(Class&lt;T&gt; annotationClass)`。 1. `...

    java 自定义注解验证

    Java 自定义注解验证是Java开发中的一个重要特性,它允许开发者创建自己的元数据,以便在编译时或运行时对代码进行验证和处理。自定义注解为代码提供了额外的信息,使得程序更具可读性、可维护性和灵活性。在本案例...

    Android-注解与反射注解知识点反射知识点注解java反射

    6. **注解与反射**:反射可以读取类、方法、字段上的注解信息,这在运行时动态处理逻辑、实现插件化等方面非常有用。 在`AnnotationDemo-master`这个项目中,可能包含了注解和反射的实践案例,例如通过注解定义接口...

    java注解和反射的12个demo.zip

    这些Demo旨在帮助学习者深入理解Java注解和反射的原理与应用,通过实际操作加深对这两个概念的认识,提高开发能力。在学习过程中,结合B站狂神的讲解,能够更有效地掌握这些高级特性,为日后的项目开发打下坚实的...

    servlet反射注解例子

    本示例“servlet反射注解例子”旨在帮助我们深入理解如何将这两种技术结合使用,以实现更加灵活和可维护的服务器端应用程序。本文将详细探讨反射和注解在Servlet中的应用。 首先,让我们来了解一下什么是反射。反射...

    Java反射动态修改注解的某个属性值.Java

    4. **替换原有注解**:使用`java.lang.reflect.Proxy`类创建一个代理对象,将新的代理注解与原始注解关联起来。这样,当我们通过反射访问注解时,实际上访问的是这个代理对象,从而实现了注解属性的动态修改。 需要...

    通过反射获取注解的属性值(源代码)

    通过反射获取注解的属性值 1、获取类上注解的值 1.1 定义注解类 1.2 定义使用注解的类 1.3 测试类 1.4 测试结果 2、获取属性变量上注解的值 2.1 定义注解类 2.2 定义使用注解的类 2.3 定义测试类 2.4 测试结果 3、...

    Java反射、泛型和注解实战之Spring核心注入IOC的实现

    通过这个实战项目,你可以深入理解Java反射、泛型和注解的用法,并且了解到如何利用它们构建一个基本的依赖注入系统,这将有助于你更好地理解和使用Spring框架。同时,这样的实践也有助于提升你的编程技能,使你能够...

    Spring IOC 原理 ,Java 反射实例,自定义注解

    总的来说,Spring的IOC结合Java反射和自定义注解,构建了一种灵活且强大的组件管理和依赖注入机制。通过深入理解这些概念,开发者可以更好地利用Spring框架,实现松耦合、高内聚的软件设计。在实际项目中,可以使用`...

    java基础之注解和反射

    java基础之注解和反射

    使用反射和注解模拟Spring的依赖注入

    在Java编程中,Spring...总的来说,理解和掌握反射与注解对于深入学习Spring框架至关重要。通过模拟Spring的依赖注入,开发者可以更好地领悟到IoC和DI的核心思想,从而在实际开发中更有效地利用Spring框架提供的功能。

    利用JAVA注解与反射 - 实现SQL语句自动生成

    利用JAVA注解实现SQL语句自动生成 编写对应Entity添加相关注解,并通过SqlUtil工具,传入相关参数生成SQL语句 例如:要生成对应数据库Person的SQL增删改查 SqlUtil.create(Person.class) SqlUtil.insert(Person....

Global site tag (gtag.js) - Google Analytics