- 在 NodeJS 中进行异步操作很简单,而 Java 到了 7 开始才支持异步的 IO 操作。虽然之前的版本有引入非阻塞 IO,但编码中还不易体现出它的优越性。亮一下 NodeJS 用异步 IO 的例子:
var fs = require('fs');
fs.readFile('Test.scala', 'utf-8', function(err, data){
if( !err ) {
console.log(data);
}
});
console.log('continue doing other thins');
执行输出是
continue doing other things
CONTENT FROM FILE Test.scala
对的,理想中的异步操作就是,传递回调函数来读取文件,读取完成后招待回调,且不阻塞主线程。
在 Java 8 之前,因为没有 Lambda 支持只能应用内部类的方式。JDK 提供了以下异步 Channel 来实现异步操作
AsynchronousFileChannel Read More - 在上一篇中写道 Java 泛型 -- 依据声明的变量类型自动推断,主要是说明了通过声明类型告知泛型方法具体类型,其后有个小结三种方式告知泛型类或泛型方法具体类型,在此重列如下:
一. 具体类型写在两尖括号中
1. List<String> list
2. new HashMap<String, String>
3. instance.<String>foo() //如方法 <T> T foo() { return (T) obj; }
二. 变量声明类型指示具体类型
1. List<String> list = new ArrayList<>() // 这个例子应该可以列在这里,List<String> 指示了具体类型是 String, 所以后只需要空 <>,JDK7 or later
2. String s = obj.foo() //比如方法是 <T> T foo() { return (T) obj; }, 由于前面的 String s 声明,所以不必写成 String s = instance.<String>foo()
三. 实参类型指示具体类型
1. instance.foo(String.class) //方法为 <T> T foo(Class<T> type) { return (T) obj; }, instance.foo(String.class) 返回的就是字符串值
2. instance.foo("abc") //方法为 <T> foo(T value) { return (T) obj; }, instance.foo("abc") 返回的就是字符串值 Read More - Java 泛型在调用方法操作时应用具体参数还是很好理解的,比如
Map<String, Integer> map = new HashMap<>(); //钻石操作符是 JDK 1.7 引入的
后续的 put 操作调用的具体方法就是
Integer put(String key, Integer value)
因为 put 方法的原型是
V put(K key, V value)
上面的代码我们是看到了 <>,所以知道是泛型调用。有时候并不需要 <>,但实际上也是进行的泛型调用,那就是 Java 可以依据变量声明类型来作特化调用 -- 应用具体参数类型。
例如:
List<String> list = Collections.emptyList(); //虽不见 <>, 但同样是泛型调用,类型为 String Read More - 我们在用 Java 解析 XML,当文档不是一个合法的 XML 时,可能会收到
[Fatal Error]的控制台输出,即使把整个代码都 catch 住,仍然不能抑制住 [Fatal Error] 的信息输出。比如常见到这样的输出:[Fatal Error] :1:1: Content is not allowed in prolog.
为什么不能禁掉它呢,本来 catch 了异常对程序已经有了很好的保护,想眼不见心不烦,但还是避之不及。
因为,因为这个 XML 解析器用System.error.print()输出来了,当然你可以用 System.setErr(PrintStream) 重定向掉错误输出,但不现实,波及面太大。我们需要找到源头,首先交代解决方案就是覆盖掉默认的 ErrorHandler。
看下这段 XML 解析代码: Read More - 因为写过类似下面的一段代码来实始化一个匿名实例看上面的
1package cc.unmi; 2 3public class Test { 4 public String name; 5 6 public static Test buildTest(final String name) { 7 Test test = new Test(){{ 8 this.name = name; //希望把 buildTest() 方法参数中的 name 赋给 this.name 9 }}; 10 System.out.println(test.name); // 仍然是 null 11 return test; 12 } 13}buildTest()方法中的this.name = name希望能把方法参数 final String name 中的 name 值赋值给 this.name, 但是无效,this.name = name 是在把自己赋给自己。 Read More - 对于一个 Java 方法 foo(int id, String name); 我们如何能在代码中获得形式参数名 id 和 name 呢?
我们知道通过反射 APIMethod.getGenericParameterTypes()可以获得方法的参数类型,但是对于参数名一般就是 arg0, arg1, arg2 ..., 因为 Java 编译时把形式参数名擦除了。所以对完全擦除了形式参数名的字节码应该是没办法了,但我们自己写的类还是有能力去管控的。
对于自己写的类,有两种办法获得形式参数名,分别是
1) Java8 的 -parameters 编译参数,然后用 Java8 新引入的反射 API Parameter
我们先在 Java8 下运行下面的代码 Read More - Java 和 Scala 都支持变参方法, 写在最后的位置上,最基本的调用方式也都是一样的,一个个罗列过去。也可以传入数组参数,因为变参本质上就是一个数组,就是把 ... 开始位置到最后一个参数都收纳到数组中去,所以变参之所以要放在最后的位置上,且一个方法中最多只能有一个变参类型。
这里主要是对比 Scala 对变参方法的调用,基本调用法当然是没问题的,但是在传入数组作为变参的参数列表与 Java 相对时就稍有变化了。
另外提一下,如果想传入 List 作为变参列表,而不是整体作为变参的第一个元素就是调用集合的 toArray() 方法转换成一个数组传入。
下面看 Java 中对变参方法的调用,参数列表和数组1public class JavaVarArgs { 2 public static void main(String[] args) { 3 foo("a", "b", "c"); 4 foo(new String[]{"d", "e"}); 5 } 6 7 public static void foo(String...params) { 8 System.out.println(params + " : " + params.length); 9 for(String s: params) { 10 System.out.println(s); 11 } 12 } 13}
Read More - 伴随着 Play1, 我们原来使用的 JSON 库是 Gson. 回忆下 Gson 是怎么自定义序列化对象的 JSON 格式,大概是这样子的
GsonBuilder()..registerTypeHierarchyAdapter(Cat.class, new Cat());
然后 Cat 需要实现 JsonSerializer 的 serialize() 方法。
来到了 Play2 中,JSON 库变成了 Jackson,那么 Jackson 该如何为对象自定义 JSON 格式呢?
例如,默认时 Jackson 对 Map 类型输出的是一个 JSON 对象
Map("key1"->"value1", "key2"->"value2") 转换成 JSON 是 {"key1":"value1", "key2":"value2"}
当为适应某些客户端,对于 LinkedHashMap 类型,我们想要输出的是一个有序的 JSON 数组: [{"key1":"value1"},{"key2":"value2"}]
我们就应该自定义某些 Map 的序列化格式,实现方法有两种,addSerializer 和 @JsonSerialize,不管哪种方式都需事先具体化 JsonSerializer 类,并实现它的 serialize 抽象方法
所以我先来实现一个能序列化 Map 的 JsonArrayMapSerializer 类 Read More - 随着通用日志组件转入 Slf4j,logback 也变成了默认的日志实现,像 log4j 一样,logback.xml 中也可以使用系统属性或环境变量,如 ${catalina.home}。在 log4j.properties 中,如果变量在系统属性和环境变量中找不到的话默认为 "" 空字符串,而到了 logback.xml 中如果某个变量找不到默认就是 "变量名_IS_UNDEFINED" 了,这样就比较奇怪了。
那如何在没有配置 catalina.home 系统属性或环境变量时设置一个默认值呢,例如,没有 catalina.home 时取值为 ".",这时值日志文件的路径就是
./logs/unmi-%d{yyyy-MM-dd}.log
了,也就是生成中当前目录下的 logs 子目录中,这样算是很友好的方式。下面就来分析怎么一步步找到答案的,没耐心或是只求结果的话,直接滚屏到下面就行。
我们的问题是,对于下面那样的 logback.xml 配置: Read More - Java 的字符串值比较不能用 == 号这个设计不知道最初是怎么考虑的,它最大的贡献无疑是滋生了一个长久未衰的面试题,加之连 Code Review 都可能被忽略掉的 Bug。本来两个字符串用等号相比较是最自然而然的做法,然而它却是要迫使我们相信想当然很可能是错误的那样一个道理。
我一直认为 Java 的字符串比较值不能用 == 而必须用 equals() 方法是个不恰当的设计,这从其他种种语言的现实做法(人家都用 == 比较值)就知道。
猜想一下 Java 为何要这样对待字符串,可能 Java 又想类型全部对象化,同时考虑到方便性,仍然保留了 int, short, boolean 等原始类型,它们是可以用 == 比较值,其他真正的对象类型用 equals() 方法比较也是无可厚非的。这时候夹缝中的字符串却被为难到了,它那么的常用,还常以字面量的面目出现,它更该是个基本类型,而实为对象类型,因此不被认可用 == 直接比较值, 而选择了用 equals() 方法来比较字符串值。
而另一方面,由于字符串是多例的,所以有些情况下又更令人迷惑,比如下面的种种情况 Read More