注解跟反射的一些基本介绍

一、什么是注解

1.1 注解的基本介绍与作用

跟注释(comment)不同,注释是写给人看的。而注解(Annotation)可以写给程序执行分析,也就是不仅写给人看,还会给程序来看。

注解是jdk5.0开始引入的新技术

注解有什么作用呢?

  1. 编译检查
    Annotation具有“让编译器进行编译检查的作用”。
    例如,@SuppressWarnings(用于抑制编译时的警告信息,这个注解与后面两个不同,这个需要添加参数才能被正确的使用), @Deprecated(标明已废弃,或者不推荐使用)和@Override(重写父类的方法)都具有编译检查作用。
  2. 在反射中使用Annotation
    在反射的Class, Method, Field等函数中,有许多于Annotation相关的接口。
    这也意味着,我们可以在反射中解析并使用Annotation。
  3. 根据Annotation生成帮助文档
    通过给Annotation注解加上@Documented标签,能使该Annotation标签出现在javadoc中。
  4. 能够帮忙查看查看代码
    通过@Override, @Deprecated等,我们能很方便的了解程序的大致结构。
    另外,我们也可以通过自定义Annotation来实现一些功能。
    5.注解处理器
    如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。
  5. 在框架中的作用
    在开发Java程序,尤其是Java EE应用的时候,总是免不了与各种配置文件打交道。以Java EE中典型的S(pring)S(truts)H(ibernate)架构来说,Spring、Struts和Hibernate这三个框架都有自己的XML格式的配置文件。这些配置文件需要与Java源代码保存同步,否则的话就可能出现错误。而且这些错误有可能到了运行时刻才被发现。把同一份信息保存在两个地方,总是个坏的主意。理想的情况是在一个地方维护这些信息就好了。其它部分所需的信息则通过自动的方式来生成。JDK 5中引入了源代码中的注解(annotation)这一机制。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。
    因为注解大多都有自己的配置参数,而配置参数以名值对的方式出现,所以从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。
    而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML文件移到了代码本身之中,在一个地方管理和维护。
    上面两段话其实已经阐述了java注解的主要作用之一,就是跟踪代码依赖性,实现替代配置文件功能。比较常见的是Spring等框架中的基于注解配置。现在的框架很多都使用了这种方式来减少配置文件的数量。基本上秉持着这么一个原则,与具体场景相关的配置应该使用注解的方式与数据关联,与具体场景无关的配置放于配置文件中。在另一方面我们还可以在通过设置注解的@Retention 级别在运行时使用反射对不同的注解进行处理。

注解的格式:

    >注解是以“@注释名”在代码中存在的,还可以添加一些参数值,就比如:@SuppressWarnings(value="unchecked")

注解可以在哪些地方使用:

   >在自定义注解的时候,会有@Target(),里面的参数表面使用的地方,比如有package,class,method,field等上面,就相当于给他们添加额外的辅助信息。我们还可以通过反射机制编程实现对这些元数据的访问。

1.2 常见注解的程序说明

1.2.1 内置注解

@Override注解

注解跟反射的一些基本介绍_第1张图片

注解跟反射的一些基本介绍_第2张图片

 

@Deprecated

注解跟反射的一些基本介绍_第3张图片

@SuppressWarnings("all") 消除警告,需要有参数,这个注释在类上,方法上都可以

注解跟反射的一些基本介绍_第4张图片

注解跟反射的一些基本介绍_第5张图片

查看一下源码

注解跟反射的一些基本介绍_第6张图片

1.2.2 元注解

元注解的作用:负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明;

在哪里:这些类型和他们所支持的类在java.lang.annotation包中可以找到,这里有4个元注解(@Target,@Retention,@Documented,@Inherited)

1.@Target: 用于描述注解的使用范围(比如描述注解使用在什么地方)

注解跟反射的一些基本介绍_第7张图片

注解跟反射的一些基本介绍_第8张图片

