Java 7 代号 Dolphin 是一个重大的更新,于 2011 年 7 月 7 日亮相,并在 2011 年7 月 28 日开放给开发者使用。开发周期被分类成十三个重要阶段,最后一个阶段在 2011 年 6 月 6 日完成。平均来看,每个里程碑各有 8 个版本(主要包括功能增强和漏洞修复)。
String in switch Statement 在其它语言中早已支持的特性,在 Java 7 姗姗来迟,无论怎么样,在 switch-case 中使用 String 让代码简洁了许多。这一特性其实是通过 String 的 hashCode() 实现 switch-case,如下面这段代码:
1 2 3 4 5 6 7 8 9 10 11 12 public static int getDayOfWeek (String dayOfWeek) { switch (dayOfWeek) { case "Monday" : return 1 ; case "Tuesday" : return 2 ; case "Wednesday" : return 3 ; case "Thursday" : return 4 ; case "Friday" : return 5 ; case "Saturday" : return 6 ; case "Sunday" : return 0 ; default : throw new IllegalArgumentException ("Invalid day of the week: " + dayOfWeekArg); } }
生成的字节码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 public static int getDayOfWeek(java.lang.String); descriptor: (Ljava/lang/String;)I flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=3, args_size=1 0: aload_0 1: astore_1 2: iconst_m1 3: istore_2 4: aload_1 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: lookupswitch { // 7 -2049557543: 146 -1984635600: 76 -1807319568: 160 -897468618: 104 687309357: 90 1636699642: 118 2112549247: 132 default: 172 } 76: aload_1 77: ldc #3 // String Monday 79: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 82: ifeq 172 85: iconst_0 86: istore_2 87: goto 172 90: aload_1 91: ldc #5 // String Tuesday 93: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 96: ifeq 172 99: iconst_1 100: istore_2 101: goto 172 104: aload_1 105: ldc #6 // String Wednesday 107: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 110: ifeq 172 113: iconst_2 114: istore_2 115: goto 172 118: aload_1 119: ldc #7 // String Thursday 121: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 124: ifeq 172 127: iconst_3 128: istore_2 129: goto 172 132: aload_1 133: ldc #8 // String Friday 135: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 138: ifeq 172 141: iconst_4 142: istore_2 143: goto 172 146: aload_1 147: ldc #9 // String Saturday 149: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 152: ifeq 172 155: iconst_5 156: istore_2 157: goto 172 160: aload_1 161: ldc #10 // String Sunday 163: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 166: ifeq 172 169: bipush 6 171: istore_2 172: iload_2 173: tableswitch { // 0 to 6 0: 216 1: 218 2: 220 3: 222 4: 224 5: 226 6: 229 default: 231 } 216: iconst_1 217: ireturn 218: iconst_2 219: ireturn 220: iconst_3 221: ireturn 222: iconst_4 223: ireturn 224: iconst_5 225: ireturn 226: bipush 6 228: ireturn 229: iconst_0 230: ireturn 231: new #11 // class java/lang/IllegalArgumentException 234: dup 235: new #12 // class java/lang/StringBuilder 238: dup 239: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V 242: ldc #14 // String Invalid day of the week: 244: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 247: aload_0 248: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 251: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 254: invokespecial #17 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V 257: athrow
不难看出实现的原理其实是:lookuptable
+ String.equals(Object)
+ tableswitch
Type Inference for Generic Instance Creation 在 Java 7 之前,范型类的实例化时,都必须要带上类型参数,否则编译器会报 unchecked conversion warning,如在 Java 7 之前:
1 ArrayList<String> list = new ArrayList <String>();
从 Java 7 开始,范型的类型参数要以省略了:
1 ArrayList<String> list = new ArrayList <>();
在 Java 7 中,为 <>
这个符号取了一个好听的名字 – Diamond
Multiple Exception Handling Java 7 引入另一个简化语法的功能便是多异常处理,在 Java 7 之前,try-catch
块中,一个 catch
块一次只能捕获一个异常,像 java.lang.reflect.**
包下的 API,会抛出一系列异常,有了这一特性,一个 catch
块就能处理所有的异常了,例如:
1 2 3 4 5 6 7 try { Class<?> clazz = Class.forName("com.example.Main" ); Method method = clazz.getMethod("main" , String[].class); method.invoke(clazz, new String [] {}); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); }
除了能处理多个异常外,Java 7 对于异常重抛也做了更多包容性类型检查,例如,在 Java 7 之前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static class FirstException extends Exception { }static class SecondException extends Exception { }public void rethrowException (String exceptionName) throws Exception { try { if (exceptionName.equals("First" )) { throw new FirstException (); } else { throw new SecondException (); } } catch (Exception e) { throw e; } }
然而,在 Java 7 中,可以通过指定抛出的异常类型为 FirstException
和 SecondException
,即使 catch
块中是 Exception
类型,编译器也能够判断出是抛 FirstException
还是 SecondException
,例如:
1 2 3 4 5 6 7 public void rethrowException (String exceptionName) throws FirstException, SecondException { try { } catch (Exception e) { throw e; } }
Support for Dynamic Language 众所周知,Java 语言是静态类型语言,一旦程序被编译完成,程序中的一切类型信息都将确定下来,静态类型对于程序的运行速度来说,优势是显而易见的,但是,在 Java 7 之前想要支持其它动态语言在 JVM 之上高效的运行却很困难,像 JavaScript ,Ruby 等。因此,Java 7 引入了 invokedynamic
指令,将 invokedynamic
的调用站点通过一个 bootstrap
方法来链接到另一个方法,如下面的例子所示:
1 2 3 def addtwo (a, b ) a + b; end
+
即为 invokedynamic
的调用站点,当编译器发出 invokedynamic
指令调用 +
时,运行时系统知道有 adder(Integer, Integer)
这个方法,便链接 invokedynamic
的调用站点到 adder
方法上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class IntegerOps { public static Integer adder (Integer x, Integer y) { return x + y; } } class Example { public static CallSite mybsm (MethodHandles.Lookup callerClass, String dynMethodName, MethodType dynMethodType) throws Throwable { MethodHandle mh = callerClass.findStatic(Example.class, "IntegerOps.adder" , MethodType.methodType(Integer.class, Integer.class, Integer.class)); if (!dynMethodType.equals(mh.type())) { mh = mh.asType(dynMethodType); } return new ConstantCallSite (mh); } }
在上面的例子中,IntegerOps
属于动态语言运行系统时附带的库。
Try with Resources try-with-resources
是一个非常实用的特性,特别是 I/O 操作,如在 Java 7 之前,需要主动关闭流:
1 2 3 4 5 6 7 8 9 10 static String readFirstLineFromFileWithFinallyBlock (String path) throws IOException { BufferedReader br = new BufferedReader (new FileReader (path)); try { return br.readLine(); } finally { if (br != null ) { br.close(); } } }
而在 Java 7 中,可以变得更简洁:
1 2 3 4 5 static String readFirstLineFromFile (String path) throws IOException { try (BufferedReader br = new BufferedReader (new FileReader (path))) { return br.readLine(); } }
当然,它唯一的缺点是 try
不支持表达式或变量。
New I/O Java 7 引入的 New I/O API 亦称 NIO.2 或 AIO(Asynchronous I/O),NIO.2 的更新主要包括:
File System API
java.nio.file.*
java.nio.file.attribute.*
Channels API
Socket Channel API
Asynchronous I/O
Future style API
Callback style API
Miscellaneous
Infinibind (IB) Sockets Direct Protocol (SDP)
Stream Control Transport Protocol (SCTP)
想要深入了解,请参见:Java Tutorials: Java NIO.2
Binary Literals Binary Literals 也是一个比较实用特性,在 Java 7 之前,表示二进制只能通过十六进制来实现,而在 Java 7 中可以直接使用二进制(以 0b
或者 0B
作为前缀),如:
1 2 3 4 5 6 7 8 9 10 11 12 13 byte aByte = (byte )0b00100001 ;short aShort = (short )0b1010000101000101 ;int anInt1 = 0b10100001010001011010000101000101 ;int anInt2 = 0b101 ;int anInt3 = 0B101 ; long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L ;
Underscore in Number Literals 这个特性感觉有点鸡肋,可能是为了使长数字具有更好的辨识度,如:
1 2 3 4 5 6 7 8 long creditCardNumber = 1234_5678_9012_3456L ;long socialSecurityNumber = 999_99_9999L ;float pi = 3.14_15F ;long hexBytes = 0xFF_EC_DE_5E ;long hexWords = 0xCAFE_BABE ;long maxLong = 0x7fff_ffff_ffff_ffffL ;byte nybbles = 0b0010_0101 ;long bytes = 0b11010010_01101001_10010100_10010010 ;
值得注意的是,下划线只能位于数字之间,而不能位于数字的首尾以及小数点两边,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 float pi1 = 3_.1415F ; float pi2 = 3. _1415F; long socialSecurityNumber1 = 999_99_9999_L; int x1 = _52; int x2 = 5_2 ; int x3 = 52_; int x4 = 5_______2 ; int x5 = 0_x52; int x6 = 0x_52; int x7 = 0x5_2 ; int x8 = 0x52_; int x9 = 0_52 ; int x10 = 05_2 ; int x11 = 052_;
Improved Compiler Warnings and Errors Java 7 针对编译器警告和错误主要包括以下几个方面:
Heap Pollution
Variable Arguments Methods and Non-Reifiable Formal Parameters
Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters
Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters
Fork/Join Framework Java 7 引入的 fork/join 框架是 ExecutorService
接口的一种实现,它充分利用了多核的优势,它被设计用于执行能拆分的任务,利用一切可用的处理能力来提升应用的性能。fork/join 框架的核心是 ForkJoinPool ,它派生自 AbstractExecutorService
,并实现了 work-stealing 核心算法。使用方式参考下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class ForkBlur extends RecursiveAction { protected static int sThreshold = 100000 ; private int [] mSource; private int mStart; private int mLength; private int [] mDestination; private int mBlurWidth = 15 ; public ForkBlur (int [] src, int start, int length, int [] dst) { mSource = src; mStart = start; mLength = length; mDestination = dst; } protected void computeDirectly () { int sidePixels = (mBlurWidth - 1 ) / 2 ; for (int index = mStart; index < mStart + mLength; index++) { float rt = 0 , gt = 0 , bt = 0 ; for (int mi = -sidePixels; mi <= sidePixels; mi++) { int mindex = Math.min(Math.max(mi + index, 0 ), mSource.length - 1 ); int pixel = mSource[mindex]; rt += (float )((pixel & 0x00ff0000 ) >> 16 ) / mBlurWidth; gt += (float )((pixel & 0x0000ff00 ) >> 8 ) / mBlurWidth; bt += (float )((pixel & 0x000000ff ) >> 0 ) / mBlurWidth; } int dpixel = (0xff000000 ) | (((int )rt) << 16 ) | (((int )gt) << 8 ) | ((int )bt); mDestination[index] = dpixel; } } protected void compute () { if (mLength < sThreshold) { computeDirectly(); return ; } int split = mLength / 2 ; invokeAll(new ForkBlur (mSource, mStart, split, mDestination), new ForkBlur (mSource, mStart + split, mLength - split, mDestination)); } } public class Example { public static void main (String[] args) { ForkBlur fb = new ForkBlur (src, 0 , src.length, dst); ForkJoinPool pool = new ForkJoinPool (); pool.invoke(fb); } }
Garbage-First Collector (G1) 直到 Oracle JDK 7 update 4 版本才完全支持 G1 垃圾回收器,G1 主要应用于多核、堆内存较大的服务器的垃圾回收。G1 的实现原理是将堆内存划分成一系列大小相同的相邻的堆区域,然后执行并发全局标记阶段以确定整个堆中对象的活跃度,当标记阶段完成后,G1 便知道哪个区域空得多,就优先收集这些区域,这样会释放大量的空间,这也是其名字的由来。顾名思义, G1将其收集和压缩活动集中在堆中可能充满可回收对象的区域,即垃圾。 G1使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要收集的区域数。
PermGen Removal 从 Java 7 开始,有一部分驻留在永生代的数据被移到了 Java 堆或 Native 堆中:
符号表移到了本地堆中
Interned String 移到了 Java 堆中
类的静态成员移到了 Java 堆中
更多详情,请参考:https://www.oracle.com/java/technologies/javase/jdk7-relnotes.html