前言

Deencapsulation.newUninitializedInstance(clazz)
跳过构造函数创建对象
我几乎都是参考JMockit中文网学习的Jmockit。在这必须得强烈安利一下啊。其实我刚开始看文档的时候,MockUp@Mock@Test@Mocked@Injectable@CapturingExpectationsVerifications那么多注解和类,傻傻分不清。后来慢慢总结了一下自己的理解思路。
这里,我没有像原笔者从概念->语法->用法->原理那样讲解的那么透彻。我是从一种抛出问题->解决问题的方式来对我学习Jmockit进行总结。

一、 JMockit介绍

JMockit是一款Java的 类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中。因为公司中都是使用JMockit进行单元测试,所以我就学的它。开始学习时,总是会把那些各种注解混搭傻傻分不清,后来看了这篇文章后,突然就能分的清了。
JMockit有两种测试方式:

1、基于状态的Mock:
是站在目标测试代码内部的,可以对传入的参数进行检查、匹配,才返回某些结果,类似白盒。
主要使用MockUp@Mock搭配使用实现Mock

2、基于行为的Mock:
就是对Mock目标代码的行为进行模仿,更像是黑盒测试。
主要使用@Test@Mocked@Injectable@CapturingExpectations搭配使用实现Mock

总结:
其实从大的方向来讲,JMockit只有两种Mock方式:new MockUp()new Expectations() 两种。
(1)注解@Mock是和new MockUp()方式搭配使用。
(2)注解@Test@Mocked@Injectable@Capturing是和new Expectations()方式搭配使用。然后@Mocked@Injectable@Capturing又有不同的特性,就可以解决不同场景下的Mock了。

场景:
下面来通过一个最简单的单元测试场景来学习JMockit的使用:
现在有一个类,我们需要对其中的公有方法、私有方法、静态方法、final方法进行Mock。

/*** @author Caocs* @date 2020/4/18*/
public class Fun {// 静态方法public static String staticFun(int x) {return "this is a static function " + x;}// 公有方法public String publicFun(int x) {return "this is a public function " + x;}// final方法public final String finalFun(int x) {return "this is a final function " + x;}// 私有方法private String privateFun(int x) {return "this is a private function " + x;}// 为了方便测试私有方法是否被Mockpublic String callPrivateFun(int x){return privateFun(x)+" is called";}
}

1、基于状态的Mock

这种写法我觉得非常简洁明了,而且已经能解决绝大部分的单元测试的需求了。针对不能解决的应用场景在后面也会分析。
要讲基于状态的Mock,那么肯定是要明白MockUp和@Mock的作用和优缺点。

作用:
如果我们想对Java中某个类的某个方法进行定制,只需要把该类传入MockUp类的构造函数即可。然后想Mock哪个方法,就在哪个方法上加@Mock注解。没有加@Mock注解的方法不会受影响。

示例代码:

