java 动态添加注解_注解和反射

feb99bc0146b4a880b962d5288f5b65e.png

一、注解Annotation

1.1 注解入门

  • 注解作用

对程序作出解释(Comment),可以被其他程序(比如编译器)读取。

  • 注解格式

@注释名,还可以添加参数值

比如:@Override

1.2 内置注解和元注解

  • 内置注解

@Override:表示方法声明旨在覆盖超类型中的方法声明。

@Deprecated的程序元素是程序员不鼓励使用的程序元素,通常是因为它是危险的,或者因为存在更好的替代方法。

@SuppressWarnings: 表示在注释元素(以及注释元素中包含的所有程序元素)中应该抑制命名的编译器警告。

  • 元注解meta-annotation

作用:负责注解其他注解。

@Target:描述注解的适用范围(比如有的注解只能用在方法上,有的注解可以用在类上。。)

@Retention:用于描述注解生命周期(RUNTIME>CLASS>SOURCE)

@Documented:该注解是否在javadoc中

@Inherited:子类可以继承父类的注解

1.3 自定义注解

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

import java.lang.annotation.*;public class TestAnno {    //测试注解    @MyAnnotion(age = 18,name = "psz")    public void test(){    }}//自定义注解//注解适用范围:方法,类@Target({ElementType.METHOD,ElementType.TYPE})//生命周期:runtime@Retention(RetentionPolicy.RUNTIME)//表示是否讲注解生成在Javadoc中@Documented//子类可以继承父类的注解@Inherited@interface MyAnnotion {    //注解的参数(不是方法):参数类型+参数名();    String name() default "";    //注解没有默认值的时候,使用注解时候必须赋值    int age();}

二、Class

2.1 Class类

Class类是反射的源头,我们首先应该得到对应的Class对象。

Class本身也是一个类,Class对象只能由系统建立,一个类加载在JVM中只有一个Class实例,一个Class对象对应一个.class文件。

  • 哪些类可以有Class对象呢?

类,接口,数组,枚举,反射,基本数据类型,void。。。

  • 获得Class实例方法

        Person p =new Person();        //方式一:通过具体实例得到        Class c1 = p.getClass();        //方式二:通过类得到        Class c2 = Person.class;        //方式三:通过全路径得到        Class c3 = Class.forName("com.psz.Person");
  • 一个类在JVM中只有一个Class实例

System.out.println(c1.hashCode()==c2.hashCode()); //true

2.2 类加载内存

Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

过程:

  • 加载:由类加载器ClassLoader执行,将字节流所代表的的静态存储结构转换为运行时数据结构,然后生成对应的java.lang.Class对象。

  • 链接:将.Class文件中二进制代码合并到运行环境中。

    • 验证:确保字节流包含的信息是否符合当前虚拟机的要求

    • 准备:为static静态域分配存储空间和设置类变量的初始值

    • 解析:将常量池中的符号引用转化为直接引用

  • 初始化:执行类中定义的java程序代码。

2.3 类初始化

所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载)

  • 类的主动引用(会发生初始化)

    虚拟机启动时候,main函数所在的类被初始化

    new 一个类

    调用类的静态成员或静态方法

    反射调用

    初始化子类的时候,如果父类没有被初始化,那么父类会被初始化

  • 类的被动引用(不会发生初始化)

    访问静态域,只有通过真正声明这个域的类才会被初始化。(比如子类调用父类的静态变量,此时子类不会初始化)

    通过数组定义类引用,不会初始化

    引用final常量时候不会触发此类的初始化

2.4 类加载器ClassLoader

  • 作用:负责加载类的对象。通俗的讲就是将class文件加载到jvm虚拟机中去。

  • 分类:

     C++编写,是JVM自身的一部分。将\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库(rt.jar),此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用

     负责加载jre\lib\ext目录下的jar包类库,用来加载java的扩展库,开发者可以直接使用这个类加载器。

  1. 系统类加载器Application ClassLoader

  1. 扩展类加载器Extendsion ClassLoader

  1. 引导类加载器Bootstrap ClassLoader

  • 测试加载器

        //1. 获取系统类加载器        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();        System.out.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2        //2. 获取系统类加载器的父类加载器        ClassLoader parent = systemClassLoader.getParent();        System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@1b6d3586        //3. 获取扩展类加载器的父类加载器-->获取不到        ClassLoader parent1 = parent.getParent();        System.out.println(parent1);//null        //4.测试当前类是哪个加载器加载的        ClassLoader classLoader = Class.forName("com.psz.TestReflection").getClassLoader();        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

三、反射Reflection

3.1 定义

