Java8 Optional 几个常见错误用法

Java 8 引入的 Optional 类型,基本是把它当作 null 值优雅的处理方式。其实也不完全如此,Optional 在语义上更能体现有还是没有值。所以它不是设计来作为 null 的替代品,如果方法返回 null 值表达了二义性,没有结果或是执行中出现异常。

在 Oracle  做  Java 语言工作的  Brian Goetz 在 Stack Overflow 回复  Should Java 8 getters return optional type? 中讲述了引入  Optional 的主要动机。

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.

说的是  Optional 提供了一个有限的机制让类库方法返回值清晰的表达有与没有值,避免很多时候 null 造成的错误。并非有了  Optional 就要完全杜绝 NullPointerException。

在 Java 8 之前一个实践是方法返回集合或数组时,应返回空集合或数组表示没有元素; 而对于返回对象,只能用 null 来表示不存在,现在可以用  Optional 来表示这个意义。

自 Java8 于  2014-03-18 发布后已 5 年有余,这里就列举几个我们在项目实践中使用 Optional 常见的几个用法。

Optional 类型作为字段或方法参数

这儿把 Optional  类型用为字段(类或实例变量)和方法参数放在一起来讲,是因为假如我们使用 IntelliJ IDEA 来写 Java 8 代码,IDEA 对于  Optional 作为字段和方法参数会给出同样的代码建议: 阅读全文 >>

Java 9 - 说说响应式流

最初看到 Java 9 的这个新特性没太在意,及至重新关注到 Spring 5/Springboot 2 的响应式编程的时候才真正重视起 Reactive Streams(响应式流或反应式流)。应用响应式流的编程也就叫做响应式编程(Reactive Programming),无论是翻译成反应式编程都有些令人摸不准头脑。与此对应的在 Web 设计方面有一个叫做响应式 Web 设计(Responsive web design),两个词都译作响应式,却有些差别,大概是 Reactive 被译为反应的原因之一。

通过这里对  Reactive Streams 的学习,主要目的是为了进一步掌握 Spring 5/Springboot 2 的响应式 MVC 作铺垫的,不至于猛然间见 Flux, Mono 而不知所措。

函数式响应式编程概念最早来自于九十年代末,这也激发了微软的 Erik Meijer 设计开发了 .NET 的  Rx(Reactive eXtension) 库,以及到后来 Netflix 的  RxJava 也与他有关系。Reactive Stream 更像是一种编程模式,致力于解决一个生产者产生一系列消息,一个或多个去消费它们的问题。两者的名词我们会用: producer-consumer(生产者-消费者), source/sink(水源/水槽, Akka Stream 用了这个概念), publisher-subscriber(发布者-订阅者)。

既然 Reactive Stream 和 Java 8 引入的 Stream 都叫做流,它们之间有什么关系呢?有一点关系,Java 8 的 Stream 主要关注在流的过滤,映射,合并,而  Reactive Stream 更进一层,侧重的是流的产生与消费,即流在生产与消费者之间的协调。 阅读全文 >>

相比于 Java 10 的 var, 更期待 Java 11 支持多行字符串

Java 六个月的版本迭代周期让我们突然间有些喘不过气来,又 Java 11 才会是一个长期支持版本,如果可能的话我们将会是从 Java 8 直接往 Java 11 跳去。在 Java 8 大行其道,裹足不前的当下,谈论着未来 Java 11 不确定的新特性,有一种看着别人家碗里的不过瘾,还要看着别人家锅里的感觉。

本篇原本只是为了对 Java 11 潜在的原始字符串字面量(Raw String Literals)的哨探,同时考虑到 Java 10 仅有的一个语言层面的新特性 var 局部变量类型推断,所以顺带介绍一下。

我这儿把它称之为 var 局部变量类型推断,说明了var 类型推断并不适用于类或实例的变量,或方法的参数。而且 var 也并未上升为一个  Java 的关键字,我们仍然可以用 var 作为变量或方法名。

对局部变量用 var 进行类型推断的代码示例变化如下:

var 学了 Scala 的语法,再重复一遍,var 只能用于局部变量,这有一个好处就是局部变量的跨越性较小,不至于 var 声明的变量阅读时只有编译器知道它的确切类型。var 类型推断给代码书写上的变化就是对局部变量的任何类型声明都可以替代为 var阅读全文 >>

WordPress 在 Linux 下不能向 Gmail 邮箱发邮件的问题

