你需要关注的 Java Enum 枚举的几个细节

枚举是一个非常古老的语言特性,用来实现具名的有限集合,在 C/C++ 中使用广泛。而 Java 在 Java SE5 才引入枚举。也许语言设计者觉得既然是后引入该特性,那么一定要在这个特性上支持比其他语言更多的特性。这些特性的确让 Java 的枚举功能看起来更加“成熟”,同时也引入了一些复杂性,需要开发者关注。

枚举是一个不能继承的常规类

定义一个一周七天的枚举类型:

编译成 class 文件后反编译查看:

从反编译结果可知:

  1. 枚举类型的关键字 enum 其实只是一个语法糖,编译器最终把它转化为一个final类,因此枚举是不可继承的。
  2. 枚举类型都继承自 java.lang.Enum 类。
  3. 枚举的每一个取值被编译器传化为了一个个 static final 属性。
  4. 本质上,这就是一个普通类,因此你可以在枚举是添加各种方法,甚至是main方法。

神奇的 values() 方法

从上面我们可以看出枚举类型被添加了一个静态的 values() 方法,但是 java.lang.Enum 并没有该方法。其实,这个方法是编译器添加的。通过这个方法可以获取到该枚举类型的所有取值。这个方法在需要遍历枚举取值,进行判断筛选的场景非常有用,可参考下例的 getByZhName 方法。

在枚举中保存其他信息

在 C 中,枚举可以简单的理解为具名的整型子集。Java 扩展了这个属性,使得可以在枚举中保存其他信息。

定义一个水果枚举类,并包含中文信息:

使用这种方式定义枚举的方式需要注意:该枚举必须含有一个构造函数,且该构造函数必须是私有的。因为枚举就是常规类,而枚举对象就是具体的枚举实例,因此枚举有多少个取值,该构造函数就会被调用多少次:

使用 EnumSet 和 EnumMap 提供性能

如果要在把枚举使用在 Set、Map 等集合场景,请使用 EnumSetEnumMap。 EnumSet 使用了 bit vector 来标记元素,EnumMap 内部将 Map 实现简化为了数组,因此可以获得更好的性能。

小结

Java 的枚举语言特性作为一个后来者,的确带来了更加“成熟”和“丰富”的实现。但是,这些丰富的特性是否一定要在日常的项目中使用,我个人是不推荐的。就我个人理解,枚举最大的优点是类型和有限集合的约束,从而增强代码的一致性。因此,我提倡在项目代码中用 C 的枚举风格来使用 Java 枚举。此外,枚举并不是编程语言必须支持的特性,比如近段时间如日中天的 Golang 是不支持枚举的。既然是一个可有可无的语言特性,那就 use is as simple as possible 吧。

扩展阅读

Java 语言中 Enum 类型的使用介绍
Java中的枚举与values()方法

HTTP/3 已经箭在弦上,你准备好了吗?

去年的这个时候,国内的 web 网络环境开始普及和部署 HTTP/2. 时隔一年,HTTP/2 的普及程度有了显著提升,而各大CDN厂商普及的广度和速度一直走在行业前列。甚至有不少CDN厂商在直播以及部分HTTP场景还引入了 QUIC.

在拙文《当我们在谈论HTTP队头阻塞时,我们在谈论什么?》中,我们提到,HTTP/2 over QUIC 是当前唯一应用落地解决了传输层队头阻塞问题的HTTP实现。那个时候,无论是 HTTP/2 over TCP 还是 HTTP/2 over QUIC(UDP) 都被我们认为是 HTTP/2,只是传输层使用的协议不一样。这种略带暧昧的模糊叫法在2018年11月成为了历史:

在2018年10月28日的邮件列表讨论中,互联网工程任务组(IETF) HTTP和QUIC工作组主席Mark Nottingham提出了将HTTP-over-QUIC更名为HTTP/3的正式请求,以“明确地将其标识为HTTP语义的另一个绑定……使人们理解它与QUIC的不同”,并在最终确定并发布草案后,将QUIC工作组继承到HTTP工作组。在随后的几天讨论中,Mark Nottingham的提议得到了IETF成员的接受,他们在2018年11月给出了官方批准,认可HTTP-over-QUIC成为HTTP/3。

虽然看起来像是之前的 HTTP/2 over QUIC 换了一个名称(从我个人角度理解,取名为 HTTP/2.1也许更合适),但是其背后却体现了 IETF 对 HTTP 未来标准的态度和方向,也许几年以后来看这次名称的确立会更加明白其重要意义。