import mockit.Mock;
import mockit.MockUp;
import org.junit.Assert;
import org.junit.Test;/*** @author Caocs* @date 2020/4/18*/
public class MockUpTest {@Testpublic void test() {// 如果想Mock某个类,只需要把这个类传入MockUp类的构造函数即可// 如果想Mock某个方法,就给哪个方法加上@Mock即可// 公有方法、私有方法、静态方法、final方法都可以MockMockUp<Fun> mockUpFun = new MockUp<Fun>(Fun.class) {@Mock // 匿名内部类中,不能加static修饰public String staticFun(int x){return "mock static";}@Mockpublic String publicFun(int x){return "mock public";}@Mock // final修饰符可以省略public final String finalFun(int x){return "mock final";}@Mock // 可以使用public修饰private String privateFun(int x){return "mock private";}};// 使用MockUp进行方法的Mock,是对该类的所有对象都生效的。Fun fun = new Fun(); // 所以此处new出来的实例也生效// Fun fun = mockUpFun.getMockInstance(); // 通过mockUpFun生成的实例也有效Assert.assertEquals("mock private is called",fun.callPrivateFun(10));Assert.assertEquals("mock static",Fun.staticFun(10));Assert.assertEquals("mock public",fun.publicFun(10));Assert.assertEquals("mock final",fun.finalFun(10));}
}

原理:
JMockitnew MockUp()装载的类中的每个方法(只要是经@Mock修饰过的)中插入了一个分支,这个分支就是走MockUp类(即new MockUp{{}}匿名类extends MockUp的子类)的mock方法,因此就达到了Mock的目的。

总结:
使用MockUp实现类中方法的Mock,
(1)可以对指定方法进行Mock。想Mock哪个方法就在哪个方法上加@Mock。不加则不受影响。
(2)对该类的所有对象都生效。无论是依赖注入或者new的多个实例对象,都会生效。

缺点:
(1)一个类多个实例情况。因为是对该类的所有实例都有效,所以如果想对一个类的多个实例有不同的操作的时候,这种写法就很不适用了。
(2)AOP动态生成的类。通过AOP动态生成的类,很可能我们连名称是什么都不知道,更不用提如何适用MockUp进行Mock啦。
(3)需要Mock的方法过多。如果需要Mock的方法过多,我们就要写很多很多代码,很不方便。

适用场景:
其实掌MockUp@Mock就能帮我们解决大部分的Mock场景,而且使用方式直接明了。
这种方式比较适用于对通用类的方法进行Mock。

2、基于行为的Mock

根据上面方希,基于状态的Mock(通过MockUp和@Mock方式)存在多种无法适用的场景。JMockit还提供基于行为的Mock方式。
要讲基于状态的Mock,那么肯定是要明白Expectations@Test@Mocked@Injectable@Capturing的作用和优缺点。

示例:
根据我自己测试来看,如果导包的时候JMockit在JUnit后面,则如果想使用测试参数@Test@Mocked@Injectable@Capturing注解,必须添加在测试类上添加@RunWith(JMockit.class)注解。否则会不支持测试方法注解的属性NullPointerException。但是使用上一节的MockUp方式则不必要。

import java.util.Locale;
public class HelloJMockit {// 向JMockit打招呼public String sayHello() {Locale locale = Locale.getDefault();if (locale.equals(Locale.CHINA)) {// 在中国,就说中文return "你好,JMockit!";} else {// 在其它国家,就说英文return "Hello,JMockit!";}}
}
// 除非在加载jar包时,JMockit包就在JUnit包前面,
// 否则,使用测试参数、@Mocked等注解的话,必须添加@RunWith(JMockit.class)
@RunWith(JMockit.class)
public class ProgramConstructureTest {@MockedHelloJMockit helloJMockit; // 这是一个测试属性@Testpublic void test1() {// 录制(Record)new Expectations() {{helloJMockit.sayHello();result = "hello,david";}};// 重放(Replay)String msg = helloJMockit.sayHello();Assert.assertTrue(msg.equals("hello,david"));// 验证(Verification)new Verifications() {{helloJMockit.sayHello();times = 1;}};}@Testpublic void test2(@Mocked HelloJMockit helloJMockit /* 这是一个测试参数 */) {// 录制(Record)new Expectations() {{helloJMockit.sayHello();result = "hello,david";}};// 重放(Replay)String msg = helloJMockit.sayHello();Assert.assertTrue(msg.equals("hello,david"));// 验证(Verification)new Verifications() {{helloJMockit.sayHello();times = 1; // 验证helloJMockit.sayHello()这个方法调用了1次}};}
}

代码总结:

(1)在JMockit中,我们可以用JMockit的注解API来修饰它。这些API有@Mocked, @Tested, @Injectable, @Capturing。不同的注解有不同的含义。

(2)在上述例子中,我们用@Mocked修饰了测试属性HelloJMockit helloJMockit,表示helloJMockit这个测试属性,它的实例化,属性赋值,方法调用的返回值全部由JMockit来接管,接管后,helloJMockit的行为与HelloJMockit类定义的不一样了,而是由录制脚本来定义了。

(3)给测试方法加参数,原本在JUnit中是不允许的,但是如果参数加了JMockit的注解API(@Mocked, @Tested,@Injectable,@Capturing)后,则是允许的。

通常,在实际测试程序中,我们更倾向于通过JUnit/TestNG/SpringTest的Assert类对测试结果的验证, 对类的某个方法有没调用,调用多少次的测试场景并不是太多。因此在验证阶段,我们完全可以用JUnit/TestNG/SpringTest的Assert类取代new Verifications() {{}}验证代码块。除非,你的测试程序关心类的某个方法有没有调用,调用多少次,你可以使用new Verifications() {{}}验证代码块。如果你还关心方法的调用顺序,你可以使用new VerificationsInOrder() {{}} .这里就不做详细的介绍了。

(1)测试属性&测试参数

a)测试属性:即测试类的一个属性。它作用于测试类的所有测试方法。
b)测试参数:即测试方法的参数。它仅作用于当前测试方法。

