SAM type和Lambda

2016 Nov 23

这是一份Java 8 Lambda的早期设计文档。 文档里Lambda的语法和最终的实现有一些细微的区别,但是不影响阅读。 最终的Lambda设计文档见这里

Lambda也叫“闭包(closure)”。 在Java 8之前,anonymous inner class可以认为是一种闭包——它可以capture enclosing class里定义的变量,封装一段或者几段代码以便稍后执行。 anonymous inner classes的缺点是语法太啰嗦、this不是lexically scoped、不能capture非final变量等。 Java 8引入Lambda是为了解决这些问题。

Java 8在引入Lambda时,并没有引入新的function type;而是借助SAM type来实现Lambda。 SAM(single abstract method) type是指只有一个抽象方法的接口。

Lambda expressions can only appear in context where it will be converted to a variable of SAM type.

Lambda表达式都会转化为一个SAM类型的变量。 凡是能接收SAM类型的地方,比如方法的参数,都可以接收一个Lambda表达式。

Comparator<String> c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);

Java 8的Lambda表达式和anonymous inner class的一个不同之处是它们对this的解读。 Lambda表达式中的this指的是enclosing class中的this。 anonymous inner class中的this指的是inner class自己的this

Kotlin,又一种JVM语言

2016 Nov 23

Kotlin是JetBrains开发的的一种JVM语言。

  • 比Java简洁
  • 没有烦人的NullPointerExceptions
  • 多用途,for the JVM, Android and the browser
  • 可以和Java代码混着写,100% Java Interoperability

相比Java,没有raw types,没有checked exceptions,类型系统保证的null safety,……。详细对比。 Java和Kotlin的语法比较

Kotlin很像Swift,或者说Swift很像Kotlin。

Weak References in Java

2016 Nov 12

一篇非常好的关于Java的“弱引用”的文章。

WeakReference和SoftReference

Java里的引用有四种级别:strong、soft、weak、phantom。Strong reference最常见。 但是strong reference并不适用所有的场景。比如,用strong reference来实现cache时,要么自己动手 决定什么时候应该evict缓存的对象(这在某种意义上是重复了JVM的工作——进行垃圾回收),要么承担内存 泄漏的风险(缓存的对象做为strong references一直被cache自己引用着,使得这些对象一直不能被回收)。

这种情形下,可以考虑使用较弱的引用,比如WeakReference或者SoftReference, 来实现缓存(把什么时候应该evict缓存对象的决定权交给JVM)。

A weak reference, simply put, is a reference that isn’t strong enough to force an object to remain in memory.

SoftReference在使用上和WeakReference类似,不过SoftReference指向的对象相比于WeakReference要存活得更久一些。 一般来说,只要可用内存充足,soft reference指向的对象就不会被回收。所以在实现cache时,可能使用SoftReference更好一些, 使用WeakReference会使得缓存失效的概率大大增加。

An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next garbage collection cycle, but an object which is softly reachable will generally stick around for a while.

PhantomReference、ReferenceQueue和Finalization

a fundamental problem with finalization: finalize() methods can “resurrect” objects by creating new strong references to them

Well, the problem is that an object which overrides finalize() must now be determined to be garbage in at least two separate garbage collection cycles in order to be collected.

Multi-Tenancy in JPA

2016 Nov 8

EclipseLink支持多种多租户(multi-tenancy)实现。 主要通过@Multitenant等相关的annotation来提供对multi-tenancy的支持。

public @interface Multitenant {
    MultitenantType value() default MultitenantType.SINGLE_TABLE;
    boolean includeCriteria() default true;
}

@Multitenant支持SINGLE_TABLETABLE_PER_TENANT等方式(还有VPD)。 SINGLE_TABLE表示多个租户共用一张表;一般这种方式单租户的成本比较低。 TABLE_PER_TENANT表示不同租户的表是隔离的。隔离的方式有不同的租户的表有不同的前缀/后缀或者放在不同的schema里。

配置好相关的annotation后,查询、更新或者删除时EclipseLink会自动帮你在query里拼接上tenant信息。 在某些情况下,自动拼出来的query也会有问题。比如EclipseLink 2.6.2在multi-tenancy下的left join就有问题。 EclipseLink会在left join的where子句里拼接上tenant相关的条件,

