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

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

public enum Gender {
    Male,
    Female
}

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

Java 反射修改 final 属性值

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

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

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

Java NIO2(AIO) 进行文件异步读取

在  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 阅读全文 >>

三种方式告知泛型类或泛型方法具体类型

在上一篇中写道 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") 返回的就是字符串值 阅读全文 >>

Java 泛型 -- 依据声明的变量类型自动推断

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 阅读全文 >>

Scala 函数式编程学习视频 -- 条件和值定义

Scala 的 if 和 Java 中的用法差不多,但它在 Scala 是个表达式而不仅是条语句,它是有返回值,这让我们用起来很方便。在 Java 中我们一般先声明一个变量,在 if 语句中不同条件下赋不同的值。Scala 中就一条赋值表达式

val a = if (x == 5) 100 else if(x == 10) 200 else 300

val 关键字是用来定义值的,value 的缩写,就是 Java 中的常量 public static final

这里也同样涉及到了 call-by-name, call-by-value,到 Scala 控制台下试下

scala> val a = (1 to 1000000000).foldLeft(0){(a,b)=>a+b} //定义时就要算出 foldLeft 的值,所以需要耗费些时间
a: Int = -243309312

scala> def b = (1 to 1000000000).foldLeft(0){(a,b)=>a+b} //定义时很快,每次使用 b 时计算,其实就是定义一个方法,应该好理解
b: Int

阅读全文 >>

Scala 函数式编程学习视频 -- 求值策略和终止

本视频继续讲了 CBN(Call By Name) 和 CBV(Call By Value),它们就是一种求值策略。一般的 Scala 是采用 call-by-value,当函数的参数是使用的 => 来声明参数,就是 call-by-name, => 是 Lambda 用的符号,y 有点像是个函数参数。=> 就是告诉这个参数会采用 call-by-name。看个例子:

def constOne(x: Int, y: => Int) =1 //函数返回一个常数,x, y 分别是 CBV, CBN
constOne(23, (1 to 1000000000).foldLeft(0){(a,b)=>a+b}) //CBN, 把 foldLeft 表达式传给 constOne,希望在 constOne 中计算,所以很快
constOne((1 to 1000000000).foldLeft(0){(a,b)=>a+b}, 23) //CBV, 计算出 foldLeft 后值传给 constOne,所以很慢

上面的 constOne(x: Int, y: => Int) = 1 用 scalac 编译后再 javap -c 反编译看到的是:

public int constOne(int, scala.Function0<java.lang.Object>);
Code:
0: iconst_1
1: ireturn

阅读全文 >>

Java 解析 XML 时如何屏蔽掉 “[Fatal Error]” 的输出

我们在用 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 解析代码: 阅读全文 >>

Scala 函数式编程学习视频 -- 编程元素

进到 Scala 的 REPL (Read-Eval-Print-Loop) 有两种方式,scala 或 sbt console。函数式的任务就是求值(reduction steps),Scala 把表达式和方法进行了统一,Scala 的变量和函数也应作一致性思考。Substitution Model, no side effects, λ-calculus -> immutable。 Call-by-name vs call-by-value ,是调用者还是被调用者来对参数求值的问题。val 定义值时,会做 call-by-value 操作,def 则会做 call-by-name 操作,或者可以说是静态与动态求值。

Scala Call-By-Name vs Call-By-Value

要指定函数参数 y 以 Call-By-Name 传递,要这么定义 def test(x: Int, y: => Int) = x * x,这样就避免了 test(3, 2*4) 调用时 2*4 被求值,这里把 y 转换成了函数参数 y: => Int

阅读全文 >>