天天看点

Java高级学习之路之篇十三(反射、注解)JAVA 反射JAVA Annotation 注解

JAVA 反射

1.什么是反射

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

反射就是将java类中的各种成分映射成相应的java类,这样就可以对各个组成部分进行剖析,得到每个组成部分,并对每一部分进行操作。在比较复杂的程序或框架中来使用反射技术,可以简化代码提高程序的复用性。

例如,一个java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示。表示java类的Class类显然要提供一系列的方法来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,它们是Field,Method,Constructor,Package等等。

一个类中每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class实例对象的方法可以得到这些实例对象并加以应用。

2.反射的作用

Java反射API的第一个主要作用:

获取程序在运行时刻的内部结构。这对于程序的检查工具和调试器来说,是非常实用的功能。只需要短短的十几行代码,就可以遍历出来一个Java类的内部结构,包括其中的构造方法、声明的域和定义的方法等。这不得不说是一个很强大的能力。只要有了java.lang.Class类的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。这三个方法还有相应的getDeclaredXXX版本,区别在于getDeclaredXXX版本的方法只会获取该类自身所声明的元素,而不会考虑继承下来的。Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据。

反射API的另外一个作用:

在运行时刻对一个Java对象进行操作。这些操作包括动态创建一个Java类的对象,获取某个域的值以及调用某个方法。在Java源代码中编写的对类和对象的操作,都可以在运行时刻通过反射API来实现。

3.反射常用方法

// 获取类(class)
getClass()

getClassLoader()

// 获取构造器(constructor)
getConstructor()

getDeclaredConstructor()

// 获取方法(method)
getMethod()

getDeclaredMethod()

// 获取属性值(field)
getField()

getDeclaredField()
           

4.反射的使用

下面一个简单的Java类:

class MyClass {
    public int count;
    public MyClass(int start) {     // 构造方法
        count = start;
    }
    public void increase(int step) {
        count = count + step;
    }
} 
           

一般做法:

MyClass myClass = new MyClass(0); //实例化
myClass.increase(2);
System.out.println("Normal -> " + myClass.count);
           

反射API做法: 

try {
    Constructor constructor = MyClass.class.getConstructor(int.class); //获取构造方法

    MyClass myClassReflect = constructor.newInstance(10); //创建对象

    Method method = MyClass.class.getMethod("increase", int.class);  //获取方法

    method.invoke(myClassReflect, 5); //调用方法

    Field field = MyClass.class.getField("count"); //获取域

    System.out.println("Reflect -> " + field.getInt(myClassReflect)); //获取域的值

} catch (Exception e) { 

    e.printStackTrace();

} 
           

使用Java反射API的时候可以绕过Java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要在获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true即可。有了这种机制,就可以很方便的在运行时刻获取到程序的内部状态。

JAVA Annotation 注解

1.什么是注解(Annotation)

Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类
  • Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法
  • Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据
  • Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中

2.基本Annotation

  1. @Override  限定重写父类方法,该注解只能用于方法
  2. @Inherited 指定注解是可继承的
  3. @Target 指定注解的运用目标,该注解用在什么上
    ElementType.ANNOTATION_TYPE  // 可以给一个注解进行注解 
    ElementType.CONSTRUCTOR      // 可以给构造方法进行注解
    ElementType.FIELD            // 可以给属性进行注解
    ElementType.LOCAL_VARIABLE   // 可以给局部变量进行注解
    ElementType.METHOD           // 可以给方法进行注解
    ElementType.PACKAGE          // 可以给一个包进行注解
    ElementType.PARAMETER        // 可以给一个方法内的参数进行注解
    ElementType.TYPE             // 可以给一个类型进行注解,比如类、接口、枚举
               
  4. @Retention 注解的生命周期:应该可以用生命周期这个词来形容吧..
    RetentionPolicy.SOURCE   // 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
    RetentionPolicy.CLASS    // 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
    RetentionPolicy.RUNTIME  /* 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,
                                所以在程序运行时可以获取到它们 */
               
  5. @Repeatable jdk1.8新特性,对于一个域可重复添加该注解
  6. @Documented 它的作用是能够将注解中的元素包含到 Javadoc 中去。