HTTP/3 与 HTTP/2 over QUIC 的区别

QUIC 将成为一个通用安全传输层协议

当前阶段,Google 实现的 QUIC 与 IETF 实现的 QUIC 是不兼容的。Google 版 QUIC 只能用于 HTTP/2,且在协议层面与 HTTP/2 有一些强绑定。如 QUIC 帧映射 HTTP/2 frame. 这就导致很多大厂都没有跟进 QUIC,使得 HTTP/2 over QUIC 基本只能在 Google 自家的 Chrome, Gmail 等软件中普及使用,一度给行业造成“只有Google在弄”的错觉。

纳入 IETF 以后,显然 Google 就不能这么玩了。QUIC 定位为一个通用安全传输层协议:

可以近似的认为 QUIC over UDP 将成为下一代(或替代)TLS over TCP. 也就是说, QUIC 将能应用于任何应用层协议中,只是当前阶段将优先在 HTTP 中进行应用和验证。

统一使用 TLS 1.3 作为安全协议

2018年,有几个重要的WEB标准终于尘埃落定,其中一个便是 RFC 8446 TLS 1.3. 这个标准对于降低延迟,改善用户体验,尤其是移动端的体验有非常重要的意义。在拙文《TLS1.3/QUIC 是怎样做到 0-RTT 的》中,我们提到 虽然 TLS 1.3和 QUIC 都能做到 0-RTT,从而降低延迟,但是 QUIC 却自顾自地实现了一套安全协议。主要是因为当时 TLS 1.3 标准还没有发布,而 QUIC 又需要一套安全协议:

The QUIC crypto protocol is the part of QUIC that provides transport security to a connection. The QUIC crypto protocol is destined to die. It will be replaced by TLS 1.3 in the future, but QUIC needed a crypto protocol before TLS 1.3 was even started.

如今,TLS 1.3 标准已经发布,而 HTTP/3 也纳入 IETF,因此 QUIC 也就顺理成章的使用 TLS 1.3 作为其安全协议。Google 在这些方面倒是从来都不鸡贼和墨迹,点赞。

使用 QHPACK 头部压缩代替 HPACK

其实,QPACK与HPACK的设计非常类似,单独提出QPACK主要是更好的适配QUIC,同时也是 Google 将 QUIC 从与 HTTP/2 的耦合中抽离出来,与 IETF 标准完成统一的必要一步。

HTTP/3 问题与挑战

UDP 连通性问题

几乎所有的电信运营商都会“歧视” UDP 数据包,原因也很容易理解,毕竟历史上几次臭名昭著的 DDoS 攻击都是基于 UDP 的。国内某城宽带在某些区域更是直接禁止了非53端口的UDP数据包,而其他运营商及IDC即使没有封禁UDP,也是对UDP进行严格限流的。这点上不太乐观,但是我们相信随着标准的普及和推广落地,运营商会逐步改变对UDP流量的歧视策略。国外的情况会稍好一些,根据Google的数据,他们部署的QUIC降级的比例不到10%。

QUIC 不支持明文传输

对于用户来说,这是一个优势,并不是问题。对于国内内容审查环境来说是个不可忽视的坎。但QUIC以后毕竟也是基于TLS协议的,国内HTTPS都能普及下来,QUIC的普及也许会更乐观一些。

UDP 消耗资源多

当前阶段,UDP消耗的CPU资源多,且处理速度慢。这是不争的事实,但是我相信随着UDP应用的增多,内核和硬件的优化一定会跟上,直至达到或超过TCP的性能。而 QUIC 因为实在应用层实现,因此迭代速度更快,部署和更新难度和代价更小,能够一定程度缓解如TCP那样的协议僵化问题。

进一步了解 HTTP/3

如果希望全面的了解 HTTP/3,推荐 Daniel Stenberg (CURL 作者)的 HTTP/3 explained, 如果不想看英文,可以翻阅 Yi Bai 同学翻译了中文版本HTTP/3详解

2018 年终总结

2018 年终总结

年末被各种事情推着走,总结拖到现在才写,也算是保持了过去一年平均时间管理水平?。

过去的一年,发生和经历的事情挺多,也逐渐到了很多事情不太适合写出来尴尬年龄。不过,一直在路上,缺少盘点的话,总觉得对自己缺乏那么一点交代。

