你永远不能保证你的代码是正确的,你只能证明它是错的。

测试

如果没有测试过,它就是不能工作的。

Java语言是一种静态类语言, 程序员经常觉得,代码能通过编译器就是没问题, 其实这只是表面上的满足编译器的语法规则而已, 迈向代码校验的第一步就是创建测试

单元测试

“单元”是指它是测试的一小部分,通常每个类都有测试来检查它所有的方法, “系统”测试则是不同的,它检查的是整个程序是否满足要求

JUnit

@BeforeAll 这个注释是在任意测试方法之前执行, 被修饰的方法必须是静态的
@AfterAll 这个注释是在所有测试方法执行结束之后执行, 被修饰的方法也必须是静态的
@BeforeEach 这个注解通常是用来注释创建或初始化公共对象的方法, 在每次测试执行之前进行, 它跟构造器有几分相似, 唯一的区别就是所有测试的所有对象都是同时创建的(而不是在测试之前创建对象), 而@BeforeEach 能保证他在测试方法执行之前调用
@AfterEach 这个是标识测试之后执行, 用于清除资源之类的
@Test 这个注释是用来让Junit来发现测试方法的

前置条件

断言(Assertions)

Java 断言语法

断言需要在运行时启动, 正常为关闭状态, 可以通过 -ea(enableAssert) 来启动程序, 也可以用 setDefaultAssertionStatus(true)来开启断言

日志

java自带的日志包不好用, 一般我们用SLF4J, SLF4J的输出格式,信息,甚至是输出的是否正常都是取决于SLF4J链接的后端包

日志等级

SLF4J提供了多个等级的日志消息, 下面的例子是”严重性”从低到高

  1. // validating/SLF4JLevels.java
  2. import org.slf4j.*;
  3. public class SLF4JLevels {
  4. private static Logger log =
  5. LoggerFactory.getLogger(SLF4JLevels.class);
  6. public static void main(String[] args) {
  7. log.trace("Hello");
  8. log.debug("Logging");
  9. log.info("Using");
  10. log.warn("the SLF4J");
  11. log.error("Facade");
  12. }
  13. }
  14. /* Output:
  15. 2017-05-09T06:07:52.846
  16. [main] TRACE SLF4JLevels - Hello
  17. 2017-05-09T06:07:52.849
  18. [main] DEBUG SLF4JLevels - Logging
  19. 2017-05-09T06:07:52.849
  20. [main] INFO SLF4JLevels - Using
  21. 2017-05-09T06:07:52.850
  22. [main] WARN SLF4JLevels - the SLF4J
  23. 2017-05-09T06:07:52.851
  24. [main] ERROR SLF4JLevels - Facade
  25. */

你可以按照等级来查找消息, 级别通常是单独配置在XML文件中的, 如果你没有配置, 那么SLF4J将采取默认配置

调试

尽管System.out 或者日志能带来系统运行的有效见解, 但是对于困难的难题来说 , 它就是比较笨拙的,而且比较耗时

这时候就需要通过调试来进行

使用 JDB 调试

java调试器(JDB)是jdk内置的命令行工具,
肯定还是idea的debug好用

基准测试

基准测试意味着对代码或算法片段进行计时看哪个跑得更快,与下一节的分析和优化截然相反,分析优化是观察整个程序,找到程序中最耗时的部分。

微基准测试

写一个计时工具来比较不同的代码——>没什么用

代码的运行速度受很多影响, 如内存什么的, 如果你的数据很大, 内存满了,也会导致运行停止, 同时java虚拟机的Hotshot也非常影响运行效率, 第一次运行的”冷处理”,比”预热处理”慢等等

JMH 的引入

截止目前为止,唯一能产生像样结果的 Java 微基准测试系统就是 Java Microbenchmarking Harness
你可以在命令行编写 JMH 代码并运行它,但是推荐的方式是让 JMH 系统为你运行测试;build.gradle 文件已经配置成只需要一条命令就能运行 JMH 测试。

这里涉及到算法啥, 先不深入了解了

剖析和优化

有的时候你需要知道程序的时间花在哪了, 从而针对花费时间的代码进行调整, 解剖器可以找到这些导致程序变慢的地方, 因而你可以找到最轻松, 最明显的方式来加快程序运行

解剖器收集的信息能显示程序哪一部分消耗内存, 哪一部分最消耗内存, 甚至可以关闭垃圾回收,从而帮助限定内存分配的模式

解剖器还可以帮助检查程序中的死锁, 注意剖析跟基准测试的区别, 剖析关注的是已经运行在真实数据上的整个程序, 而基准测试关注的是程序中隔离的片段,通常是去优化算法

安装java开发工具包(JDK)时,会顺带安装一个虚拟的解剖器, 叫做VisualVM, 它会被自动安装在javac的相同目录下.
你的执行路径应该包含该目录, 启动VirsualVM的控制台指令是: >jvisualvm, 运行后会弹出一个窗口, 其中包括一些指向信息的链接

优化准则

  • 避免牺牲代码的可读性
  • 不要独立的看待代码的性能, 要衡量其带来的收益和你付出的工作量
  • 程序大小很重要, 性能对大型的长时间运行的程序才有价值, 而不是小型程序的关注点
  • 运行起来程序比一心钻研它的性能更具优先级, 一旦你的程序可以运行, 你可以通过解剖器来提高它的效率, 只有当性能是关键性因素的时候, 才需要你在开发/设计阶段考虑它
  • 不要靠猜, 让解剖器来告诉你运行的平静在哪
  • 不论何时, 如果可能的话 ,在不使用对象的时候设置其为 null, 这对垃圾处理系统是一个暗示
  • static final 修饰的变量会被jvm优化从而提高其运行效率,因此常量经常应该使用static final修饰