3.自定义Annotation

  • 使用@interface定义Annotation
  • 使用Annotation修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前
  • 定义带成员变量的Annotation
  • 为Annotation的成员变量指定初始值

4.提取Annotation信息

  • Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的父接口
  • AnnotatedElement接口代表程序中可以接受注释的程序元素
  • 调用AnnotatedElement对象的如下三个方法来访问Annotation信息:
  • getAnnotation(Class<T> annotationClass):返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null
  • Annotation[] getAnnotations():返回该程序元素上存在的所有注释
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false

5.Annotation 注解实例1

自定义Annotation:

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;  
 /** 
  * 定义一个注解 
  */  
 @Target(ElementType.METHOD) // 这是一个对方法的注解,还可以是包、类、变量等很多东西  
 @Retention(RetentionPolicy.RUNTIME) // 保留时间,一般注解就是为了框架开发时代替配置文件使用, 
                                        JVM运行时用反射取参数处理,所以一般都为RUNTIME类型。 
 @Documented // 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如 
                javadoc此类的工具文档化。
 public @interface OneAnnotation {  
     // 定义注解的参数,类型可以为基本类型以及String、Class、enum、数组等,default为默认值  
     String parameter1() default "";  
     int parameter2() default -1;  
 }  
           

使用注解:

/** 
  * 一个用到了自定义的注解的类 
  */  
 public class OClass {  
     @OneAnnotation(parameter1="YES", parameter2=10)  
     public void oneMethod () {  
     }  
 }  
           

获取注解参数:

import java.lang.reflect.Method;  

 public class TestDemo {  
     public static void main(String[] args) throws Exception {  
         // 提取到被注解的方法Method,这里用到了反射的知识  
         Method method = Class.forName("OneClass").getDeclaredMethod("oneMethod");  
         // 从Method方法中通过方法getAnnotation获得我们设置的注解  
         OneAnnotation oneAnnotation = method.getAnnotation(OneAnnotation.class);  
         // 得到注解的俩参数  
         System.out.println(oneAnnotation.parameter1());  
         System.out.println(oneAnnotation.parameter2());  
     }  
 }  
           

6.Annotation 注解实例2

Demo功能:使用一个工厂类来根据传递的类名来创建实例,并用注解标明需要创建实例的个数。

6.1.创建一个注解类,用于表明类需要被实例的个数

package hbi.banktest.dto;

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


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface  Reflection{
    int num() default 1;
}
           

6.2.创建测试bean并加入注解

package hbi.banktest.dto;


@Reflection(num = 5)
public class TestBean {
    private long id;
    private String name;
    private static final long DEFAULT_ID = 1;
    private static final String DEFAULT_NAME = "xixi";

    public static long i = 0;
    public TestBean(){
        id = DEFAULT_ID + i++;
        name = DEFAULT_NAME + id;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String toString(){
        return id+","+name;
    }
}
           

6.3.创建工厂类并测试

package hbi.banktest.dto;


import java.util.ArrayList;
import java.util.List;


public class ReflectionFactory<T> {
    public List<T> getBean(Class<T> clazz){
        System.out.println("aaa");
        List<T> list = new ArrayList<>();
        Reflection reflection = clazz.getAnnotation(Reflection.class);
        //Assert.notNull(reflection,"无Reflection注解");
        if(reflection == null){return null;}
        int length = reflection.num();
        try {
            for (int i =0;i < length;i++){
                T t = clazz.newInstance();
                list.add(t);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return list;
    }

    public static void main(String[] args) {
        ReflectionFactory reflectionFactory = new ReflectionFactory();
        List list = reflectionFactory.getBean(TestBean.class);
        System.out.println(list.size());
        for (int i = 0;i < list.size();i++){
            System.out.println(list.get(i).toString());
        }
    }
}

/*
aaa
5
1,xixi1
1,xixi1
1,xixi1
1,xixi1
1,xixi1
*/