一年前解决了 WordPress 在 Linux 下不能发送邮件的问题,后来有段时间很正常,我的网站接收邮件的邮箱是 Gmail 的。但是近来,网站上有留言时极少收到通知邮件,怀疑是 Debian 下的  exim4 又不能正常工作了。但是试了如下的 PHP 脚本

$ php -a
Interactive mode enabled
php> mail('<my_gmail_account>@gmail.com', 'test subject', 'test content');
php > exit

从命令行上并没有提示任何的错误,但是检查我的 Gmail 信箱,死活就是收不到邮件。即使直接用  mail 命令

echo Hello World | mail -v -s Test <my_gmail_account>@gmail.com

也是不行的。

查看日志文件 /var/log/exim4/mainlog, 发现有下面的错误信息 阅读全文 >>

Linux 下配置滚动日志之 rotatelogs

上一篇 Linux 下配置滚动日志之 logrotate, 介绍了定时服务 logrotate 的方式对日志进行滚动,删除旧归档。logrotate 是目前流行 Linux 发行版内置的定时服务,默认每日根据配置文件来滚动日志文件。那么它可能有一个弊端就是,如果每日增长的日志超大,同时会暴盘,那么就必须调整 logrotate 这个 cron 定时服务为每小时,或自定义的 cron 表达式来控制。

而本文所介绍的 rotatelogs 工具(与 logrotate 名称太过相似) 是采用管道操作的方式来控制日志的滚动,可以基于实时监控的日志文件大小来滚动日志,也可以配置像 logrotate 来定时滚动日志。rotatelogs 是出自于 Apache HTTP Server 家族的,它被用于 Apache HTTP Server 的就错误日志,访问日志的滚动控制。

类似于 rotatelogs 的工具不有一个老旧的 cronolog,也是应用管道操作控制日志,它早已无人问津,最近更新在五年前,且功能很弱,只带滚动,不能清理旧归档,磁盘空间占用仍然是无上限。 阅读全文 >>

本站已启用新域名 yanbin.blog

本站已启用新域名 yanbin.blog, 博客链接已更新为 https://yanbin.blog,  完全脱离了主流域名后缀了。其实也是不得已而为之,毕竟  unmi.cc 相伴多年。在接下来的一个多月里新老域名都同时有效,已作 301 重定向,希望搜索引擎能尽快切换过来。届时 unmi.cc 将会失效,也许是一个月,也许是永远,又将为互联网创建不少死链接。

本站自 2010 年独立创建以来,一直用的就是 unmi.cc 这个域名,从 bloghost.cn 上购买的。由于后来域名都是在 GoDaddy 管理的,所以想着域名 unmi.cc 转到一块进行管理。同时也是为了防备着国内互联网公司不光在主机服务上设限制,也可能会在域名上作起文章来。

于是试图联系 bloghost.cn, 于是便是中国特色了,假若要从 bloghost.cn 买新服务,他们还算和气,但想从他们那儿转出域名,没门。看他们的网站还号称专业

别一不小心反而给他们做了个广告,其实是真不值得信赖的。他们网站上的联系方式只有 E-mail 和几个 IM 聊天号,没有电话 阅读全文 >>

Java 9 - 快速创建不可变集合

平台之所以谓之平台,以其能建立一个生态,并与之外围达成共赢。霸道点的平台也会反噬外围生态,像微软集成浏览器,媒体播放器。还有即将的 iOS 12 要把应用商店多是收费的 AR 皮尺放到它自己系统中来,走别人的路,让别人无路可走。从此众泰皮尺部的唯一的生产工具就会是人手一部能安装 iOS 12 iPhone 了。

JDK 也不例外,Java 8 之前日期库的话 Joda-Time 是首要之选,Java 8 集成后应该是鲜有人问津。以往说到集合操作库,有两个选择,其一为 Apache Commons Collections,二为 Google 的 Guava,当然前者与后者竞争中也早已败下阵来,况且前者还受到 Java 8 的夹击。而本文要说的可以说是 Java 9 把 Guava 中创建不可变集合的方式据为已用了,直截了当的说,凡是 Java 9 后有创建不可变集合的需求,只要用三大接口 ListSetMap 中的 of(...) 方法就对了。