SELECT t0.ID, t1.ID FROM CURRENCY t0 LEFT OUTER JOIN EXCHANGERATE t1 
  ON (((t1.currencyId = t0.ID) AND (t1.RATEDATE >= ?)) AND (t1.RATEDATE <= ?)) 
  WHERE (((t0.STATUS = ?) AND (t0.TENANT_ID = ?)) AND (t1.TENANT_ID = ?))

(t1.TENANT_ID = ?)会使得left join“退化”成inner join。

对于这种问题可以用native query来避免EclipseLink对query的修改;

Optional in Java 8

2016 Nov 6

这是Java SE 8 for the Really Impatient的一篇笔记。

一个Optional对象要么有内容(contain a non-null value),要么其内容为空。 Optional如果使用不当并不能体现它的优势。比如,

// 下面两段代码都可能抛异常
Optional<T> optionalValue = ...; 
optionalValue.get().someMethod(); 

T value = ...; 
value.someMethod();

// 下面两段代码都有繁琐的if判断
if (optionalValue.isPresent()) 
  optionalValue.get().someMethod();
  
if (value != null) 
  value.someMethod();

Optional定义了一些方法,这些方法可以在一个Optional对象有内容时才执行某段代码, 或者没有内容时提供替代性的值。 使用这些方法,才能让Optional发挥优势,使代码远离NullPointerException或者繁琐的null值判断。

// 如果Optional对象里有非null值,则执行某个操作;否则什么也不做
optionalValue.ifPresent(v -> results.add(v));
// 如果Optional对象里有非null值,那么把这个值赋给result;否者把"foo"赋给result
String result = optionalString.orElse("foo");
String result = optionalString.orElseGet(() -> SOME_MAP.get("key"));
String result = optionalString.orElseThrow(NoSuchElementException::new);

Optional对象可以通过下面的方法创建,

Java代码性能调优

2016 Nov 2

关于Java性能调优的一篇文章,其内容是java-performance.info上所有文章的归纳总结。 主要侧重于Java代码本身的调优,不涉及JVM参数。 推荐一看。

避开Java基准测试中的陷阱

2016 Nov 2

基准测试看似简单,但是由于JVM会在运行时做优化,可能使得基准测试的结果失去意义。

比如JVM如果“看到”接口只有一种实现时,会对接口方法的调用做优化。 如果在后续运行中“看到”很多的接口实现时,则会抛弃掉之前的优化。 这会导致对第二个接口实现的benchmark结果变得不准确。 其它JVM优化手段还有dead-code elimination、constant folding等等。

所谓基准测试,意味着有baseline,有对比,JVM的优化会导致对比的某一方得到了“不公平的”优待,使得对比的结果失去了意义。

文章推荐使用jmh来做基准测试。

it was developed as part of the OpenJDK project

JMH is popular for writing microbenchmarks, that is, benchmarks that stress a very specific piece of code. …

博客推荐:Aleksey Shipilëv

2016 Nov 2

Aleksey Shipilëv的博客。

Aleksey is working on Java performance for 10+ years. Today he is employed by Red Hat, where he does OpenJDK development and performance work.

博客推荐:Java Annotated Monthly

2016 Nov 2

来自IntelliJ IDEA团队的Java社区每月动态汇编。💯

为什么推荐用char[]而不是String来存储密码

2016 Nov 1

为什么Java Swing用char[]来存储密码,而不是String

因为String是不可变的(immutable)。 这意味着在String对象被垃圾回收前,密码会一直存在于内存中。 (虽然可以通过反射来修改String对象的field的访问权限,进而修改String对象。) 使用char[]来存储密码的好处是使用后可以马上抹掉char[]的内容。

安全总是相对的。一种可能性(取决于JVM实现)是在抹掉char[]之前发生了GC,移动了char[]对象 (比如从Eden移动到Survivor),使得老的char[]还驻留在内存里。

还有一点要注意的是不要在log里(无意地)打印敏感信息,比如密码等。