(2)Record-Replay-Verification结构

在JMockit单元测试中最常见的写法:录制代码块重放测试逻辑验证代码块。Record-Replay-Verification 是JMockit测试程序的主要结构。
a)Record: 即先录制某类/对象的某个方法调用,在当输入什么时,返回什么。
b)Replay: 即重放测试逻辑。
c)Verification: 重放后的验证。比如验证某个方法有没有被调用,调用多少次。
其实,Record-Replay-Verification与JUnit程序的AAA(Arrange-Action-Assert)结构是一样的。
Record对应Arrange,先准备一些测试数据,测试依赖。Replay对应Action,即执行测试逻辑。Verification对应Assert,即做测试验证。

(3)注解@Mocked, @Tested, @Injectable, @Capturing的区别与用法

注解@Mocked的说明

解释:

  1. 使用@Mocked可以修饰类、接口、抽象类
  2. 使用@Mocked修饰,就是告诉JMockit生成一个Mocked对象,这个对象方法(包含静态方法,私有方法)都返回默认值。
  3. 如果返回类型为原始类型(short、int、float、double、long),则返回0。
  4. 如果返回类型为String类型,则返回null。
  5. 如果返回类型是其他引用类型,则返回这个引用类型的Mocked对象。(这里就又需要Mock这个对象,就这样递归的定义下去)

注意:

  1. 使用@Mocked修饰是对该类的所有实例都生效的。
  2. 但是如果被@Mock修饰的对象是作为参数传入Expectations构造函数中时,会只针对该对象有效。

应用场景:
当我们的测试程序依赖某个接口时,用@Mocked非常适合了。只需要@Mocked一个注解,JMockit就能帮我们生成这个接口的实例。

Expectations主要有两种使用方式:

  1. 通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制
    对类的所有方法都mock了
  2. 通过构建函数注入类/对象来录制
    把待Mock的类传入Expectations的构造函数,可以达到只mock类的部分行为的目的

示例1:
使用@Mocked实例化对象,Expectations不传参数。

