Java高级技术

单元测试

单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。

Junit单元测试框架

Junit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。
此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。

Junit优点

  • Junit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
  • Junit可以生成全部方法的测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试

单元测试快速入门

  • 1.将JUnit的jar包导入到项目中
    • IDEA通常整合好了Junit框架,一般不需要导入。
    • 如果IDEA没有整合好,需要自己手工导入如下2个JUnit的jar包到模块
  • 2.编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
  • 3.在测试方法上使用@Test注解:标注该方法是一个测试方法
  • 4.在测试方法中完成被测试方法的预期正确性测试。
  • 5.选中测试方法,选择“JUnit运行” ,如果测试良好则是绿色;如果测试失败,则是红色

单元测试常用注解

Junit常用注解(Junit 4.xxxx版本)

  • 开始执行的方法:初始化资源。
  • 执行完之后的方法:释放资源。

Junit常用注解(Junit 5.xxxx版本)

  • 开始执行的方法:初始化资源。
  • 执行完之后的方法:释放资源。

反射

反射概述

  • 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
  • 在运行时,可以直接得到这个类的构造器对象:Constructor
  • 在运行时,可以直接得到这个类的成员变量对象:Field
  • 在运行时,可以直接得到这个类的成员方法对象:Method
  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。

反射的关键:

  • 反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。

反射获取类对象

反射的第一步:获取Class类的对象

获取Class类的对象的三种方式

  • 方式一:Class c1 = Class.forName(“全类名”);
  • 方式二:Class c2 = 类名.class
  • 方式三:Class c3 = 对象.getClass();

反射获取构造器对象

使用反射技术获取构造器对象并使用

  • 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
  • Class类中用于获取构造器的方法
  • 获取构造器的作用依然是初始化一个对象返回。
  • Constructor类中用于创建对象的方法

反射获取成员变量对象

使用反射技术获取成员变量对象并使用

  • 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
  • Class类中用于获取成员变量的方法
  • 获取成员变量的作用依然是在某个对象中取值、赋值
  • Field类中用于取值、赋值的方法

反射获取方法对象

使用反射技术获取方法对象并使用

  • 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
  • Class类中用于获取成员方法的方法
  • 获取成员方法的作用依然是在某个对象中进行执行此方法
  • Method类中用于触发执行的方法

反射的作用-绕过编译阶段为集合添加数据

  • 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
  • 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
  • 反射是作用在运行时的技术,此时已经不存在泛型了。

反射的作用-通用框架的底层原理

需求

  • 给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。

分析

  • 定义一个方法,可以接收任意类的对象。
  • 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
  • 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
  • 使用反射获取对象的Class类对象,然后获取全部成员变量信息。
  • 遍历成员变量信息,然后提取本成员变量在对象中的具体值
  • 存入成员变量名称和值到文件中去即可。

反射的作用

  • 可以在运行时得到一个类的全部成分然后操作。
  • 可以破坏封装性。(很突出)
  • 也可以破坏泛型的约束性。(很突出)
  • 更重要的用途是适合:做Java高级框架
  • 基本上主流框架都会基于反射设计一些通用技术功能。

注解

注解概述

  • Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
  • Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

注解的作用

  • 对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。
  • 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

自定义注解

自定义注解 --- 格式

  • 自定义注解就是自己做一个注解来使用。

特殊属性

  • value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!
  • 但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。

元注解

元注解:就是注解注解的注解。
元注解有两个:

  • @Target: 约束自定义注解只能在哪些地方使用
  • @Retention:申明注解的生命周期

@Target中可使用的值定义在ElementType枚举类中,常用值如下

  • TYPE,类,接口
  • FIELD, 成员变量
  • METHOD, 成员方法
  • PARAMETER, 方法参数
  • CONSTRUCTOR, 构造器
  • LOCAL_VARIABLE, 局部变量

@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下

  • SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
  • CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
  • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

注解解析

注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口

  • Annotation: 注解的顶级接口,注解都是Annotation类型的对象
  • AnnotatedElement:该接口定义了与注解解析相关的解析方法
  • 所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力

解析注解的技巧

  • 注解在哪个成分上,我们就先拿哪个成分对象。
  • 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
  • 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
  • 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解

动态代理

代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。

关键步骤

  • 1.必须有接口,实现类要实现接口(代理通常是基于接口实现的)。
  • 2.创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
/**
    public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h)
    参数一:类加载器,负责加载代理类到内存中使用。
    参数二:获取被代理对象实现的全部接口。代理要为全部接口的全部方法进行代理
    参数三:代理的核心处理逻辑
 */
public class ProxyUtil {
    /**
      生成业务对象的代理对象。
     * @param obj
     * @return
     */
    public static <T> T  getProxy(T obj) {
        // 返回了一个代理对象了
        return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 参数一:代理对象本身。一般不管
                        // 参数二:正在被代理的方法
                        // 参数三:被代理方法,应该传入的参数
                       long startTimer = System .currentTimeMillis();
                        // 马上触发方法的真正执行。(触发真正的业务功能)
                        Object result = method.invoke(obj, args);

                        long endTimer = System.currentTimeMillis();
                        System.out.println(method.getName() + "方法耗时:" + (endTimer - startTimer) / 1000.0 + "s");

                        // 把业务功能方法执行的结果返回给调用者
                        return result;
                    }
                });
    }
}

动态代理的优点

  • 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
  • 可以为被代理对象的所有方法做代理。
  • 可以在不改变方法源码的情况下,实现对方法功能的增强。(AOP思想)
  • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
posted @ 2022-11-09 10:26  晚点心动。  阅读(95)  评论(0)    收藏  举报