Java 9 之前,当我们需要集合相关的操作,两个选择:

  1. Apache Commons Collections 的几个类 ListUtils, SetUtils, MapUtils, 和 CollectionsUtils。比如它们提供的以下几些个工具方法

    ListUtils.unmodifiableList<List<? extends E> list)   //创建不可变 List
    SetUtils.emptySet()  //不可变的空  Set
    SetUtils.unmodifiableSet(Set<? extends E> set)  //创建不可变 Set
    MapUtils.unmodifiableMap(Map<? extends K, ? extends V> map)  //创建不可变 Map
    CollectionUtils.unmodifiableCollection(Collection<? extends C> collection)  //创建不可变集合

  2. Guava 的几个类 ImmutableList, ImmutableSet, 和 ImmutableMap。而它们创建不可变集合的方式就是通过各自的 of(...) 方法,以 ImmutableList 为例(其余两个类也类似),它有

    of(): ImmutableList<E>
    of(E element): ImmutableList<E>
    of(E e1, E e2): ImmutableList<E>
    of(E e1, E e2, E e3): ImmutableList<E>
    ......
    of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11, E e12, E... others): ImmutableList<E>

阅读全文 >>

Linux 下配置滚动日志之 logrotate

日志是个好东西,便于定位历史问题,但记录太多,不滚动,不除旧总暴盘的时候。如果是用日志框架输出的日志,像 Log4j 或 Logback 通过选择具有滚动特性的 Appender 就能实现日志的滚动,并删除旧的归档日志文件。但也有在程序当中难以控制的日志输出文件,这用的话必须采取事后补救措施,程序尽管往一个日志文件里写,由另一个程序来对该日志文件进行归档,清理操作。

与此相关的工具,我们可以找到以下几个

  1. logrotate, 如今的多数 Linux 发布版都自带了,感觉有一种主场优势。github 上 logrotate/logrotate 仍活跃着
  2. newsyslog,  FreeBSD 和  Mac 系统自带,应该不常用。Mac OS 下可以看下配置文件 /etc/newsyslog.conf
  3. cronolog, 原本的官网 www.cronolog.org 全是日文了,找到它的快照 fleible web log rotation, github 上 fordmason/cronolog 最近更新是五年前
  4. rotatelogs, 出自于 Apache HTTP 项目, Apache HTTP server 用它滚动访问和错误日志

本人最为推崇使用第一个工具 logrotate, 因为多数 Linux 系统自带,不像 cronolog 和 rotatelogs 需要额外安装。它也有着更完备的功能,下面慢慢领略

logrotate 的工作机制

Linux 下默认有一个每日执行的 Cron Job,配置在 /etc/cron.daily/logrotate,文件内容为(以 centos7 为例) 阅读全文 >>

Java 9 线程栈遍历 API

什么是线程栈

继续纠缠 Java 9 的新特性,仍然是一个边角料,即 Java 9 增加了对线程栈遍历的 API。那么什么是线程栈,JVM 在创建每一个线程的同时都会创建一个私有的虚拟机栈,每一桢代表着一个方法调用,每次方法的调用与退出意味着压栈与出栈。每一桢上有局部变量,操作数常量引用等信息,这也是为什么局部变量是能最快被销毁的对象。过深的栈(比如过多的递归调用) 会出现我们程序员赖以生存的 StackOverflow。

浅显些说,线程栈就是通常我们捕获到异常后,用 e.printStackTrace() 看到自 main 方法追溯到当前方法的调用。例如:

java.lang.RuntimeException: stack
    at cc.unmi.TestStackWalking.m2(TestStackWalking.java:15)
    at cc.unmi.TestStackWalking.m1(TestStackWalking.java:10)
    at cc.unmi.TestStackWalking.main(TestStackWalking.java:6)

调用层次是 main() 调用 m1(), m1() 调用 m2(), m2() 中的代码如下

上面输出的每一行就是一个栈桢,输出了当前类名,方法名,代码行号。 阅读全文 >>

对 Java 9 把单个下划线作为关键字的猜想

我们知道 Java 的合法命名是以字母或下划线开头的字符串,当然,以前单个下划线 _ 也是一个合法的变量命名。但是自 Java 8 的第一个版本开始,单个下划线的变量名编译时会有警告

int _ = 99;

用 Java 8 编译时提示警告:

Test.java:2: warning: '_' used as an identifier
    int _ = 99;
         ^
    (use of '_' as an identifier might not be supported in releases after Java SE 8)
1 warning

这正是为 Java 9 作的预谋,以一个 LTS 版的 Java 8 作为过渡。因此,来到了 Java 9 后,单个下划线不再担当普通变量名的角色,变成了一个保留关键字,只说是另有任用。

先来看下 Java 9 中的单个下划线变量名编译时的错误提示

Test.java:2: error: as of release 9, '_' is a keyword, and may not be used as an identifier
    int _ = 99;
         ^
1 error

到底单下划线会作为什么用途呢? 已发布的 Java 10 没有给出答案,正在演进中的 Java 11 也没有相关的信息。 阅读全文 >>