Java 接口常量反模式及如何定义 Java 常量

初学 Java 的人很不经意间就会把常量定义在接口中,大概唯一的理由是接口不能实例化,而使用接口中定义的常量也是不用附着在实例上的。这主要还是 JDK 本身给我们做了很多这样的榜样, 如  java.io.ObjectStreamConstans,多是出现在 Enum 类型到来之前。

其实 Java 的接口常量是一种反模式,理由如下:

1. 接口是不能阻止被实现或继承的,也就是说子接口或实现中是能够覆盖掉常量的定义(重名),这样通过父,子接口(或实现) 去引用常量是可能不一致的
2. 同样的,由于被实现或继承,造成在继承树中可以用大量的接口, 类 或实例去引用 同一个常量,从而造成接口中定义的常量污染了命名空间。(Java 编译器竟然允许使用实例去引用类变量)
3. 接口暗含的意思是:它是需被实现的,代表着一种类型,它的公有成员是要被暴露的 API。而在接口中定义的常量说不上是 API

4. 这点有些重复,Java 允许通过子类去引用父类中定义的常量,各级对像实例去引用父类的常量,所以这会造成相当的混乱不堪。定义的常量不能保证单一的引用方式。

参见: Effective java 第 19 条: 接口只用于定义类型

既然接口中不适于定义常量,那么该在何处为常量安家呢?接口为 实现/继承 而生,如果放在类中,并且这个类是 final,且封闭掉构造方法就行。于是我们先前的接口常量定义 阅读全文 >>

fish 2.2.0 (July 12, 2015) 支持 vi 模式

随着 Mac 下终端的使用日益增多,系统默认的 bash 已经满足不了需求了,于是有了更为强劲的 fishzsh,以及它们各自的强心剂 Oh-My-FishOh-My-Zsh. 我的选择是 Fish 和 Oh-My-Fish。

