java注解、反射整理

注解

注解简介

注解的英文就是 Annotation,是在JDK 1.5之后引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

注解就是给 java 代码加上一个标识规则,javac编译器在编译时就会去检测应用了该注解类的类是否符合标识规则,来约束编码规范。

元注解

Java目前只内置了三种标准注解,以及四种元注解。

内置注解

  • @Override:重写父类方法的声明
  • @Deprecated:修饰方法、类、属性,表示不推荐使用
  • @SuppressWarnings:抑制编译时的警告信息
package annotition;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: everbolee
 * @createDate: 2021/2/19
 */
public class AnnotationTest {
    public static final Logger logger =  LoggerFactory.getLogger(AnnotationTest.class);
    @Override
    public String toString(){
        return "hello";
    }

    @Deprecated
    public static void deprecatedTest(){
        logger.error("Deprecated....");
    }
    @Test
    public void deprecatedDemo(){
        deprecatedTest();
    }
    @SuppressWarnings("all")
    @Test
    public void suppressWarningsDemo(){
        List list = new ArrayList();
    }
}
1.@Target

表示支持注解的程序元素的种类,注解该用于什么地方,ElementType 注解修饰的元素类型,使用ElementType枚举类来表示:

CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:  包声明
PARAMETER:参数声明
TYPE:类,接口(包括注解类型)enum声明
ANNOTATION_TYPE:注解类型声明
TYPE_PARAMETER:类型参数声明  从jdk1.8开始  、 hide1.8
TYPE_USE:类型的使用 从jdk1.8开始 、 hide1.8
2.@Retention

表示保留时间的长短,需要在什么级别保存该注解信息, RetentionPolicy参数包括:

SOURCE:注解将被编译器丢弃。
CLASS:注解在class文件中可用,但会被VM丢弃。
RUNTIME:VM将在运行期也保留注解,因此可通过反射机制读取注解的信息。

RetentionPolicy 它是枚举类,分别简单了解三种类型:

  • RetentionPolicy.SOURCE

    当注解保留时期为 SOURCE 时:表示注解信息只会被保留到源码和编译期间,当javac编译器编译源代码后会将该注解去除,但是在编译期间,注解处理器是是可以处理这些注解的,在编译完成之后,这些注解信息就没有了。

  • RetentionPolicy.CLASS

    当注解保留时期为 CLASS 时:表示注解信息会被保留到编译时期,该注解信息会被编译到.class文件中,但不会被虚拟机加载到内存中,保留到 Javac 将.java文件编译成 .class文件,但是不会随着 ClassLoader 将该.class文件加载到内存中。

  • RetentionPolicy.RUNTIME

    .当注解保留时期为 RUNTIME 时:表示注解信息会被保留到"运行时期",这是可以通过反射获取该注解的信息。Javac会将源文件.java编译为.class文件,然后通过 ClassLoader 将.class文件加载到内存中成为字节码,这时就可以通过反射来获取对应的注解信息。

3.@Documented

将此注解包含在Javadoc中。 表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分。

4.@Inherited

允许子类继承父类中的注解表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。

自定义注解

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

规则:

  • @interface用来声明注解,格式:public @interface 注解名{内容}
  • 其中的每一个方法实际声明一个配置参数
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型,返回值只能是基本类型 Class,String,Enum
  • 可通过default来声明默认参数
  • 只有一个参数成员,一般参数名为value
  • 元注解必须要有值,定义注解元素时可用 “”、0作为默认值
public class MyAnnotationTest {
    public static final Logger logger =  LoggerFactory.getLogger(AnnotationTest.class);
    @MyAnnotation(name = "xiaoli")
    @Test
    public void myAnnotationDemo(){
        logger.error("test...");
    }

}
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    //参数:参数类型+参数名()
    String name() default "";
    int age() default 0;
    String[] nums() default {"1","2"};
}

反射

反射简介

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

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

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

优点:可以实现动态创建对象和编译,体现出很大灵活性

缺点:对性能有影响。

@Test
public void speedTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    long begin = System.currentTimeMillis();
    Person person = new Person();
    for (int i = 0; i < 1000000000; i++) {
        person.getAge();
    }
    long end = System.currentTimeMillis();
    logger.info("正常创建对象时间:{}",end-begin);
    Class<? extends Person> personClass = person.getClass();
    Method getAge = personClass.getDeclaredMethod("getAge", null);
    long reflectBegin = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
        getAge.invoke(person,null);
    }
    long reflectEnd = System.currentTimeMillis();
    logger.info("反射创建对象时间:{}",reflectEnd-reflectBegin);
}
//执行结果如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Af7wEtrD-1613808075403)(C:\Users\DB007\AppData\Roaming\Typora\typora-user-images\image-20210220144551232.png)]

类加载过程

1. 加载

加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。

通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。

  • 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式。

  • 从JAR包加载class文件,这种方式也是很常见的,前面介绍JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。

  • 通过网络加载class文件。

  • 把一个Java源文件动态编译,并执行加载。

    类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。

2. 连接

当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为如下3个阶段。

**1、验证:**验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。Java是相对C++语言是安全的语言,例如它有C++不具有的数组越界的检查。这本身就是对自身安全的一种保护。验证阶段是Java非常重要的一个阶段,它会直接的保证应用是否会被恶意入侵的一道重要的防线,越是严谨的验证机制越安全。验证的目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

四种验证做进一步说明:

**文件格式验证:**主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主,次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。

**元数据验证:**对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范。

**字节码验证:**最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。

**符号引用验证:**主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段,主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题。

**2、准备:**类准备阶段负责为类的静态变量分配内存,并设置默认初始值。