因为Java是静态语言,运行时结构不可变,当我们想让编程更加灵活,想获得类似动态语言的特性时候,就可以用反射机制

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

通俗的来讲:加载完类之后,内存的方法区中就会生成一个Class类型的对象,这个对象包含了类的所有结构信息(属性,方法,私有字段等等)。我们通过这个对象看到类的结构。

缺点:影响性能,比如invoke()会对参数做封装和解封操作。项目中尽量不要使用大量的反射。

3.2 获得类的信息

  • 通过Class获得类中的属性,方法,修饰符等等等。

        Class personClass = Person.class;                //1.获得类的名字        String name = personClass.getName();  //com.psz.Person  包名+类名        String simpleName = personClass.getSimpleName(); //Person        //2. 获得类的属性        Field[] fields = personClass.getFields(); //得到声明为public的属性        Field[] declaredFields = personClass.getDeclaredFields();//全部属性        Field name1 = personClass.getDeclaredField("name"); //得到属性为name的值        //3.得到方法        personClass.getMethods();//public 本类+父类的public方法        personClass.getDeclaredMethods();//本类的所有方法        personClass.getMethod("方法名","参数"); //因为存在重载,所以需要参数        //4.获得构造器        personClass.getConstructors();        personClass.getDeclaredConstructor();

3.4 动态创建对象

  • 通过反射获得对象

Class personClass = Person.class;//2.创建对象,调用无参构造器Person person = (Person)personClass.newInstance();System.out.println(person); //Person{name='null', age=0}//3.通过构造器创建对象Constructor constructor = personClass.getDeclaredConstructor(String.class,int.class);Person psz = (Person)constructor.newInstance("psz", 22);System.out.println(psz); //Person{name='psz', age=22}
  • 通过反射调用方法

//通过反射调用方法,方法名+参数Method m = personClass.getMethod("setName", String.class);//invoke(对象,方法值)m.invoke(psz,"codecat");System.out.println(psz.getName()); //codecat
  • 通过放射操作属性

  1. 错误方式:

    public static void main(String[] args) throws Exception {        //1.获得Class对象        Class personClass = Person.class;        //通过反射操作属性        Person psz = (Person)personClass.newInstance();        Field age = personClass.getDeclaredField("age");        age.set(psz,22);        System.out.println(psz.getAge());    }

报错:因为age字段是private,无法直接set。

Exception in thread "main" java.lang.IllegalAccessException: Class com.psz.TestReflection can not access a member of class com.psz.Person with modifiers "private"at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)at java.lang.reflect.Field.set(Field.java:761)at com.psz.TestReflection.main(TestReflection.java:59)
  1. 正确方式,关闭程序的安全检测setAccessible(true),设置成true后,就关闭了安全监测。关闭检测效率会高一些

Person psz = (Person)personClass.newInstance();Field age = personClass.getDeclaredField("age");//=========关闭安全监测===================age.setAccessible(true);age.set(psz,22);System.out.println(psz.getAge());

3.4 性能分析

我们尝试去执行2000000000次方法,来查看几种方式的执行时间。

    //1. 普通方式,new对象    public static void normalTest(){        Person p1 =new Person("psz",22);        long startTime = System.currentTimeMillis();        for (int i = 0; i < 2000000000; i++) {            p1.getName();        }        long endTime = System.currentTimeMillis();        System.out.println("普通方式:"+(endTime-startTime)+"ms");    }    //2. 反射    public static void reflectionTest() throws Exception {        Class personClass = Person.class;        Constructor constructor = personClass.getDeclaredConstructor(String.class,int.class);        Person p2 = (Person)constructor.newInstance("psz", 22);        long startTime = System.currentTimeMillis();        for (int i = 0; i < 2000000000; i++) {            p2.getName();        }        long endTime = System.currentTimeMillis();        System.out.println("反射方式:"+(endTime-startTime)+"ms");    }    //3. 反射,关闭检查    public static void reflectionAndCloseCheckTest() throws Exception {        Class personClass = Person.class;        Person p3 = (Person)personClass.newInstance();        Field name = personClass.getDeclaredField("name");        name.setAccessible(true);//关闭检查        name.set(p3,"psz");        long startTime = System.currentTimeMillis();        for (int i = 0; i < 2000000000; i++) {            p3.getName();        }        long endTime = System.currentTimeMillis();        System.out.println("反射+不检查方式:"+(endTime-startTime)+"ms");    }

结果:普通 > 反射不检查 > 反射检查

普通方式:4ms反射方式:6ms反射+不检查方式:5ms

总结:注解和反射在框架中有大量的应用,也是面试常见问题,所以学好注解和反射,对工作有很大帮助。

e42d250937a4ced400ccac5fc90bd559.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值