import mockit.Expectations;
import mockit.Mocked;
import mockit.integration.junit4.JMockit;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;/*** @author Caocs* @date 2020/4/19*/
// @RunWith(JMockit.class)
public class ExpectationsTest {@Testpublic void test(@Mocked Fun fun/* 使用测试参数,则仅在当前测试方法起作用*/) {/*** 如果Expectations构造函数中,没有传入任何参数* 那么,被@Mocked注解的类(接口/抽象类)中的方法会被全部Mock掉。规则如上所述。* 并且,如果在Expectations中指定方法,就会按照指定的来。*/new Expectations() {{fun.publicFun(1);result = "mock public";Fun.staticFun(1); // Mock 静态方法result = "mock static";}};Assert.assertEquals("mock public", fun.publicFun(1)); // System.out.println(fun.callPrivateFun(1)); // null 说明fun对象的所有方法都被使用默认规则Mock掉了System.out.println(Fun.staticFun(1)); // mock static 说明静态方法也被Mock掉了Fun fun1 = new Fun(); // 用于测试new出来的新实例System.out.println(fun1.publicFun(1)); // mock public 说明如果在Expectations中指明方法的话,新实例的该方法也被指定了System.out.println(fun1.callPrivateFun(1)); // null 说明new出来的新实例对象的所有方法都被使用默认规则Mock掉了}
}

示例2:
使用@Mocked实例化对象,Expectations传入类。

/*** @author Caocs* @date 2020/4/19*/
public class ExpectationsTest {@Testpublic void test(@Mocked Fun fun/* 使用测试参数,则仅在当前测试方法起作用*/) {/*** 如果Expectations构造函数中,传入类* 那么,这个类只有在Expectations中指定的方法会被按照指定的来Mock* 并且,在Expectations中指定的方法是对该类的所有实例有效* 并且,其他没有被指定的方法并不会被Mock掉,还是执行原来的逻辑* 所以,这样就可以实现Mock指定类的指定方法*/new Expectations(Fun.class) {{fun.publicFun(1);result = "mock public";}};Assert.assertEquals("mock public", fun.publicFun(1));System.out.println(fun.callPrivateFun(1)); // this is a private function 1 is called 说明没有被指定的方法还是执行原来的逻辑Fun fun1 = new Fun();System.out.println(fun1.publicFun(1)); // mock public 说明新实例的指定方法也别Mock掉了System.out.println(fun1.callPrivateFun(1)); // this is a private function 1 is called 说明新实例的没有被指定的方法还是执行原来的逻辑}
}

示例3:
使用@Mocked实例化对象,Expectations传入对象。

/*** @author Caocs* @date 2020/4/19*/
public class ExpectationsTest {@Testpublic void test(@Mocked Fun fun/* 使用测试参数,则仅在当前测试方法起作用*/) {/*** 如果Expectations构造函数中,传入对象* 那么,这个类只有在Expectations中指定的方法会被按照指定的来Mock* 并且,在Expectations中指定的方法是对该对象有效,对该类的其他实例无效* 并且,其他没有被指定的方法并不会被Mock掉,还是执行原来的逻辑* 所以,这样就可以实现Mock指定对象的指定方法*/new Expectations(fun) {{fun.publicFun(1);result = "mock public";}};Assert.assertEquals("mock public", fun.publicFun(1));System.out.println(fun.callPrivateFun(1)); // this is a private function 1 is called 说明没有被指定的方法还是执行原来的逻辑Fun fun1 = new Fun();System.out.println(fun1.publicFun(1)); // this is a public function 1 说明新实例的指定方法还是执行原来的逻辑System.out.println(fun1.callPrivateFun(1)); // this is a private function 1 is called 说明新实例的没有被指定的方法还是执行原来的逻辑}
}

注解@Injectable的说明

注解@Injectable只针对其修饰的实例,所以对类的静态方法、构造函数都没有影响。因为它只影响某一个实例嘛。

/**1. @author Caocs2. @date 2020/4/19*/
public class InjectableTest {@Testpublic void test(@Injectable Fun fun/* 使用测试参数,则仅在当前测试方法起作用*/) {new Expectations() {{fun.publicFun(1);result = "mock public";}};Assert.assertEquals("mock public", fun.publicFun(1)); // 说明该对象的公有方法被Mock了System.out.println(Fun.staticFun(1)); // this is a static function 1 说明静态方法没有被MockFun fun1 = new Fun(); // @Injectable不会影响构造方法System.out.println(fun1.publicFun(1)); // this is a public function 1 说明new的实例对象方法没有被MockSystem.out.println(fun1.callPrivateFun(1)); // this is a private function 1 is called}
}

注解@Tested的说明

使用@Tested修饰的类,表示我们要测试对象。JMockit也会帮我们实例化这个测试对象。

如何实例化@Tested修饰的类:

