• 超详细的JUnit单元测试介绍


    前言

    在这里插入图片描述

    本文为JUnit单元测试相关知识,下边将对JUnit单元测试概念JUnit优点JUnit安装与使用JUnit运行流程与常用注解JUnit测试套件使用及参数化设置JUnit断言等进行详尽介绍~

    📌博主主页:´Code_Wang的主页
    👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
    👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~


    目录

    在这里插入图片描述

    一、JUnit单元测试

    ​单元测试又称模块测试,属于白盒测试,是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

    1️⃣什么是JUnit

    • JUnit是用于编写可复用测试集的简单框架,是xUnit的一个子集。xUnit是一套基于测试驱动开发的测试框架,有PythonUnit、CppUnit、JUnit等。
    • JUnit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。
    • 多数Java的开发环境都已经集成了JUnit作为单元测试的工具,比如IDEA,Eclipse等等。

    2️⃣JUnit优点

    另外JUnit是在极限编程和重构(refactor)中被极力推荐使用的工具,因为在实现自动单元测试的情况下可以大大的提高开发的效率,但是实际上编写测试代码也是需要耗费很多的时间和精力的,那么使用这个东西好处到底在哪里呢?

    极限编程:
    要求在编写代码之前先写测试,这样可以强制你在写代码之前好好的思考代码(方法)的功能和逻辑,否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加。因此在极限编程中,基本过程是这样的:构思-> 编写测试代码-> 编写代码-> 测试,而且编写测试和编写代码都是增量式的,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。

    重构:
    其好处和极限编程中是类似的,因为重构也是要求改一点测一点,减少回归错误造成的时间消耗。

    其他情况:
    我们在开发的时候使用JUnit写一些适当的测试也是有必要的,因为一般我们也是需要编写测试的代码的,可能原来不是使用的JUnit,如果使用JUnit,而且针对接口(方法)编写测试代码会减少以后的维护工作,例如以后对方法内部的修改(这个就是相当于重构的工作了)。另外就是因为JUnit有断言功能,如果测试结果不通过会告诉我们哪个测试不通过,为什么,而如果是像以前的一般做法是写一些测试代码看其输出结果,然后再由自己来判断结果是否正确,使用JUnit的好处就是这个结果是否正确的判断是它来完成的,我们只需要看看它告诉我们结果是否正确就可以了,在一般情况下会大大提高效率。

    3️⃣为什么要用JUnit

    • 测试框架可以帮助我们对编写的程序进行有目的地测试,帮助我们最大限度地避免代码中的bug,以保证系统的正确性和稳定性。
    • 很多人对自己写的代码,测试时就简单写main,然后sysout输出控制台观察结果。这样非常枯燥繁琐,不规范。缺点:测试方法不能一起运行,测试结果要程序猿自己观察才可以判断程序逻辑是否正确。JUnit的断言机制,可以直接将我们的预期结果和程序运行的结果进行一个比对,确保对结果的可预知性。

    断言概述:

    • 编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。
    • 可以将断言看作是异常处理的一种高级形式。
    • 断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。

    4️⃣JUnit安装

    • JUnit官网:http://JUnit.org/
    • JUnit的jar包下载:https://github.com/JUnit-team/JUnit/wiki/Download-and-Install
    • 如果你安装的开发工具是eclipse,这就很好办,因为eclipse自带JUnit,只是不同版本的eclipse把JUnit放在了不同的地方,但是也很好找。
    • 如果你安装的开发工具是IDEA,那么要么去下载jar包,要么通过IDEA去安装插件

    IDEA中JUnit安装步骤如下:
    (1)新建一个项目工程,点击 文件File - 新建New - 项目Project,我这里项目名使用 JunitProj,点击完成
    在这里插入图片描述
    (2)完成项目的创建后,点击 文件File-设置Settings-Plugins 在搜索栏搜索 JUnit,此时出现了几个Plugins,选择 JUnit。Install JetBrains plugin…和Browser repositories两种方法,前者直接点击下载就好;解决IDEA 的 plugins 搜不到任何的插件问题:https://www.jb51.net/article/185940.htm
    在这里插入图片描述
    (3)安装完成之后,需要重启IDEA;
    在这里插入图片描述
    (4)当你下载好Junit4插件后,打开 文件File-设置Settings,如图注明修改配置
    在这里插入图片描述
    (5)在 JUnit4 模块里找到此代码 将test去掉
    在这里插入图片描述

    5️⃣简单例子快速入门

    以前测试自己写的代码都是新建一个main方法,然后sysout输出控制台观察结果,繁琐又麻烦!现在让我们开始JUnit单元测试之旅!

    (1)创建简单业务类
    编写一个简单的计算类:Calculate类

    package demo.util;
    
    /**
     * 实现加减乘除的简单计算类
     */
    public class Calculate {
    	public int add(int a, int b) {
    		return a + b;
    	}
    	public int subtract(int a, int b) {
    		return a - b;
    	}
    	public int multiply(int a, int b) {
    		return a * b;
    	}
    	public int divide(int a, int b) {
    		return a / b;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (2)创建测试类

    • 1、在项目目录创建一个文件,用于保存我们的测试代码,创建文件夹test、然后将他设置为测试目录。
      在这里插入图片描述
    • 2、创建Junit4的测试代码,有两种方法(超级简单方便)

    第一种直接点击被测试类Calculator 使用 Ctrl+Shift+T
    在这里插入图片描述
    第二种方法 鼠标右键点击类名 使用 goto-Test即可实现
    在这里插入图片描述

    • 3、创建测试,根据自身需要来勾选
      在这里插入图片描述

    (3)修改生成的测试类代码

    上面步骤生成的测试类只包含测试方法的模板,并没有具体的测试细节,修改成如下:(其中有比较多的重复代码,暂时不管;并将减法的测试设为有问题)

    package demo.util;
    import org.junit.Assert;
    import org.junit.Test;
    
    class CalculateTest {
        Calculate Calculate;
        @Test
        public void testAdd() {
            calculate = new Calculate();
            int result = calculate.add(2, 3);
            Assert.assertEquals("加法有问题", 5, result);
            /*
             * "加法有问题":期望值和实际值不一致时,显示的信息
             * 5 :期望值
             * result :实际值
             * Assert 断言	assertEquals相等断言
             */
        }
        @Test
        public void testSubtract() {
            calculate = new Calculate();
            int result = calculate.subtract(12, 2);
            Assert.assertEquals("减法有问题", 10000, result); //故意设置减法期望值为10000
        }
        @Test
        public void testMultiply() {
            calculate = new Calculate();
            int result = calculate.multiply(2, 3);
            Assert.assertEquals("乘法有问题", 6, result);
        }
        @Test
        public void testDivide() {
            calculate = new Calculate();
            int result = calculate.divide(6, 3);
            Assert.assertEquals("除法有问题", 2, result);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    (4)运行结果

    我们可以通过Run ->Edit Configuration或工具栏上的标签来调整我们测试运行配置:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总共有4个测试方法,运行了4个方法;其中failed有1个,即有一个方法的输出结果跟我们的预期不一样。

    6️⃣JUnit使用注意点及测试失败的两种情况

    (1)JUnit使用的最佳实践

    • 1、测试方法上必须使用@Test进行修饰 (只有添加@Test,才是测试方法,测试的时候才会运行)
    • 2、测试方法必须使用public void 进行修饰,不能带任何的参数
    • 3、新建一个测试代码目录来存放我们的测试代码,即将测试代码和项目业务代码分开(不要在src下放测试代码)
    • 4、测试类所在的包名应该和被测试类所在的包名保持一致
    • 5、测试单元中的每个方法必须可以独立测试,测试方法间不能有任何的依赖
    • 6、测试类使用Test作为类名的后缀(不是必须)
    • 7、测试方法使用test作为方法名的前缀(不是必须)

    (2)测试失败的两种情况

    注意: 测试用例是用来达到测试想要的预期结果,而不能测试出程序的逻辑错误。
    比如:你需要写一个计算长方形面积的方法,而你错误地认为周长的公式就是计算面积的。所以在测试方法中,就算结果达到了你的预期,但这显然不是正确的计算面积方法。

    • 新建测试类
    package demo.util;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class ErrorAndFailureTest {
    	@Test
    	public void testAdd() {
    		int result = new Calculate().add(3, 3);
    		Assert.assertEquals("加法有问题", 5, result); // 预期值与程序输出不一样
    	}
    	@Test
    	public void testDivide() {
    		int result = new Calculate().divide(6, 0); // 除法中,除数为0
    		Assert.assertEquals("除法有问题", 3, result);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 运行结果

    testAdd()方法是failure(失败/故障)错误
    在这里插入图片描述
    testDivide()方法是error错误
    在这里插入图片描述
    说明:

    • Failure一般由单元测试使用的断言方法判断失败所引起的,这经表示测试点发现了问题,就是说程序输出的结果和我们预期的不一样。
    • Error是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中的一个隐藏的bug。

    7️⃣运行流程及常用注解

    (1)JUnit的运行流程

    • 新建测试类

    右键被测试类,新建一个测试类。弹出框中,首先改变测试类所在的代码目录,然后勾选4个方法:
    在这里插入图片描述

    • 修改测试类代码
    package demo.util;
    import org.junit.*;
    
    public class CalculateTest {
        @BeforeClass
        public static void setUpBeforeClass() throws Exception {
            System.out.println("this is setUpBeforeClass()...");
        }
        @AfterClass
        public static void tearDownAfterClass() throws Exception {
            System.out.println("this is tearDownAfterClass()...");
        }
        @Before
        public void setUp() throws Exception {
            System.out.println("this is setUp() @Before");
        }
        @After
        public void tearDown() throws Exception {
            System.out.println("this is tearDown() @After");
        }
        @Test
        public void add() {
            System.out.println("this is add()");
        }
        @Test
        public void subtract() {
            System.out.println("this is subtract()");
        }
        @Test
        public void multiply() {
            System.out.println("this is multiply()");
        }
        @Test
        public void divide() {
            System.out.println("this is divide()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    控制台输出如下:

    this is setUpBeforeClass()...
    this is setUp() @Before
    this is subtract()
    this is tearDown() @After
    this is setUp() @Before
    this is divide()
    this is tearDown() @After
    this is setUp() @Before
    this is add()
    this is tearDown() @After
    this is setUp() @Before
    this is multiply()
    this is tearDown() @After
    this is tearDownAfterClass()...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 总结说明

    @BeforeClass修饰的方法会在所有方法被调用前被执行,而且该方法是静态的,所以当测试类被加载后接着就会运行它,而且在内存中它只会存在一份实例,它比较适合加载配置文件,进行初始化等等;
    @AfterClass所修饰的方法会在所有方法被调用后被执行,通常用来对资源的清理,如关闭数据库的连接;
    @Before和@After会在每个测试方法的前后各执行一次。

    (2)JUnit常用注解

    • @Test:将一个普通的方法修饰成为一个测试方法 @Test(expected=XX.class)
    • @Test(expected=ArithmeticException.class)检查被测方法是否抛出ArithmeticException异
    • @Test(timeout=毫秒)
    • @BeforeClass:它会在所有的方法运行前被执行,static修饰,只执行一次
    • @AfterClass:它会在所有的方法运行结束后被执行,static修饰,只执行一次
    • @Before:会在每一个测试方法被运行前执行一次
    • @After:会在每一个测试方法运行后被执行一次
    • @Ignore:所修饰的测试方法会被测试运行器忽略
    • @RunWith:可以更改测试运行器 org.junit.runner.Runner
    • 一个JUnit4的单元测试用例执行顺序为: @BeforeClass -> @Before -> @Test -> @After -> @AfterClass;
    • 每一个测试方法的调用顺序为: @Before -> @Test -> @After;

    JUnit4和JUnit5对比:

    特性Junit 4Junit 5
    在当前类的所有测试方法之前执行。注解在静态方法上。此方法可以包含一些初始化代码。@BeforeClass@BeforeAll
    在当前类中的所有测试方法之后执行。注解在静态方法上。此方法可以包含一些清理代码。@AfterClass@AfterAll
    在每个测试方法之前执行。注解在非静态方法上。可以重新初始化测试方法所需要使用的类的某些属性。@Before@BeforeEach
    在每个测试方法之后执行。注解在非静态方法上。可以回滚测试方法引起的数据库修改。@After@AfterEach

    @Test、@Ignore的测试

    package demo.util;
    import org.junit.Assert;
    import org.junit.Ignore;
    import org.junit.Test;
    
    public class AnotationTest {
    	@Test(expected = ArithmeticException.class)
    	public void testDivide() {
    		Assert.assertEquals("除法有问题", 3, new Calculate().divide(6, 0)); // 将除数设置为0
    	}
    	@Test(timeout = 2000)
    	public void testWhile() {
    		while (true) {
    			System.out.println("run forever..."); // 一个死循环
    		}
    	}
    	@Test(timeout = 3000)
    	public void testReadFile() {
    		try {
    			Thread.sleep(2000); // 模拟读文件操作
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	@Ignore("...")
    	@Test
    	public void testIgnore() {
    		System.out.println("会运行吗?");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述
    说明:

    • 1、testDivide()方法中,将除数设为0,本会抛出Error,但设置了@Test(expected=ArithmeticException.class),说明我们预期它会抛出一个算术异常,所以程序结果也符合我们的预期。
    • 2、testWhile()方法是一个死循环,但设置了@Test(timeout=2000),即2秒之后,自动结束循环。
    • 3、testReadFile()方法模拟读取文件操作,设置读取超时时间为3秒,等于或大于测试时间则认为不成功,而程序睡眠了2秒,没有超时。这里表示用作一些性能的测试。
    • 4、testIgnore()方法,因为使用的@Ignore注解,所以不会运行。

    8️⃣JUnit测试套件使用及参数化设置

    @RunWith: 当类被@RunWith注解修饰,或者类继承了一个被该注解修饰的类,JUnit将会使用这个注解所指明的运行器(runner)来运行测试,而不是JUnit默认的运行器。

    (1)JUnit测试套件
    如果在测试类不增加的情况下,如何运行所有的单元测试代码类?一个个测试类的执行吗?显然繁琐且费劲。
    将要运行的测试类集成在我们的测试套件中,比如一个系统功能对应一个测试套件,一个测试套件中包含多个测试类,每次测试系统功能时,只要执行一次测试套件就可以了。

    • 测试类及测试套件代码

    新建3个测试任务类:

    package demo.util;
    import org.junit.Test;
    public class TaskTest1 {
    	@Test
    	public void test() {
    		System.out.println("this is TaskTest1...");
    	} 
    }
     
    package demo.util;
    import org.junit.Test;
    public class TaskTest2 {
    	@Test
    	public void test() {
    		System.out.println("this is TaskTest2...");
    	}
    } 
     
    package demo.util;
    import org.junit.Test;
    public class TaskTest3 {
    	@Test
    	public void test() {
    		System.out.println("this is TaskTest3...");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    新建一个套件类,包含以上三个任务类:

    package demo.util;
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;
     
    @RunWith(Suite.class)
    @Suite.SuiteClasses({TaskTest1.class,TaskTest2.class,TaskTest3.class})
    public class SuiteTest {
    	/*
    	 * 1.测试套件就是组织测试类一起运行的
    	 * 
    	 * 写一个作为测试套件的入口类,这个类里不包含其他的方法
    	 * 更改测试运行器Suite.class
    	 * 将要测试的类作为数组传入到Suite.SuiteClasses({})
    	 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:
    在这里插入图片描述

    • 说明

    1、使用@RunWith注解,修改测试运行器。例如@RunWith(Suite.class),这个类就成为测试套件的入口类。
    2、@Suite.SuiteClasses()中放入测试套件的测试类,以数组的形式{class1,class2,…}作为参数

    (2)JUnit参数化设置
    如果测试代码大同小异,代码结构都是相同的,不同的只是测试的数据和预期值,那么有没有更好的办法将相同的代码结构提取出来,提高代码的重用度呢?
    解决:进行参数化测试。

    步骤:

    • 1、要进行参数化测试,需要在类上面指定如下的运行器:@RunWith (Parameterized.class)
    • 2、然后,在提供数据的方法上加上一个@Parameters注解,这个方法必须是静态static的,并且返回一个集合Collection。
    • 3、在测试类的构造方法中为各个参数赋值,(构造方法是由JUnit调用的),最后编写测试类,它会根据参数的组数来运行测试多次。

    代码如下:

    package demo.util;
    
    import org.junit.Assert;
    
    import java.util.Arrays;
    import java.util.Collection;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    import org.junit.runners.Parameterized.Parameters;
    
    @RunWith(Parameterized.class) // 1.更改默认的测试运行器为RunWith(Parameterized.class)
    public class ParameterTest {
    
    	// 2.声明变量存放预期值和测试数据
    	int expected = 0;
    	int input1 = 0;
    	int input2 = 0;
    
    	// 3.声明一个返回值 为Collection的公共静态方法,并使用@Parameters进行修饰
    	@Parameters
    	public static Collection<Object[]> data() {
    		return Arrays.asList(new Object[][] { { 3, 1, 2 }, { 4, 2, 2 } });
    	}
    
    	// 4.为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值
    	public ParameterTest(int expected, int input1, int input2) {
    		this.expected = expected;
    		this.input1 = input1;
    		this.input2 = input2;
    	}
    
    	// 5.运行测试方法,即可完成对多组数据的测试
    	@Test
    	public void testAdd() {
    		Assert.assertEquals(expected, new Calculate().add(input1, input2));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    运行结果:
    在这里插入图片描述

    二、JUnit断言

    1️⃣断言概述

    断言是编程术语,表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
    使用断言可以创建更稳定、品质更好且 不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言(Junit/JunitX)。
    断言More:https://www.cnblogs.com/qiumingcheng/p/9506201.html

    2️⃣断言方法

    这个类提供了很多有用的断言方法来编写测试用例。只有失败的断言才会被记录。Assert 类中的一些有用的方法列式如下:

    MethodDescription
    assertNull(java.lang.Object object)检查对象是否为空
    assertNotNull(java.lang.Object object)检查对象是否不为空
    assertEquals(long expected, long actual)检查long类型的值是否相等
    assertEquals(double expected, double actual, double delta)检查指定精度的double值是否相等
    assertFalse(boolean condition)检查条件是否为假
    assertTrue(boolean condition)检查条件是否为真
    assertSame(java.lang.Object expected, java.lang.Object actual)检查两个对象引用是否引用同一对象(即对象是否相等)
    assertNotSame(java.lang.Object unexpected, java.lang.Object actual)检查两个对象引用是否不引用同一对象(即对象不等)

    3️⃣测试代码

    package demo.util;
    import org.junit.Test;
    import static org.junit.Assert.*;
    
    public class TestAssertions {
       @Test
       public void testAssertions() {
          //test data
          String str1 = new String ("abc");
          String str2 = new String ("abc");
          String str3 = null;
          String str4 = "abc";
          String str5 = "abc";
          int val1 = 5;
          int val2 = 6;
          String[] expectedArray = {"one", "two", "three"};
          String[] resultArray =  {"one", "two", "three"};
    
          //Check that two objects are equal
          assertEquals(str1, str2);
    
          //Check that a condition is true
          assertTrue (val1 < val2);
    
          //Check that a condition is false
          assertFalse(val1 > val2);
    
          //Check that an object isn't null
          assertNotNull(str1);
    
          //Check that an object is null
          assertNull(str3);
    
          //Check if two object references point to the same object
          assertSame(str4,str5);
    
          //Check if two object references not point to the same object
          assertNotSame(str1,str3);
    
          //Check whether two arrays are equal to each other.
          assertArrayEquals(expectedArray, resultArray);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    后记

    在这里插入图片描述
    👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
    👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

  • 相关阅读:
    PHP面试题大全
    磁盘的挂载
    西门子触摸屏上电显示初始画面几秒后,自动切换到下一个画面的具体方法
    MySQL之优化服务器设置(四)
    『期末复习』16/32位微处理器(8086)基本寄存器
    Leetcode周赛368补题(3 / 3)
    [Angular 基础] - 表单:响应式表单
    清华大佬力荐的JVM学习路线+实战笔记+阿里真题,吃透吊打面试官
    Greenplum导入导出-COPY命令
    从零开始Blazor Server(4)--登录系统
  • 原文地址:https://blog.csdn.net/qq_42146402/article/details/127590932