Java SE 学习笔记(十七)—— 单元测试、反射

目录

  • 1 单元测试
    • 1.1 单元测试概述
    • 1.2 单元测试快速入门
    • 1.3 JUnit 常用注解
  • 2 反射
    • 2.1 反射概述
    • 2.2 获取类对象
    • 2.3 获取构造器对象
    • 2.4 获取成员变量对象
    • 2.5 获取常用方法对象
    • 2.6 反射的作用
      • 2.6.1 绕过编译阶段为集合添加数据
      • 2.6.2 通用框架的底层原理

1 单元测试

1.1 单元测试概述


开发好的系统中存在很多方法,如何对这些方法进行测试?
以前我们都是将代码全部写完再进行测试。其实这样并不是很好。在以后工作的时候,都是写完一部分代码,就测试一部分。这样,代码中的问题可以得到及时修复。也避免了由于代码过多,从而无法准确定位到错误的代码。

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

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

JUnit优点:

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

1.2 单元测试快速入门


需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门

分析:

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

示例代码

要测试的方法

public class UserService {
    public String login(String name,String passwd){
        if ("admin".equals(name) && "123456".equals(passwd)){
            return "登陆成功";
        }else{
            return "用户名或密码错误!";
        }
    }
    public void selectNames(){
//        System.out.println(10/0);
        System.out.println("查询全部用户成功!");
    }
}

测试方法

import org.junit.Assert;
import org.junit.Test;

public class TestUserService {
    // 测试方法(公开的无参数无返回值的非静态方法)
    @Test
    public void testLogin(){
        UserService userService = new UserService();
        String rs = userService.login("admin", "123456");
        // 进行预期结果的正确性测试
        Assert.assertEquals("您的登录业务出现问题","登陆成功",rs);
    }
    @Test
    public void testSelectNames(){
        UserService userService = new UserService();
        // 要测试的方法没有返回值,不用断言
        userService.selectNames();
    }
}

一个业务要对应一个测试方法

1.3 JUnit 常用注解


Junit 4.xxxx 版本

在这里插入图片描述

Junit 5.xxxx 版本

在这里插入图片描述

2 反射

2.1 反射概述


反射是指对于任何一个 Class 类,在 " 运行的时候 " 都可以直接得到这个类全部成分。

  • 在运行时 , 可以直接得到这个类的构造器对象: Constructor
  • 在运行时 , 可以直接得到这个类的成员变量对象:Field
  • 在运行时 , 可以直接得到这个类的成员方法对象: Method

这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制
反射的第一步都是先得到编译后的 Class 类对象,然后就可以得到 Class 的全部成分。

2.2 获取类对象


获取 class 对象的有以下三种方式

在这里插入图片描述

示例代码

Student

package com.huwei;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("无参构造器执行!");
    }

    public Student(String name, int age) {
        System.out.println("有参构造器执行!");
        this.name = name;
        this.age = age;
    }

    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;
    }

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

测试类

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 通过Class类中的静态方法forName("全限名")来获取
        Class c = Class.forName("com.huwei.Student");
        System.out.println(c); // class com.huwei.Student

        // 2. 通过class属性来获取
        Class c1 = Student.class;
        System.out.println(c1); // class com.huwei.Student

        // 3. 利用对象的getClass方法来获取
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2); // class com.huwei.Student
    }
}

注意:

  • 第一种方式forName(String className)中的className为全限名(包名+类名)

已经获取类对象了,接下来就可以获取以下对象

在这里插入图片描述

2.3 获取构造器对象


Class类中用于获取构造器的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        // 获取类对象
        Class c = Student.class;
