6. 随机整数
  这更像是一个脑筋急转弯。先不要着急看答案。看你能不能自己搞定。当运行如下这段程序时:
  for (int i = 0; i < 10; i++) {
  System.out.println((Integer) i);
  }
  “偶尔”它会输出这样的结果:
  92
  221
  45
  48
  236
  183
  39
  193
  33
  84
  这特么可能么?
  . . . . . . . . .
  要剧透了,答案要来了。。
  . . . . . . . .
  好吧,答案在这里,你得通过反射重写掉JDK的Integer里面的缓存,然后再使用自动装箱和拆箱的功能。不要真的这么做!换句话说,我们再强调一次:
  亲爱的同事,提交完这段代码,我下月休假了哦(你看不懂可别怪我):
  image
  7. GOTO
  这是我津津乐道的。Java其实也有GOTO!敲下代码看看
  int goto = 1;
  它的输出是
  Test.java:44: error: <identifier> expected
  int goto = 1;
  这是因为goto是一个预留的关键字,万一以后有用呢。。。
  不过这还不是重点。有意思的是你可以通过break,continue以及标签块来实现goto的功能:
  跳转到前面:
  label: {
  // do stuff
  if (check) break label;
  // do more stuff
  }
  它的字节码是:
  2  iload_1 [check]
  3  ifeq 6          // Jumping forward
  6  ..
  跳转到后面:
  label: do {
  // do stuff
  if (check) continue label;
  // do more stuff
  break label;
  } while(true);
  它的字节码是:
  2  iload_1 [check]
  3  ifeq 9
  6  goto 2          // Jumping backward
  9  ..
  8.Java也有类型别名
  在其它语言中(比如说Ceylon,定义类型别名非常简单:
  interface People => Set<Person>;
  这样所定义出来的People类型可以和Set交替使用:
  People?      p1 = null;
  Set<Person>? p2 = p1;
  People?      p3 = p2;
  Java中无法在外层实现类型别名。不过我们可以在类或者方法的作用域内来实现这点。假设我们现在觉得Integer, Long的名字看着不爽了,希望能更简短些:I以及L。这很简单:
  class Test<I extends Integer> {
  <L extends Long> void x(I i, L l) {
  System.out.println(
  i.intValue() + ", " +
  l.longValue()
  );
  }
  }
  上面这段程序中,在Test类的作用域内,Integer的别名是I,而在x方法内,Long的是L。我们可以这样来调用上面的方法:
  new Test().x(1, 2L);
  这当然不是什么正式的别名的方式。在本例中,Integer和Long都是final类型,也是说类型I和L是真正意义上的别名(算是吧。因为它只有一种赋值相容(assignment-compatibility)的方式)。如果你使用的是非final类型时(比如说Object),那你用的其实是泛型而已了。
  这种雕虫小技先说到这吧。现在我们来讲点有意义的!
  9. 某些类型关系是无法确定的
  好吧,这个有点费脑了,先喝杯咖啡集中下精神吧。假设有下面两种类型:
  // A helper type. You could also just use List
  interface Type<T> {}
  class C implements Type<Type<? super C>> {}
  class D<P> implements Type<Type<? super D<D<P>>>> {}
  那么,C类型和D类型到底是什么?
  这看起来是有点像递归了,java.lang.Enum看起来也是类似的递归(尽管略有不同)。看下它的定义:
  public abstract class Enum<E extends Enum<E>> { ... }
  知道了上面这个规范之后,我们明白了,枚举的实现其实不过是个语法糖罢了:
  // This
  enum MyEnum {}
  // Is really just sugar for this
  class MyEnum extends Enum<MyEnum> { ... }
  记住这点,我们再回来看一下这两个类型。下面这段代码能通过编译吗?
  class Test {
  Type<? super C> c = new C();
  Type<? super D<Byte>> d = new D<Byte>();
  }
  这个问题有点难,不过Ross Tate曾经回答过它。这个问题其实是无解的:
  C是Type<? super C>的子类吗?
  Step 0) C Step 1) Type>
  还有
  D是Type<? super D>的子类型吗?
  Step 0) D > Step 1) Type>>> > Step 2) D >> Step 3) List>> > Step 4) D> >> Step . . . (expand forever)
  试试在你的Eclispe里编译一下,这会让它崩溃的!(别担心,这个BUG我已经提交了)
  要想彻底搞清楚这点。。。
  Java中的某些类型是不确定的
  如果你对Java的这个罕见的奇怪行为感兴趣,可以读下Ross Tate的这篇论文“如何驾驭Java类型系统中的通配符”(与Alan Leung及Sorin Lerner共同发表),或者看下我们的几点愚见“论子类型多态与泛型多态的关联关系”。
  10. 类型交集
  Java有一个非常古怪的特性叫做类型交集(虽然乍看上去有点像并集)。你可以声明一个泛型,它其实是两个类型的交集。比如说:
  class Test<T extends Serializable & Cloneable> {
  }
  绑定到Test类中的这个泛型参数T必须同时实现Serializable接口以及Cloneable接口。比方说,String没有同时实现这两个接口,但Date是:
  // Doesn't compile
  Test<String> s = null;
  // Compiles
  Test<Date> d = null;
  Java 8中也用到了这一特性,你可以将某个类型转换成两个类型的交集。这有什么用?好像是没啥用,但是如果你要把一个lambda表达式强转成这样一个类型的话,除此之外别无它法了。假设你的方法里面有这么一个看似疯狂的约束条件:
  <T extends Runnable & Serializable> void execute(T t) {}
  你希望Runnable对象同时还实现了Serializable接口,这样你既可以去执行它,也可以将它发送出去。Lambda表达式和序列化?这看上去有点基情。
  Lambda表达式其实是可以进行序列化的:
  如果Lambda表达式的目标类型和参数都是可序列化的,那么它也是可序列化的。
  尽管这是可以实现的,但它并没有直接实现Serializable这个接口。为了能适配成这个类型,你必须得进行类型转换。但是如果你只转成Serializable了的话:
  execute((Serializable) (() -> {}));
  那么这个Lambda表达式不是一个Runnable类型了。
  那么,
  只能转成两个类型了:
  execute((Runnable & Serializable) (() -> {}));