**3、解析:**将类的二进制数据中的符号引用替换成直接引用。说明一下:符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。该引用是和内存中的布局有关的,并且一定加载进来的。

3. 初始化

初始化是为类的静态变量赋予正确的初始值,准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

类加载时机

  1. 创建类的实例,也就是new一个对象

  2. 访问某个类或接口的静态变量,或者对该静态变量赋值

  3. 调用类的静态方法

  4. 反射(Class.forName(“com.lyb.load”))

  5. 初始化一个类的子类(会首先初始化子类的父类)

  6. JVM启动时标明的启动类,即文件名和类名相同的那个类

    除此之外,下面几种情形需要特别指出:

    对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。

类加载器

类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了。正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识。在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。

JVM预定义有三种类加载器,当一个 JVM启动的时候,Java开始使用如下三种类加载器:

1)根类加载器(bootstrap class loader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

2)扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。

3)系统类加载器(system class loader):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader。

类加载器加载Class大致要经过如下8个步骤:

  1. 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
  2. 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
  3. 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
  4. 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
  5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
  6. 从文件中载入Class,成功后跳至第8步。
  7. 抛出ClassNotFountException异常。
  8. 返回对应的java.lang.Class对象。

反射获取class对象的三种方式

  1. 类名.class属性
  2. 对象名.getClass()方法
  3. Class.forName(全类名)方法
public class ReflectTest {
    public static final Logger logger =  LoggerFactory.getLogger(ReflectTest.class);
    @Test
    public void test() throws ClassNotFoundException {
        Class<Person> personClass = Person.class;
        logger.info("{}",personClass);
        //--------------------------
        Person person = new Person();
        Class<? extends Person> personClass1 = person.getClass();
        logger.info("{}",personClass1);
        //--------------------------
        Class<?> personClass2 = Class.forName("reflect.Person");
        logger.info("{}",personClass2);
        //--------------------------
        logger.info("{}",personClass==personClass1);
        logger.info("{}",personClass==personClass2);
        logger.info("{}",personClass1==personClass2);
    }
}
class Person{
    public static final Logger logger =  LoggerFactory.getLogger(Person.class);
    private String name;
    private int age;
    private int id;
    public int m = 10;
    static {
        logger.info("Person类静态代码块加载。。。");
    }
    public Person() {
        logger.info("Person类构造方法执行。。。");
    }

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

    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 int getId() {
        return id;
    }

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

反射获取构造方法

方法名说明
Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
Constructor getConstructor(Class<?>… parameterTypes)返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造方法对象
    @Test
    public void getConstructorTest() throws ClassNotFoundException, NoSuchMethodException {
        Class<?> personClass = Class.forName("reflect.Person");
        //返回所有公共构造方法对象的数组
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            logger.info("{}",constructor);
        }
        //返回所有构造方法对象的数组
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            logger.info("{}",declaredConstructor);
        }
        //返回单个公共构造方法对象,没有则报异常NoSuchMethodException
        /*Constructor<?> constructor = personClass.getConstructor(int.class);
        logger.info("{}",constructor);*/
        //返回单个构造方法对象
        Constructor<?> constructor = personClass.getDeclaredConstructor(int.class);
        logger.info("{}",constructor);
    }
}

反射获取成员变量

方法名说明
Field[] getFields()返回所有公共成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredField(String name)返回单个成员变量对象
@Test
public void getFieldTest() throws ClassNotFoundException {
    Class<?> personClass = Class.forName("reflect.Person");
    //返回所有公共成员变量对象的数组
    Field[] fields = personClass.getFields();
    for (Field field : fields) {
        logger.info("{}",field);
    }
    //返回所有成员变量对象的数组
    fields = personClass.getDeclaredFields();
    for (Field field : fields) {
        logger.info("{}",field);
    }
    //返回单个公共成员变量对象
    try {
        Field string_everbo = personClass.getField("everbo");
        logger.info("{}",string_everbo);
    } catch (NoSuchFieldException e) {
        logger.info("{}",e);
    }
    //返回单个成员变量对象
    try {
        Field xiaoli = personClass.getDeclaredField("name");
        logger.info("{}",xiaoli);
    } catch (NoSuchFieldException e) {
        logger.info("{}",e);
    }
}

通过反射获取注解

package reflect;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * @author: everbolee
 * @createDate: 2021/2/20
 */
public class GetAnnotationTest {
    public static final Logger logger =  LoggerFactory.getLogger(GetAnnotationTest.class);
    @Test
    public void getAnnotationDemo() throws ClassNotFoundException, NoSuchFieldException {
        Class student = Class.forName("reflect.Student");

        //获取注解
        Annotation[] annotations = student.getAnnotations();
        for (Annotation annotation : annotations) {
            logger.info("{}",annotation);
        }
        //获取注解value值
        TableEverbo annotation = (TableEverbo) student.getAnnotation(TableEverbo.class);
        String value = annotation.value();
        logger.info("{}",value);
        //获得指定注解
        Field name = student.getDeclaredField("name");
        FiledEverbo filedEverbo = name.getAnnotation(FiledEverbo.class);
        logger.info("{}",filedEverbo.columnName());
        logger.info("{}",filedEverbo.type());
        logger.info("{}",filedEverbo.length());
    }
}
//pojo类
@TableEverbo("db_student")
class Student{
    @FiledEverbo(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FiledEverbo(columnName = "db_age",type = "int",length = 10)
    private int age;
    @FiledEverbo(columnName = "db_name",type = "varchar",length = 3)
    private String name;
    public Student(){
    }

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//自定义类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableEverbo{
    String value();
}
//自定义属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledEverbo{
    String columnName();
    String type();
    int length();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值