//        System.out.println(c); // class com.huwei.Student

        // 提取类中全部的构造器(只能是公开的构造器)
        Constructor[] cons1 = c.getConstructors();
        // 遍历构造器
        for (Constructor con : cons1) {
            System.out.println(con.getName() + "=====>" + con.getParameterCount());
        }
        System.out.println("-------------------------------------");
        // 提取类中全部的构造器,包括私有
        Constructor[] cons2 = c.getDeclaredConstructors();
        // 遍历构造器
        for (Constructor con : cons2) {
            System.out.println(con.getName() + "=====>" + con.getParameterCount());
        }
        System.out.println("-------------------------------------");
        // 获取单个构造器(无参构造器),只能是公开的(如果无参构造器私有会报错)
        Constructor con1 = c.getConstructor();
        System.out.println(con1.getName() + "=====>" + con1.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(无参构造器),包括私有
        Constructor con2 = c.getDeclaredConstructor();
        System.out.println(con2.getName() + "=====>" + con2.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(有参构造器),只能是公开的(如果有参构造器私有会报错)
        Constructor con3 = c.getConstructor(String.class, int.class);
        System.out.println(con3.getName() + "=====>" + con3.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(有参构造器),包括私有
        Constructor con4 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(con4.getName() + "=====>" + con4.getParameterCount());
    }
}

获取构造器的作用依然是初始化一个对象返回

Constructor类中用于创建对象的方法

在这里插入图片描述

import java.lang.reflect.Constructor;

public class Test2 {
    public static void main(String[] args) throws Exception{
        // 获取类对象
        Class c = Student.class;
        
        // 获取单个构造器(无参构造器),包括私有
        Constructor con1 = c.getDeclaredConstructor();
        System.out.println(con1.getName() + "=====>" + con1.getParameterCount());
        // 如果遇到了私有构造器,可以暴力反射
        con1.setAccessible(true); // 权限被打开,仅当前这次
        Student s1 = (Student) con1.newInstance();
        System.out.println(s1);
        
        System.out.println("--------------------------------");

        // 获取单个构造器(有参构造器),包括私有
        Constructor con2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(con2.getName() + "=====>" + con2.getParameterCount());
        Student s2 = (Student) con2.newInstance("孙悟空",50000);
        System.out.println(s2);
    }
}

如果遇到非 public 的构造器,需要打开权限(暴力反射),然后再创建对象,说明反射可以破坏封装性,私有的也可以执行了

2.4 获取成员变量对象


Class类中用于获取成员变量的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Field;

public class Test3 {
    public static void main(String[] args) throws NoSuchFieldException {
        // 获取类对象
        Class c = Student.class;
        // 获取全部成员变量,包括私有
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+"=====>"+field.getType());
        }

        System.out.println("----------------------");

        // 获取某一个成员变量,包括私有
//        Field field = c.getDeclaredField("name");
        Field field = c.getDeclaredField("age");
        System.out.println(field.getName()+"=====>"+field.getType());
    }
}

获取成员变量的作用依然是在某个对象中取值、赋值

Filed 类中用于取值、赋值的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Field;

public class Test4 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        // 获取类对象
        Class c = Student.class;
        // 获取某一个成员变量,包括私有
        Field name = c.getDeclaredField("name");

        name.setAccessible(true); // 暴力打开权限
        // 赋值
        Student s = new Student();
        name.set(s,"小明");
        System.out.println(s);
        // 取值
        String name1 = (String) name.get(s);
        System.out.println(name1);
    }
}

2.5 获取常用方法对象


Class类中用于获取成员方法的方法

在这里插入图片描述

获取成员方法的作用依然是在某个对象中执行此方法

Method类中用于触发执行的方法

在这里插入图片描述

示例代码

定义Dog类

public class Dog {
    private String name;

    public Dog() {
    }

    public Dog(String name) {
        this.name = name;
    }

    public void run(){
        System.out.println("狗在跑");
    }
    public String eat(String name){
        System.out.println(name+"在吃");
        return "吃的很开心!";
    }
    private void eat(){
        System.out.println("吃啥");
    }

}

测试类

import java.lang.reflect.Method;

public class Test5 {
    public static void main(String[] args) throws Exception {
        // 获取类对象
        Class c = Dog.class;
        // 提取全部方法,包括私有
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "=====>" + method.getReturnType() + "====>" + method.getParameterCount());
        }
        // 提取单个方法,包括私有
        Method eat1 = c.getDeclaredMethod("eat"); // 无参的eat方法
        Method eat2 = c.getDeclaredMethod("eat", String.class); // 有参的eat方法

        eat1.setAccessible(true); // 暴力反射

        // 触发方法的执行
        Dog d = new Dog();
        Object rs1 = eat1.invoke(d); // 方法如果是没有返回值的,则返回的是null
        System.out.println(rs1);
        Object rs2 = eat2.invoke(d, "小黑");
        System.out.println(rs2);
    }
}

2.6 反射的作用

2.6.1 绕过编译阶段为集合添加数据


反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。

泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成 Class 文件进入运行阶段的时候,其真实类型都是 ArrayList 了,泛型相当于被擦除了。

反射是作用在运行时的技术,此时已经不存在泛型了。

示例代码

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();

        // 编译成 Class 文件进入运行阶段的时候,泛型会自动擦除。===> ArrayList.class
        System.out.println(list1.getClass()); // class java.util.ArrayList
        System.out.println(list2.getClass()); // class java.util.ArrayList
        System.out.println(list1.getClass() == list2.getClass()); // true

        System.out.println("=======================================");

        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(11);
        list3.add(22);