到目前为止,最新的 fish 2.2.0 于 2015 年 7 月 12 日发布,Release notes 如下 http://fishshell.com/release_notes.html,其中显著改变有:

  • Abbreviations: the new abbr command allows for interactively-expanded abbreviations, allowing quick access to frequently-used commands (#731).
  • Vi mode: run fish_vi_mode to switch fish into the key bindings and prompt familiar to users of the Vi editor (#65).
  • New inline and interactive pager, which will be familiar to users of zsh (#291).
  • Underlying architectural changes: the fishd universal variable server has been removed as it was a source of many bugs and security problems. Notably, old fish sessions will not be able to communicate universal variable changes with new fish sessions. For best results, restart all running instances of fish.
  • The web-based configuration tool has been redesigned, featuring a prompt theme chooser and other improvements.
  • New German, Brazilian Portuguese, and Chinese translations.

我对第二点比较感兴趣,即增加了 vi 模式,在 fish 下运行 fish_vi_mode 命令,或者在 ~/.config/fish/config.fish 中加上 fish_vi_mode 便自动进入 vi 模式。 阅读全文 >>

通过反编译字节码来理解 Java 枚举

枚举的声明很简单, 像 enum Gender { Male, Female }, 其余事情就是 Java 编译器帮我们干的了,所以 enum 也就是一块语法糖。有了枚举确实是很方便,避免了传统常量的无范围性。那么编译器到底在后面做了什么呢?以及理解了这个之后我们可以怎么去使用 Java 的枚举, 下面就从这个例子说起:

public enum Gender {
    Male,
    Female
}

把上面的编译成 Gender.class, 然后用  javap -c Gender 反编译出来就是 阅读全文 >>

Linux 输入输出重定向, &>file, 2>&1, 1>&2 等

我们无论是在写批处理还是 Linux 的 Shell 都常用到 >, >> 或 <,这是输入输出重定向。特别是 Linux 的 Shell 常见到  2>&1 这样的写法,这是在干什么呢?这里就来了解下 Linux 下的输入输出重定向的一些来龙去脉。

在 Linux 下几乎一切都号称是文件,标准输入、输出也不例外,它们是叫做 fd (File Descriptor) 文件描述符。这里我们关注三个东西

名称 代码 操作符 Java中表示 Linux 下文件描述符(Debian 为例)
标准输入(stdin) 0 < 或 << System.in /dev/stdin -> /proc/self/fd/0 -> /dev/pts/0
标准输出(stdout) 1 >, >>, 1> 或 1>> System.out /dev/stdout -> /proc/self/fd/1 -> /dev/pts/0
标准错误输出(stderr) 2 2> 或 2>> System.err /dev/stderr -> /proc/self/fd/2 -> /dev/pts/0

从以上表格我们可以理解 0, 1 和 2 分别是什么东西了,它们的输入源或输出目的地默认都是屏幕。

下面不作系统解释输入, 输出重定向的完整使用,只说明一些常见的例子: 阅读全文 >>

Java 反射修改 final 属性值

使用过 Java 反射的大多都知道, 想要修改某个类或对象的私有变量的值的话, 在调用 set 设置新值之前执行一下 setAccessible(true) 即可。这样利用的 Java 的反射就能绕过 private 的限制 ,不再有 IllegalAccessException 异常了。这是一个 trick, 调用 Java 的私有方法也能这么做,有些人或许或这样来测试 Java 私有方法。

提前说一句:在修改 final 型值时,要特别留意它的常量值本身是否被编译器优化内联到某处,否则你会看到虽然没什么异常,但取出的还是原来的值。后面会稍为深入的讲到。

例如下面是一段完整的代码, 由于调用了 setAccessiable(true), 所以能成功把 OneCity 的私有属性 name 的值改为 "Shenzhen": 阅读全文 >>

Bash 的常用语法,控制结构

最近玩弄 Jenkins 较多,构建服务器基本是 Mac OS 和 Linux,虽说有许多现成插件可用,但不敢不说 Execute Shell 这个东西都是即开即用,方便而灵活的。因此不断的要和 Shell 打交道,真正通用的的 Shell 自然是 Bash,在 Mac OS 下可发现自带了 zsh, ksh 和 tcsh,考虑到 Linux 还是 Bash 吧。

本人工作时用的是 Fish Shell,目前相当的脚本语言都可用来写 Shell 脚本,如 PHP, Ruby, Python, Perl,NodeJs 等,只是 Bash 的地位总难被替代,借句话说 Bash 的是拿来 “用” 的,而像 Ruby, Python 等做 Shell 是拿来 “编” 的,再就是 Bash 与 Linux 命令的亲缘性更强。

我也只会在实在用 Bash 太难于表达的时候才用其他脚本语言,如处理日期的运算,因 Mac OS 的 date 和 Linux 的 date 命令差异较大,不得已会选择有较强类型的 Ruby 等。

既然 Bash 要作为一个日常语言,那不妨作个备忘录,记录下那些常用的语法,控制结构。

1. if 条件语句 阅读全文 >>

优秀程序设计的18大原则

好的编程原则跟好的系统设计原则和技术实施原则有着密切的联系。下面的这些编程原则在过去的这些年里让我成为了一名优秀的程序员,我相信,这些原则对任何一个开发人员来说,都能让他的编程能力大幅度的提高,能让他开发出可维护性更强、缺陷更少的程序。

1. 不要自我重复(DRY - Don't repeat yourself)

这也许是在编程开发这最最基本的一个信条,就是要告诉你不要出现重复的代码。我们很多的编程结构之所以存在,就是为了帮助我们消除重复(例如,循环语句,函数,类,等等)。一旦程序里开始有重复现象的出现(例如很长的表达式、一大堆的语句,但都是为了表达相同的概念),你就需要对代码进行一次新的提炼,抽象。

2. 提炼原则(Abstraction Principle)

跟“不要自我重复原则”相关,这一原则是说“程序中任何一段具有功能性的代码在源代码文件中应该唯一的存在。”

3. 保持简单(KISS - Keep it simple, stupid!)

简单化(避免复杂)永远都应该是你的头等目标。简单的程序让你写起来容易,产生的bug更少,更容易维护修改。

4. 不要开发你目前用不到的功能(Avoid Creating a YAGNI - You aren't going to need it)

除非你真正需要用到它,否则不要轻易加上那些乱七八糟用不到的功能。 阅读全文 >>

NodeJS 的 Web 服务也可以监听在 sock 文件

像 NodeJS 写的 TCP 服务可以监听在某个 sock 文件(Domain Socket) 上,它的 HTTP 服务也能这么干。虽然作为 HTTP 服务连接某个 sock 文件的意义不大,所以这里只算是一个纯粹的尝试。

TCP 服务是这样写

连接上面那个 '/tmp/node_tcp.sock'

✗ telnet /tmp/node_tcp.sock
Trying /tmp/node_tcp.sock...
Connected to (null).
Escape character is '^]'.
Hello World!
received: Hello World!

准确说来本文应该是 NodeJS 的 TCP 和 HTTP 监听 Domain Socket 文件。 阅读全文 >>

使用 NodeJS 框架 NW.js 编写桌面应用入门

前阵子还在琢磨于 Atom Shell 和 Node-Webkit 间如何作个选择。基于 Atom Shell 有点像是站在了 Node-Webkit 的肩膀上的原因,感觉 Atom Shell  会有些优势,所以首先体验了下 使用 NodeJS 框架 Atom Shell 编写桌面应用入门。至于它们实现上是否合并了 Context 对我目前来说还不清楚会有什么影响。

正当我要安下心来的时候,先前那个 Node-Webkit 最近几日来了个华丽变身,以更为简洁明快的名字 NW.js 横空出世,并且与 NodeJS 的另一分支 io.js 更热乎。新的名字似乎有意在含糊它与 Webkit 之间的关系。对于 NW.js 的这一激动人心之举, 难免心起涟漪,顿绝不能只是路过,而况它还是出自国人之手,自豪感总有的。

于是决心一试,略玩之后发现, 特别是开发阶段可显示 toolbar -- 浏览任意 html, 前进/后退/刷新,并能用 Developer Tools 调试 UI 和 JavaScript 的特性。这才令我有些爱不释手,尚不知 Atom Shell 是否有类似的功能。

开始随我体验 NW.js 的 Hello World 程序,这里包含了外部 js 和 css 文件,仍是以 Mac OS X 平台为例,其他平台类似。

1. 前提条件,安装 NW.js

$ npm install nw -g

nw 模块就安装在 /usr/local/lib/node_modules/nw 目录下了,以后可以直接用 nw 命令来测试程序。 阅读全文 >>

使用 NodeJS 框架 Atom Shell 编写桌面应用入门

NodeJS 使前后台语言上得到了统一,给 “不就是做网站的” 那些人带来了福音,其基于事件驱动的处理机制更是在并发/CPU 密集型计算上大展身手。

虽然我不怎么做 UI 程序,但对于每一种语言却爱关心它做 GUI 程序是否得心应手。像 Java, Groovy, Scala 等写桌面程序也不怎么样,JavaFX 似乎显现出苗头来,因为 JavaFX 也支持 CSS 了,但布局仍然老套。

HTML + CSS + JS 做页面布局等样式控制有种与身俱来的优越感,于是 NodeJS 的世界里便产生了  Node-Webkit(已更名为 NW.js) 和 Atom Shell,它们各自的代表作有 LightTable 和堪与 Sublime 相媲美的 Atom 编辑器。

Node-Webkit(NW.js) 有两个context,node context 和 web context, 应用入口是一个 html 文件。而在  Atom Shell 只维持一个context,入口是一个 JS 文件,浏览器的启动控制要亲自动手。

完了基本的介绍后,回到原点,来看个 Atom Shell 的 Hello World 程序例子。 阅读全文 >>