查看一下ElementType的源代码,可以发现它是一个枚举类,里面标明了一些列参数

注解跟反射的一些基本介绍_第9张图片

元注解简单案例:

public class Test02 {

    @MyTest
    public void test(){

    }

}

//自定义一个注解,指明可以作用在方法和类上
@Target(value={ElementType.METHOD,ElementType.TYPE})
//表示在什么地方还有效  SOURCE

2.@Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE

3.@Documented:说明该注解将被包含在javadoc中

4.@Inherited:说明子类可以继承父类中的该注解

 

1.2.3 自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

自定义注解的一些说明:

      1.@interface用来声明一个注解,格式:public @interface 注解名{自定义内容}

      2.其中的每一个方法实际上时声明了一个配置参数

      3.方法的名称就是参数的名称

      4.返回值类型就是参数的类型(返回值类型只能是基本类型,Class,String,enum)

      5.可以通过default来声明参数的默认值

      6.如果只能一个参数成员,一般参数名为value

      7.注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值

 

二、反射的探究

反射是框架设计的灵魂

使用前提:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并且能够直接操作任意对象的内部属性以及方法。

 

Class c = Class.forName("java.lang.String")

正常方式:引入需要的“包类”名称 ->通过new实例化 ->取得实例化对象

反射方式:实例化对象 -> getClass()方法  -> 得到完整的“包类”名称

 

2.1 反射的概述

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

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

2.2 什么是反射

反射就是把Java类中的各个成分映射成一个个的Java对象成一个个对象。

     (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)

如图是类的正常加载过程:反射的原理在与class对象。

熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

注解跟反射的一些基本介绍_第10张图片

2.3 查看Class类在Java中的api详解(1.7的API)

如何阅读java中的api详见java基础之——String字符串处理

注解跟反射的一些基本介绍_第11张图片

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

Class类常用的方法

注解跟反射的一些基本介绍_第12张图片

 

没有公共的构造方法,方法共有64个

注解跟反射的一些基本介绍_第13张图片

2.4 反射的使用

先写一个Student类。

public class Student {

    //学号
    private String sno;

    //姓名
    private String name;

    //年龄
    private int age;

    public String getSno() {
        return sno;
    }

    public void setSno(String sno) {
        this.sno = sno;
    }

    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 Student(String sno, String name, int age) {
        this.sno = sno;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sno='" + sno + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

1、获取Class对象的三种方式

1.1 Object ——> getClass();
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String  className)(常用)

 

其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取

注解跟反射的一些基本介绍_第14张图片

public class Reflect1 {

    public static void main(String[] args) {
        //第一种方式获取Class对象  
        Student stu1 = new Student("001","小莫",18);//这一new 产生一个Student对象,一个Class对象。
        Class stuClass = stu1.getClass();//获取Class对象
        System.out.println(stuClass.getName());

        //第二种方式获取Class对象
        Class stuClass2 = Student.class;
        System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

        //第三种方式获取Class对象
        try {
            Class stuClass3 = Class.forName("de.wen.dewen.entity.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出结果:

注解跟反射的一些基本介绍_第15张图片

 

注意:在运行期间,一个类,只有一个Class对象产生。

三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

2.5 所有类型的Class对象

public class Reflect2 {
    public static void main(String[] args) {
        Class c1 = Object.class; //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[].class;   //一维数组
        Class c4 = int[][].class;    //二维数组
        Class c5 = Override.class;   //注解
        Class c6 = ElementType.class;//枚举
        Class c7 = Integer.class;    //基本数据类型
        Class c8 = void.class;       //void
        Class c9 = Class.class;      //Class

        //按Alt键可以复制区域内的多行
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
        
        //只要元素类型跟维度一样,就属于属于同一个Class
        int [] a = new int[10];
        int [] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }

}

打印结果:

class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
41359092
41359092

 

你可能感兴趣的:(注解跟反射的一些基本介绍)