Java8 Lambda 表达式与 Checked Exception

当我们在使用 Java 8 的 Lambda 表达式时,表达式内容需要抛出异常,也许还会想当然的让当前方法再往外抛来解决编译问题,如下面的代码

main() 方法抛出 Exception 还是不解决决编译错误,仍然提示 "Unhandled exception: java.io.FileNotFoundException"。

因为我们可能保持着惯性思维,忽略了 Lambda 本身就是一个功能性接口方法的实现,所以把上面的代码还原为匿名类的方式

那么对于上面那种情况应该如何处理呢? 阅读全文 >>

Java 运行时如何获取泛型参数的类型

在 Java 中对于下面最简单的泛型类

class A<T> {
    public void foo() {
        //如何在此处获得运行时 T 的具体类型呢?
    }
}

设想我们使用时

new A<String>().foo();

是否能在 foo() 方法中获得当前的类型是 String 呢?答案是否定的,不能。在 foo() 方法中 this 引用给不出类型信息,this.getClass() 就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类,this.getClass() 实例例是被所有的不同具体类型的 A 实例(new A<String>(), new A<Integer>() 等) 共享的,所以在字节码中类型会被擦除到上限。

我们可以在 IDE 的调试时看到这个泛型类的签名

阅读全文 >>

《Practical Vim》阅读笔记 (2)

1. Vim 的模式: 通常我们说有三种模式,正常模式(Normal Mode), 插入模式(Inert Mode) 和可视模式(Visual Mode). 其实除此之外还有
    4) Operator-Pending Mode: 就是正常模式下按下操作(Operator) 指令(如 c, d 等) 后,等待输入移动(Motion) 指示时的模式。这时 Escape 或 Motion 指令后退回到正常模式
    5) Insert Normal Mode: 在插入模式时,按下 <Ctrl-o> 组合键后进入该模式, 此时可接受一个 Action (Operator + Motion = Action), 比如 3dd, 然后自动返回到插入模式。<Esc> 也能从 Insert Normal Mode 返回到插入模式。这方便了插入模式只想执行一次指令操作时来回切换, Insert Normal Mode 在 Vim 的状态栏中显示为下图那样 - NORMAL --(insert) --

    6) 替换模式(Replace Mode)-- REPLACE --R 会进到替换模式,随后输入往后覆盖,可尝试 <Insert> 键;  r{char} 和 gr{char} 临时进入单字符的替换模式,替换完当前字符立即退到正常模式,也就是光标不往下走。
    7) 可视替换模式(Visual Replace Mode) -- VREPLACE --, 用 gR 进入该模式,在处理 tab 字符时会更友好,还有别的好处吧,所以该书建议尽量用可视替换模式。
    8) 可视模式(Visual Mode),这就复杂了。它有三个子模式:
      character-wise Visual mode --  -- VISUAL ;
      line-wise Visual mode --  -- VISUAL LINE --
      block-wise Visual mode -- -- VISUAL BLOCK --

    9) 选择模式(Select Mode),可视模式下选择了内容后用 <C-g> 可在两种模式下切换,状态栏 -- VISUAL -- 和 -- SLECT -- 的不同,还有选择模式下直接输入就会替换当前的内容,与其他编辑器的行为一样,而可视模式需要按 c 来替换当前内容。 阅读全文 >>

使用 Byte Buddy 运行时生成泛型子类

在上一篇中尝试了 使用 Javassist 运行时生成泛型子类,这里要用另一个更方便的字节码增加组件 Byte Buddy 来实现类似的功能, 但代码上要直白一些。就是运用 Byte Buddy 在运行时生成一个类的子类,带泛型的,给类加上一个注解,可生成类文件或 Class 实例,不过这里更进一步,实现的方法是带参数的。

用 Byte Buddy 操作起来更简单,根本不需要接触任何字节码相关的,诸如常量池等概念。与 Javassist 相比,Byte Buddy 更为先进的是能生成的类文件都是可加载运行的,不像 Javassist 生成的类文件反编译出来是看起来是正常的,但一加载执行却不那回事。

本例所使用的 Byte Buddy 的版本是当前最新的 1.6.7,在 Maven 项目中用下面的方式引入依赖

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.6.7</version>
</dependency>

下面是几个需要在本例中用到的类定义 阅读全文 >>

使用 Javassist 运行时生成泛型子类

越是复杂的项目希望使用者能愉快的编码的话,可能就要使用到字节码增强工具来暗地里做些手脚。这方面的工具有 JDK 的 Instrumentation, ASM, BCEL, CGLib, Javassist, 还有 Byte Buddy. Javassist 和 Byte Buddy 更贴近我们编码中的概念,使用起来也简单,而其他几个工具需要我们更多的了解字节码指令,以及常量池等概念。所以我着重去了解怎么运用 Javassist 和 Byte Buddy 来动态修改来生成类文件。