  1. 如果该对象没有赋值,就去实例化它。
  2. 首先,对构造函数实例化。
  3. 若@Test对象的构造函数有参数,则JMockit通过测试属性和测试参数中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化。
  4. 若@Test对象的构造函数没有参数,则用无参构造函数来实例化。
  5. 然后,需要将@Tested对象中其他字段属性注入。
  6. JMockit通过属性查找的方式,把@Injectable对象注入到@Tested对象中。
  7. 注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象。

注意:
当然,我们的测试程序要尽量避免这种情况出现。
因为给哪个测试属性、测试参数加@Injectable,是人为控制的

应用场景:
当我们需要手动管理被测试类的依赖时,就需要用到@Tested和@Injectable。
两者搭配使用,JMockit就能帮我们轻松搞定被测试类及其依赖注入细节。

@Tested & @Injectable 搭配使用
其实,我们在使用的过程中,往往将@Tested & @Injectable 搭配使用。
示例1:

public interface MailService {public boolean sendMail(long userId, String content);
}
public interface UserCheckService {public boolean check(long userId);
}
public class OrderService {// 邮件服务类,用于向某用户发邮件。@ResourceMailService mailService;// 用户身份校验类,用于校验某个用户是不是合法用户UserCheckService userCheckService;// 构造函数public OrderService(UserCheckService userCheckService) {this.userCheckService = userCheckService;}public boolean submitOrder(long buyerId, long itemId) {// 先校验用户身份if (!userCheckService.check(buyerId)) {// 用户身份不合法return false;}if (!this.mailService.sendMail(buyerId, "下单成功")) {// 邮件发送成功return false;}return true;}
}
@RunWith(JMockit.class)
public class TestedAndInjectable {@Testpublic void testSubmitOrder(@Tested OrderService orderService, // @Tested修饰的类,表示是我们要测试对象,在这里表示,我想测试订单服务类。JMockit也会帮我们实例化这个测试对象@Injectable MailService mailService,@Injectable UserCheckService userCheckService) {//测试用户IDlong testUserId = 123456l;//测试商品idlong testItemId = 456789l;new Expectations() {{// 当向testUserId发邮件时,假设都发成功了mailService.sendMail(testUserId, anyString);result = true;// 当检验testUserId的身份时,假设该用户都是合法的userCheckService.check(testUserId);result = true;}};// JMockit帮我们实例化了userCheckService了,并通过OrderService的构造函数,注入到orderService对象中。// JMockit帮我们实例化了mailService了,并通过OrderService的属性,注入到orderService对象中。Assert.assertTrue(orderService.submitOrder(testUserId, testItemId));}
}

注解@Capturing的说明

应用场景:
注解@Capturing主要用于子类/实现类的Mock我们只知道父类或接口时,但我们却需要控制它所有子类的行为时,子类可能有多个实现(可能有人工写的,也可能是AOP代理自动生成时),就用@Capturing

示例1:
其实我从来没用过哈哈哈哈。借用一下JMockit中文网的代码。

//@Capturing注解用途
public class CapturingTest {// 测试用户IDlong testUserId = 123456l;// 权限检验类,可能是人工写的IPrivilege privilegeManager1 = new IPrivilege() {@Overridepublic boolean isAllow(long userId) {if (userId == testUserId) {return false;}return true;}};// 权限检验类,可能是JDK动态代理生成。我们通常AOP来做权限校验。IPrivilege privilegeManager2 = (IPrivilege) Proxy.newProxyInstance(IPrivilege.class.getClassLoader(),new Class[] { IPrivilege.class }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) {if ((long) args[0] == testUserId) {return false;}return true;}});// 有Cautring情形@Testpublic void testCaputring(@Capturing IPrivilege privilegeManager) {// 加上了JMockit的API @Capturing,// JMockit会帮我们实例化这个对象,它除了具有@Mocked的特点,还能影响它的子类/实现类new Expectations() {{// 对IPrivilege的所有实现类录制,假设测试用户有权限privilegeManager.isAllow(testUserId);result = true;}};// 不管权限校验的实现类是哪个,这个测试用户都有权限Assert.assertTrue(privilegeManager1.isAllow(testUserId));Assert.assertTrue(privilegeManager2.isAllow(testUserId));}// 没有Cautring情形@Testpublic void testWithoutCaputring() {// 不管权限校验的实现类是哪个,这个测试用户没有权限Assert.assertTrue(!privilegeManager1.isAllow(testUserId));Assert.assertTrue(!privilegeManager2.isAllow(testUserId));}
}

(4)总结

实例范围 方法范围
@Mocked 类的所有实例都有效 静态方法、私有方法、公有方法
@Injectable 只对修饰的实例有效 对静态方法,构造函数无效
@Tested 标识被测试的对象
@Capturing 子类、实现类都有效 静态方法、私有方法、公有方法

其实我的总结就是:

  1. 如果能使用MockUp,则直接使用MockUp。
  2. 如果不行,再考虑使用@Mocked进行Mock。(但是使用@Mocked方式和MockUp方式我觉得大同小异,只是可以针对性Mock方法。)
  3. 如果需要手动管理被测试类的依赖注入,就使用@Tested和@Injectable搭配的方式。我觉得这种写法的可能性都已经很小很小了。
  4. 如果需要对子类、实现类进行Mock,就使用@Capturing的方式。其实我从来没遇到过哈哈哈。

参考文章:

JMockit中文网 个人强烈推荐
使用Junit4和JMockit进行单元测试
浅谈Jmockit使用
spring+jmockit单元测试
单元测试之初步使用篇(testng + jmockit + springboot)

Jmockit的使用总结相关推荐

  1. 【原创】如何使用Jmockit进行单元测试

