注解与反射

注解:

  java.Annotation

Annotation作用:

    不是程序本身,可以对程序做出解释。

    可以被其他程序读取,比如:编译器等。

Annotation格式:

    注解是以“@注解名” 在代码中存在的,还可以添加一些参数。

Annotation使用场景:

    可以附加在package,class,method,field 等上面,相当于添加了额外的辅助信息,可以通过反射机制编程实现对元数据的访问。

常用注解:

  @Override : 重写注解

  @Deprecate : 标记注释,标记注释,就是已经过期或者即将过期的方法。不推荐使用。 

  @SuppressWarnings : 阻止警告信息。

元注解:

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

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

注解

  1、@Target : 用于描述注解的使用范围。

  2、@Retention : 表示在什么级别保存该注释信息,用于描绘注解的生命周期(SOURCE < ClASS < RUNTIME )。

  3、@Document : 说明该注解生成在 JAVAdoc中。

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

 

自定义注解:

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

描述:

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

  其中的每一个方法实际上是声明了一个配置参数。

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

  返回值类型就是参数的类型(返回值只能返回 class,String,enum)

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

  如果只有一个参数成员,一版参数名为value

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

 

代码:

 

 

 

反射机制:

  java.Reflection

  静态/动态语言描述

    动态语言:

      是一类在运行时可以改变其结构的语言:例如:新的函数,对象,甚至代码可以被引进,已有函数可以被删除或是其他结构上的变化。通俗讲,可以在运行时代码可以根据某种条件改变自身结构。
      

      主要动态语言: Object-C,C#,javaScript、PHP、Python 等。

    静态语言:

      与动态语言相对应的,运行时结构不可以改变的语言就是静态语言。如java、C、C++

 

      java 不是动态语言,但是java 可以称之为”准动态语言“。即java有一定动态性,可以通过反射机制获得类似动态语言的特性。可以让编程更灵活。

 

    反射:

      

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

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

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

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

 

    反射优缺点:

      优点: 可以实现动态创建对象和编译 ,灵活性高。

      缺点: 对性能有影响,操作慢于直接执行相同 new 操作。

 

创建Class 对象的方式:

  1、通过对象获得。

  2、forname 获得。

  3、通过类名获得。

  注: 基本内置类型的包装类都有一个Type属性(注意 只有内置的对象才有)。

代码:

  

 类的主动引用:

  一定会发生类的初始化。

  场景:

    1、调用类的静态成员(除了final常量) 和静态方法。

    2、当虚拟机启动,先初始化Main所在的类。

    3、new 一个 类的对象。

    4、当初始化一个类,如果父类没有被初始化,则会先初始化父类。

 

类的主动引用:

  不会发生类的初始化。

  场景:

    1、当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当子类引用父类的静态变量。

    2、通过数组定义类引用,不会触发类的初始化。

    3、引用常量不会触发此类的初始化。

 

类加载器的作用

    将class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据集结构,在堆中生成一个代表这个类的java.lang.Class类型的对象。

    类缓存: 标准的javaSE类加载器可以按要求查找类,但是一旦类被加载到类加载器中,他将维持加载(缓存)一段时间。不过JVM回收机制可以回收这些Class对象。

代码:

  

 

 

 

双亲委派机制:

     通俗来讲就是 定义类之后 ,系统会一级一级往上找如果根加载器 存在你定义的类,则抛出异常,此类无效。

 

通过反射获取类的基本属性:

  一、 获取类名称:

    1、Class.getName() : 获取类路径 + 名称。

    2、Class.getSimpleName() : 获取类名称。

 

  二、获取类属性:

    1、Class.getFields() : 获取类 Public 属性。 带参数就是获取指定参数。

    2、Class.getDeclaredFields() : 获取类所有属性,包括 private 。带参就是获取指定属性。

 

  三、获得类的方法:

    1、Class.getMethods() : 获取本类及其父类所有public 方法。

    2、Class.getDeclaredMethods() : 获取本类所有方法。

    3、Class.getMethods(“参数”,类型): 获取指定方法,因为有重载的可能性,所以需要加入参数。

 

  四、获取类的构造器

    1、Class.getConstructors():  获取所有Public 构造器。

    2、Class.getDeclaredConstructors() : 获取本类所有构造器。

代码:

 

 

 

通过反射创建对象:

  

Class<?> aClass = Class.forName("Study_2021_08_17.reflection.User");
User user = (User) aClass.newInstance(); //默认调用无参构造 ,如果没有无参构造则报错
System.out.println(user);

// 通过构造器创建对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, String.class);
User user1 = (User) declaredConstructor.newInstance("张三", "1234141");
System.out.println(user1);

//通过反射调用普通方法
User user2 = (User) declaredConstructor.newInstance("张三", "1234141");

//通过反射获取一个方法
Method setName = aClass.getDeclaredMethod("setName", String.class);
//invoke: 激活的意思
// (对象,”方法的值“)
setName.invoke(user2, "李四");
System.out.println(user2);

// 通过反射获取属性 : 不能直接操作私有属性。如果需要操作 ,需要关闭程序的安全检测,通过方法或者属性的 setAccessible(true)来关闭,可以调高效率。
Field name = aClass.getDeclaredField("name");
//name.setAccessible(true);
name.getName();
name.set(user2, "王五");
System.out.println(user2);

 

 

反射性能测试:

 

/**
* @description: 反射性能测试
* @author: zhx
* @created: 2021/08/18 22:00
*/
public class Test09 {

// 普通调用
public static void test01() {
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法调用执行时间:" + (endTime - startTime) + "ms");
}

// 反射
public static void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<?> aClass = user.getClass();
Method getName = aClass.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射调用执行时间:" + (endTime - startTime) + "ms");

}

// 反射 关闭检测
public static void test03() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

User user = new User();
Class<?> aClass = user.getClass();
Method getName = aClass.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("关闭权限检测:" + (endTime - startTime) + "ms");

}

public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
test01();
test02();
test03();
/**
* 结果:
* 普通方法调用执行时间:11ms
* 反射调用执行时间:561ms
* 关闭权限检测:347ms
*/
}
}

  

posted @ 2021-08-20 15:58  初心不负~  阅读(46)  评论(0)    收藏  举报