//        list3.add("哈哈哈"); // 报错
        Class c = list3.getClass(); // ArrayList.class ===> public boolean add(E e)
//        // 定位c中的add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        boolean rs = (boolean)add.invoke(list3, "嘿嘿嘿");
        System.out.println(rs); // true
        System.out.println(list3); // [11, 22, 嘿嘿嘿]

		// 还有一种方法可以突破泛型的限制
		ArrayList list4 = list3;
		list4.add("呜呼啦胡");
		list4.add(false);
		System.out.println(list3); // [11, 22, 嘿嘿嘿, 呜呼啦胡, false]
    }
}

2.6.2 通用框架的底层原理


反射可以做通用框架

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

分析

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

示例代码

存在TeacherStudent

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class MybatisUtil {
    /**
     保存任意类型的对象
     * @param obj
     */
    public static void save(Object obj){
        try (
                PrintStream ps = new PrintStream(new FileOutputStream("reflect/src/data.txt", true));
        ){
            // 1、提取这个对象的全部成员变量:只有反射可以解决
            Class c = obj.getClass();  //   c.getSimpleName()获取当前类名   c.getName获取全限名:包名+类名
            ps.println("================" + c.getSimpleName() + "================");

            // 2、提取它的全部成员变量
            Field[] fields = c.getDeclaredFields();
            // 3、获取成员变量的信息
            for (Field field : fields) {
                String name = field.getName();
                // 提取本成员变量在obj对象中的值(取值)
                field.setAccessible(true);
                String value = field.get(obj) + "";
                ps.println(name  + "=" + value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
   目标:提供一个通用框架,支持保存所有对象的具体信息。
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Student s = new Student();
        s.setName("猪八戒");
        s.setClassName("西天跑路1班");
        s.setAge(1000);
        s.setHobby("吃,睡");
        s.setSex('男');
        MybatisUtil.save(s);

        Teacher t = new Teacher();
        t.setName("波仔");
        t.setSex('男');
        t.setSalary(6000);
        MybatisUtil.save(t);
    }
}

结果文件

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/108644.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的太阳跟踪系统的设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、硬件电路设计2.1跟踪控制方案的选择2.1.1跟踪系统坐标系的选择2.2系统总体设计及相关硬件介绍…

服务熔断保护实践--Hystrix

概述 微服务有很多互相调用的服务&#xff0c;构成一系列的调用链路&#xff0c;如果调用链路中某个服务失效或者网络堵塞等问题&#xff0c;而有较多请求都需要调用有问题的服务时&#xff0c;这是就会造成多个服务的大面积失效&#xff0c;造成服务“雪崩”效应。 服务“雪…

十九、类型信息(2)

本章概要 Class 对象 类字面常量泛化的 Class 引用cast() 方法 Class 对象 要理解 RTTI 在 Java 中的工作原理&#xff0c;首先必须知道类型信息在运行时是如何表示的。这项工作是由称为 **Class**对象 的特殊对象完成的&#xff0c;它包含了与类有关的信息。实际上&#x…

JVM第二十三讲:Java动态调试技术原理

Java动态调试技术原理 本文是JVM第二十三讲&#xff0c;Java动态调试技术原理。转载自 美团技术团队胡健的Java 动态调试技术原理及实践&#xff0c;通过学习java agent方式进行动态调试&#xff0c;了解目前很多大厂开源的一些基于此的调试工具 (例如来自阿里开源的Arthas)。 …

微信小程序设计之主体文件app-wxss/less

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

elementUI 特定分辨率(如1920*1080)下el-row未超出一行却换行

在1920*1080分辨率下&#xff0c; el-col 内容未超出 el-col 宽度&#xff0c;el-col 不足以占据一行&#xff0c;el-row 却自动换行了&#xff08;其他分辨率没有这个问题&#xff09;。 截图&#xff1a; 排查&#xff1a; el-col 内容没有溢出&#xff1b;没有多余的 pad…

拜耳阵列(Bayer Pattern)和解马赛克简介

拜尔阵列 典型的图像传感器&#xff08;例如我们在数码相机中使用的图像传感器&#xff0c;主要有CCD, CMOS&#xff09;由许多单独的光电传感器组成&#xff0c;所有这些传感器都会捕获光线。这些光电传感器本身能够捕获光的强度&#xff0c;但不能捕获其波长&#xff08;颜色…

CTF-Web(3)文件上传漏洞

笔记目录 CTF-Web(2)SQL注入CTF-Web(3)文件上传漏洞 1.WebShell介绍 (1)一句话木马定义 一种网页后门&#xff0c;以asp、php、jsp等网页文件形式存在的一种命令执行环境&#xff0c;而 一句话木马往往只有一行WebShell代码。 作用&#xff1a; 攻击获得网站控制权限 查看、修改…

如何防范AI等技术带来的诈骗风险?从技术、法律、教育等多方面入手

文章目录 前言什么是AI诈骗案例案例一案例二 AI诈骗的特点如何预防和应对AI诈骗建议后记 前言 互联网是一把双刃剑&#xff0c;这是我们常说的一个问题。 随着人工智能技术的快速发展&#xff0c;AI诈骗成为当今社会面临的新兴威胁。不法分子利用人工智能技术&#xff0c;以更…

Qt之实现支持多选的QCombobox

一.效果 1.点击下拉列表的复选框区域 2.点击下拉列表的非复选框区域 二.实现 QHCustomComboBox.h #ifndef QHCUSTOMCOMBOBOX_H #define QHCUSTOMCOMBOBOX_H#include <QLineEdit> #include <QListWidget> #include <QCheckBox> #include <QComboBox>…

面试算法43:在完全二叉树中添加节点

题目 在完全二叉树中&#xff0c;除最后一层之外其他层的节点都是满的&#xff08;第n层有2n-1个节点&#xff09;。最后一层的节点可能不满&#xff0c;该层所有的节点尽可能向左边靠拢。例如&#xff0c;图7.3中的4棵二叉树均为完全二叉树。实现数据结构CBTInserter有如下3种…

Vue 3 响应式对象:ref 和 reactive 的使用和区别

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是尘缘&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f449;点击这里&#xff0c;就可以查看我的主页啦&#xff01;&#x1f447;&#x…

Flink CDC 2.0 主要是借鉴 DBLog 算法

DBLog 算法原理 DBLog 这个算法的原理分成两个部分&#xff0c;第一部分是分 chunk&#xff0c;第二部分是读 chunk。分 chunk 就是把一张表分为多个 chunk&#xff08;桶/片&#xff09;。我可以把这些 chunk 分发给不同的并发的 task 去做。例如&#xff1a;有 reader1 和 re…

二叉树的最近公共祖先

题目&#xff1a; 样例&#xff1a; 输入 6 1 4 2 5 -1 -1 1 4 -1 -1 -1 -1 -1 3 输出 2 思路&#xff1a; 由题意&#xff0c;最近公共祖先就是&#xff0c;找出给出的两个结点的父结点 是谁。 这里有两种情况 1、给定的两个结点都是孩子结点 2、给定的两个结点&#xff…

【送书福利-第二十二期】《Vue.js 3企业级项目开发实战(微课视频版)》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

Table-GPT:让大语言模型理解表格数据

llm对文本指令非常有用&#xff0c;但是如果我们尝试向模型提供某种文本格式的表格数据和该表格上的问题&#xff0c;LLM更有可能产生不准确的响应。 在这篇文章中&#xff0c;我们将介绍微软发表的一篇研究论文&#xff0c;“Table-GPT: Table- tuning GPT for Diverse Table…

用示例和应用程序了解必要的Golang库

Golang&#xff0c;也被称为Go&#xff0c;因其简单性、性能和并发性支持而在开发人员中迅速流行起来。导致Go成功的关键因素之一是其丰富的库生态系统&#xff0c;可以简化开发并提供解决常见问题的解决方案。在本文中&#xff0c;我们将更仔细地查看一些必要的Golang库&#…

心血管疾病药物不良反应不容忽视,华大基因基因检测辅助降低风险!

随着医疗技术的不断进步&#xff0c;个体化用药已经成为药物治疗的新趋势。在此趋势下&#xff0c;华大基因基因检测基于药物基因组学的药物选择和个性化用药方案&#xff0c;为心血管疾病患者的临床治疗提供了新机会&#xff0c;同时可以更好地帮助患者控制心血管疾病&#xf…

数据结构之栈的讲解(源代码+图解+习题)

我们在学习过顺序表和链表之后&#xff0c;了解了使用数组存储数据&#xff0c;使用结构体来存储数据和有关的指针&#xff0c;这些都是底层的东西&#xff0c;链表是靠指针的链接&#xff0c;顺序表是靠数组的下标才能得以实现增删查改。众多数据结构其实底层都离不开数组&…

HTML简单实现v-if与v-for与v-model

Vue启动&#xff01;&#xff01; 首先VIewModel将View和Model连接一起&#xff0c;Model的数据改变View的数据也变 使用Visual Studio Code 启动Vue需要vue.js插件和导入CDN(包) vue.js插件&#xff1a;CTRL shift x 在搜索栏搜 索vue.js安装即可 CDN&#xff1a; http…