    如何使用jmockit进行单元测试 1. Jmockit简介 JMockit 是用以帮助开发人员编写测试程序的一组工具和API,它完全基于 Java 5 SE 的 java.lang.instrume ...

  2. JUnit+JMockit单元测试

    1.简介 对编码完成的功能,进行测试,是每个程序员最熟悉不过的事了,每完成一部分功能,都需要对实现的功能进行测试,然后才能进行交付.但如何保证自己完成的每个功能都是正确无误的呢?对,单元测试! 2.J ...

  3. JMockit 1.37 示例

    2019独角兽企业重金招聘Python工程师标准>>> 环境 1. JDK 1.8 2. 依赖 <dependency><!-- jmockit 必须在Junit之 ...

  4. 使用 JMockit 来 mock 构造函数

    Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式,经常这也 Mock 不了,那也 Mock 不了,需要迁就于测试来调整实现代码,使 ...

  5. JMockit常用操作

    JMockit常用操作 2017-11-30 转自:http://blog.csdn.net/foreverling/article/details/51234149 目录 1 基本概念   1.1 ...

  6. (三)JMockit API:@Mocked -基础篇

    @Mocked可以修饰一个类,接口等. 当@Mocked修饰一个类 import mockit.Mocked; import org.junit.Assert; import org.junit.Te ...

  7. java mocked,JMockit 中被 Mocked 的对象属性及方法的默认值

    前脚研究完 Mockito 中被 Mocked 的对象属性及方法的默认值, 虽然目今更多的是拥抱着 Mockito, 但总有时对 JMockit 也会挤眉弄眼,谁叫 JMockit 无所不能呢!被 M ...

  8. Jmockit使用笔记_基本功能使用@Tested_@Injectable_@Mocked_Expectations

    Jmockit使用笔记 测试框架Jmockit集合junit使用 @RunWith(JMockit.class) 写在测试案例类上的注解 @Tested 在测试案例中,写在我们要测试的类上面, 一般用 ...

  9. (四)JMockit 的API:@Injectable 与 @Mocked的不同--基础篇

    @Injectable 与 @Mocked的不同 import mockit.Injectable; import mockit.Mocked; import org.junit.Assert; im ...

  10. JMockit @mocked 注释标签

    使用场景举例,我们做代码测试时会遇到待测代码调用其他人写的函数的情况,常规办法是修改源代码进行模拟返回,临时跳过别人的代码,这种方法容易产生隐藏的风险,例如没有及时改回去,导致测试代码被提交. JMo ...

最新文章

  1. 16S预测细菌表型-bugbase:革兰氏阴阳、生物膜、致病力、移动元件、氧气消耗等...
  2. Spring-Cloud 从0开始(二) Eureka-Client
  3. initBuilder注解接收多个对象
  4. 用SAXBuilder、Document、Element操作xml
  5. cents上运行wget报错:unable to resolve host address
  6. U3D中物体的渲染顺序
  7. 【图嵌入】DeepWalk原理与代码实战
  8. shields 徽标_到处都有平面徽标
  9. 深度学习基本概念笔记
  10. js结合jquery实现的ajax瀑布流加载实例
  11. 富士康筹划在越南建造2.7亿美元新工厂,扩大生产线!
  12. 在Visual Studio 2017中找不到.NET Framework 4.6.2
  13. 品质主管每日工作需要做哪些_游戏配音需要做哪些工作?
  14. 【新Attention】最强的Attention函数诞生啦,带给你意想不到的巨大提升!
  15. Java获取打印机打印图片
  16. linux切换声卡,Ubuntu中双声卡使用实例
  17. 使用rufus制作windows系统安装u盘
  18. 我的大一C++学习笔记
  19. Better than better,白山EC2.0发布
  20. srs源码分析2-浅析state_threads

热门文章

  1. QProcess调用外部程序
  2. 【书山有路】SQL必知必会 第13课
  3. Python爬虫:关于scrapy、Gerapy等爬虫相关框架和工具
  4. 苹果企业开发者账号值不值得申请呢!
  5. 《大话数据结构》框要
  6. 关于深度学习人工智能模型的探讨(三)(1)
  7. 计算机博士美国高校雅思要求,博士生考试
  8. 通信协议篇---TLS(未完)
  9. Python sh脚本
  10. 初级日语|日语助词有哪些?作用是什么?