java进阶知识--反射、junit、注解

一、反射

 1.1 反射机制

    概述:将类的各个组成部分封装成其他对象,在代码运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

    好处:

      1. 可以在程序运行的过程中,操作这些对象;(如:突破封装性)
      2. 可以解耦,提高程序的可扩展性;
      3. 因编译成Class文件进入运行阶段时,泛型会自动擦除。所以反射可以绕过编译阶段为集合添加元素(突破泛型约束);
      4. 通用框架的底层原理。

    用途:在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值,起到保护用户的隐私目的。

    反射机制的相关类:

类名用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法
    java代码在计算机中经历的三个阶段

 1.2 获取Class对象的方式

     1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。
        * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
     2. 类名.class:通过类名的属性class获取。
        * 多用于参数的传递。
     3. 对象.getClass():getClass()方法在Object类中定义着。
        * 多用于对象的获取字节码的方式。
     结论:
     同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
  小贴士:
    上述三种获取Class对象的方式,刚好对应java代码经历的三个阶段。

 1.3 Class对象功能

    获取功能:
      
      1. 获取构造方法
        * Constructor<?>[] getConstructors():获取全部public修饰的构造器
        * Constructor<T> getConstructor(Class<?>... parameterTypes):指定获取某个public修饰的构造器
 
        * Constructor<?>[] getDeclaredConstructors():获取全部构造器,不考虑修饰符。
        * Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):指定获取某个构造器,不考虑修饰符。
      2. 获取成员变量
        * Field[] getFields() :获取所有public修饰的成员变量
        * Field getField(String name)   获取指定名称的 public修饰的成员变量
 
        * Field[] getDeclaredFields() :获取所有的成员变量,不考虑修饰符。
        * Field getDeclaredField(String name) :获取指定名称的成员变量,不考虑修饰符。
      3. 获取成员方法
        * Method[] getMethods() :获取全部public修饰的方法
        * Method getMethod(String name, Class<?>... parameterTypes) :获取某个public修饰的方法
 
        * Method[] getDeclaredMethods() :获取全部方法,不考虑修饰符。
        * Method getDeclaredMethod(String name, Class<?>... parameterTypes) :获取某个方法,不考虑修饰符。

      4. 获取类名 

        * String getName()   全类名
        * String getSimpleName() 简名(类名)

 1.4 Constructor构造方法

    1. 获取参数个数

       * String getParameterCount() 

    2. 暴力反射

       * setAccessible(true):设置true,表示禁止检查访问控制。

    3. 创建对象:
       * T newInstance(Object... initargs) 调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回。

  如果使用空参数构造方法创建对象时,操作可以简化:Class对象.newInstance方法

 1.5 Field成员变量

    操作:
      1. 设置值
         * void set(Object obj, Object value) 
      2. 获取值
         * get(Object obj)
      3. 当给私有属性get/set时,需忽略访问权限修饰符的安全检查
         * setAccessible(true):暴力反射

 1.6 Method方法对象

    1. 执行方法:
      * Object invoke(Object obj, Object... args):obj 对象,args  参数
    2. 获取方法名称:
      * String getName:获取方法名
    3. 当给私有属性get/set时,需忽略访问权限修饰符的安全检查
        * setAccessible(true):暴力反射
 

二、Junit单元测试

 2.1 测试分类

    1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
    2. 白盒测试:需要写代码的。关注程序具体的执行流程。

 2.2 Junit使用(白盒测试的一种)

     步骤:
      1. 定义一个测试类(测试用例)
          * 测试类名:被测试的类名Test
          * 包名:xxx.xxx.xx.test
      2. 定义测试方法:可以独立运行
          * 方法名:test测试的方法名  testAdd() 
          * 返回值:void
          * 参数列表:空参
      3. 给方法加@Test
      4. 导入junit依赖环境
      * 判定结果(junit4):
        * 一般我们会使用断言操作来处理结果:Assert.assertEquals('期望的结果','运算的结果');

 2.3 Junit断言(junit5)

  

JUnit不同版本的差异。
JUnit 4主要使用org.junit.Assert类,里面的方法是静态的,比如assertEquals、assertTrue等。
而JUnit 5则引入了新的org.junit.jupiter.api.Assertions类,方法更丰富,支持lambda表达式和更详细的错误信息。

  <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
  </dependency>

 2.4 Junit常用注解

    

     

 

三、注解

 3.1 概述

    注解(Annotation),也叫元数据。一种代码级别的说明,即用来说明程序的。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
小贴士:它可以声明在类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
    格式:@注解名称(...)  其实就是一个实现类对象,实现了该注解以及Annotation接口。

 3.2 注解的作用

    分类:
      ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
      ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
      ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

 3.3 JDK中预定义的一些注解

    * @Override :检测被该注解标注的方法是否是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过时
    * @SuppressWarnings:压制警告
        * 一般传递参数all  @SuppressWarnings("all")

 3.4 自定义注解

   3.4.1 格式

      public @interface 注解名称{
        // 属性列表;
        public  属性类型  属性名()  default  默认值;
      }

   3.4.2 本质

      注解本质上就是一个接口,该接口默认继承Annotation接口。

      如:public interface MyAnno extends java.lang.annotation.Annotation { ... }

   3.4.3 属性 

      属性:接口中的抽象方法
        * 要求:
           1. 属性的返回值类型有下列取值
              * 基本数据类型
              * String
              * 枚举
              * 注解
              * 以上类型的数组
           2. 定义了属性,在使用时需要给属性赋值
              * 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
              * 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
              * 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略。

 3.5 元注解

   概念:用于描述注解的注解
      * @Target:描述注解能够作用的位置
         * ElementType取值:
            * TYPE:可以作用于类上
            * CONSTRUCTOR:可以作用于构造器上
            * METHOD:可以作用于方法上
            * FIELD:可以作用于成员变量上
            * LOCAL_VARIABLE:可以作用于局部变量上
            * PARAMETER:可以作用于方法参数上
      * @Retention:描述注解被保留的阶段
          * RetentionPolicy取值:
          * SOURCE:只作用于源码阶段,字节码文件中不存在。
            被修饰的注解只能存在源码中,字节码class没有。编译器要丢弃的注解。
          * CLASS(默认值):保留到字节码文件阶段 ,运行阶段不存在。
            编译器将把注解记录在类文件中,但在运行时 JVM 不需要保留注解。
          * RUNTIME:一直保留到运行阶段。
            编译器将把注解记录在类文件中,在运行时 JVM 将保留注解,因此可以反射性地读取。

      * @Documented:描述注解是否被抽取到api文档中
      * @Inherited:描述注解是否被子类继承

 3.6 注解的解析

   概述:判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。

   如何解析注解?
      指导思想:要解析谁上面的注解,就应该先拿到谁的对象,再通过对象解析其上面的注解。
      比如:要解析类上面的注解,就应该先获取类的Class对象,再通过Class对象解析其上面的注解。
      因为:Class、Method、Field、Constructor等都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

   

   案例:

    

 注解解析:06、注解概述、自定义注解、元注解、注解解析_哔哩哔哩_bilibili



 面试题:描述一下JVM加载class文件的原理机制?

  JVM中类的装载是由ClassLoader和它的子类来实现的。
  Java ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件的类。

 

posted @ 2020-08-27 11:19  九点的太阳  阅读(188)  评论(0)    收藏  举报