所以本文是系列中的第一篇,旨在以一个 Javassist 的例子来了解它的基本使用方法。本例中在运行时动态生成一个类的子类,并且是泛型的,实现了一个方法,给类加上了一个注解,最终生成一个类文件。总之尽可能的让这个例子具有代表性,同时又需控制它的复杂性。最后通过加载类文件的方式来验证前面生成的类是否是正确的,也可以直接反编译生成的类文件来查看源代码,不过实际操作中我们可能会被反编译出来的源代码欺骗。

本例所使用的 Javassist 的版本是 3.21.0-GA, 是在一个 Maven 项目中测试的,所以 Maven 的依赖是

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.21.0-GA</version>
</dependency>

阅读全文 >>

Java 8 可重复注解的理解与应用

Java 8 之前如何重复使用注解

在 Java 8 之前我们不能在一个类型重复使用同一个注解,例如 Spring 的注解 @PropertySource 不能下面那样来引入多个属性文件

@PropertySource("classpath:config.properties")
@PropertySource("file:application.properties")
public class MainApp {}

上面的代码无法在 Java 7 下通过编译,错误是: Duplicate annotation

于是我们在 Java 8 之前想到了一个方案来规避 Duplicate Annotation 的错误: 即声明一个新的 Annotation 来包裹 @PropertySource, 如 @PropertySources

然后使用时两个注解齐上阵 阅读全文 >>

Mockito 如何 mock 返回值为 void 的方法

最初接触 Mockito 还思考并尝试过如何用它来 mock 返回值为 void 的方法,然而 Google 查找到的一般都会说用 doThrow() 的办法

doThrow(new RuntimeException()).when(mockObject).methodWithVoidReturn();

因为无法使用常规的 when(mockObject.foo()).thenReturn(...) 的方法。

当时我就纳闷,为何我想 mock 一个返回值为 void 的方法,却是在模拟抛出一个异常,现在想来如果一个返回值为 void 的方法,为何要去 mock 这个方法呢?

回想一个我们要 mock 一个方法的意图是什么:

  1. 在特定输入参数的情况下期待需要的输出结果(返回值)
  2. 在方法抛出某种类型异常调用者作出的反应

对于 void 返回值的方法,如果要验证有没有被调用过几次可以在事后用 verify() 方法去断言。所以基本上对于 void 返回值的方法一般可不用去 mock 它,只需用  verify() 去验证,或者就是像前面一样模拟出现异常时的情况。

所以本文并不像是去直接回答标题所示的问题: Mockito 如何 mock  返回值为  void 的方法,而是如何应对 mock  对象的  void 方法 阅读全文 >>

Mockito 中捕获 mock 对象方法的调用参数

Mockito 可以帮助我们创建 Mock 对象,mock 被调用的方法,断言调用次数,在方法参数不易确定的情况下还能帮我们捕获参数。下面是我们第一个问题:

为什么要捕获调用参数

在被 mocker 方法调用参数明确的情况下可无需捕获参数,例如,下面的情景:

如果 UserService 的 save(user) 最终操作的不是同一个对象,它的实现稍加变化如下 阅读全文 >>

5 个最好的 Vim 速查卡 (Cheat Sheet)

Vim(Vi Improved) 早已替代了 Vi, 它存在于大多数的 Linux 发行版中。所以基本上 Vi 和 Vim 在你的系统中就是同一个程序,我用的 Mac, vi 命令就是一个指向到 vim 的链接

ls -l $(which vi)
lrwxr-xr-x 1 root wheel 3 Sep 20 23:47 /usr/bin/vi -> vim

macOS Sierra 自带的 Vi/Vim 版本仍然是 7.4,我用 brew install macvim 安装了最新版的 Vim 8.0, 由于只想启动 MacVim 控制台的 Vim,  所以把 vi/vim 命令链接到新版 Vim 上。

alias vim=/usr/local/Cellar/macvim/8.0-121/MacVim.app/Contents/MacOS/Vim
alias vi=vim

这里找来了 5 个最好的 Vim Cheat Sheet, 不仅每个按键本身的操作,还有组合健, 窗口,缓冲区,寄存器等操作。

1. Vim Cheat Sheet for Programmers

阅读全文 >>

《Practical Vim》阅读笔记 (1)

Vim 的东西时而学一点,但很快又会忘记,就是最简单的 h, j, k, l 来移动光标都会有所迟疑,因为一直未强迫自己完全脱离方向键来使用 Vim,下个目标是 87 键的键盘都嫌多,打算入一个 61 键的 WSAD 键盘, HHKB 还是有些极端了。

找到了一本学习 Vim 的好书 《Practical Vim》第二版,阅读时把对自己有用的东西仅当笔记记下来备忘,这个其实更应该记录在我的 Evernote 中作为私有笔记。重点的东西我在书中注解了,这里的笔记只能算是一个补充。 阅读全文 >>