过去的一年,重新捡回来写作的习惯,同时也开始鼓动身边的人开始写作。毕竟这是当前仅存的几项只有好处,但咸有坏处的活动。过去的一年,每月至少有一篇输出,同时靠写作赚了几杯咖啡钱,也因为写作,连接到了一些素未谋面的有趣灵魂们。其中,收到过一位自己喜欢的“大佬”的打赏。这是一些好的方面。换工作以后,写作的时间和频率被动减少了很多,借口就不提了。但好不容易养成的习惯,是无论如何不能被摧毁的,新的一年希望自己有更多与自己交谈的思考和沉淀。

过去的一年,离开了自己舒适区,换了份工作。其实非常舍不得前东家,我知道作为在职场混迹多年的人,依然说这种话是非常不成熟和忌讳的。但是,离开真的并不是因为它有什么不好,只是因为未来两年,我有自己想成长的方向和天空。开弓没有回头箭,下山的弟子要去爬属于自己的那座山,没来得及好好道别的兄弟们,我们江湖再见。来到新的工作岗位心态非常平和,这也是自己一直以来希望自己面对任何事情的基本心态——在智力、心里、脑力、体力之外,一定不能扭曲的就是心态。新的位置压力不大肯定是骗人的,但是希望自己能够保持好这份心态。要不了几年,我依然会从这个山头下山,人说雁过留痕,我希望自己能对得起自己当年的那次下山,当然还有自己的年轮。

过去一年,没有什么像样的出游,如果一定要记录一下的话,那就是在青城山下悠闲的几日,以及带家人去了一趟乐山。这种出游距离和时间,对于成都人来说,也就是一个周末亲自游的水平。但是青城山下,因为放下了所有事情,逛着经不起历史推敲的道观,也算是难得的放空。乐山之行算是特意为家里的四个女人安排的:婆婆,老妈,妞儿还有女儿。乐山我跟老爸都去过很多次了,但是对于前面四位在某些层面来说都是新的:婆婆、女儿从来没有去过乐山;老妈去过乐山,但是没看过大佛;妞儿去过大佛,但是因为四年前自己安排不当,并没有尝到乐山的美食。一天的逛吃下来,在力所能及范围内,用尽了一切便利条件,大小老少都累得够呛。晚饭时候,婆婆说”今天最幸福“,我知道他们那一辈人是很难表达这些感情的,能说出这句话,我真的觉得自己歪打正着的这次安排真的太幸运了。

过去的一年,纸质阅读时间和质量都下降严重。有时候,我甚至已经开始怀疑自己被算法推荐的狗粮给喂成了?。这是过去一年时间管理出问题的一个侧面,每天警醒自己,但是每天依然岿然不动,也算是极致的讽刺了。新的一年,需要改变。

过去的一年,莫名其妙的看了很多场电影。最推荐的是《我不是药神》,影评层面的就不提了,仅仅是因为这是这几年陪老妈看的第一部电影。希望以后能陪父母看更多的电影。

过去的一年,经济很冷。自己在投资层面稍有盈余,看看身边各种腰斩的兄dei,至少不算太差。被动收入方面,完成了几个流程性的事情,虽然远比自己预想的要慢,但是这个事情本来也就是在认定的方向上去祈求一个时间的礼物,急不得太多,但须一直坚持下去。希望来年,能有一个姑且能称为产品的达到自己设定的目标吧。

过去的一年,依然没有学会如何哄女儿。跟别人说起来头头是道的 positive 式引导教育,自己实践起来屁用没有。我现在已经接受女儿当前 double two 阶段的这种超乎常人的顽皮,也许她在 third two 的时候,这种顽皮会有所收敛?天知道呢。

过去的一年,送给了妞儿一个大件礼物。其实,她至今没有正式的跟我说声谢谢,但是我能感受到事物本身对她认知的影响。有些事情,不仅是责任,更是义务,其他的并不重要。

过去的一年,没有买到自己心仪已久的大房子,挺遗憾的。希望来年准备好更多子弹,给家人一个更好的居所,也给自己一个更舒适的奋斗窝。

2018年初,我知道会发生很多事情,但是真正做一些事情让预想发生的时候,自己早已没有当时的兴奋,反而会带来一些自己没有考虑到的问题。你说这些重要吗?肯定重要,因为它会影响你的轨迹;但是也没那么重要,毕竟我过去做过如此多最坏的选择,其实也没有太糟糕。新的起点,我想在自己认真的事情上保持认真和专注,在自己没有那么认真的事情上,想清楚,要么认真起来,要么随他而去。2019,心之所向,不过如此。