JDK1.7新特性
JDK1.7<一>概述
JDK1.7<二>语法
JDK1.7<三>遍历文件树
JDK1.7<四>
............
1,switch中可以使用字串了
String s = "test";
switch (s) {
case "test" :
System.out.println("test");
case "test1" :
System.out.println("test1");
break ;
default :
System.out.println("break");
break ;
}
2.运用ListtempList = new ArrayList<>(); 即泛型实例化类型自动推断
3.语法上支持集合,而不一定是数组
final List piDigits = [ 1,2,3,4,5,8 ];
4.新增一些取环境信息的工具方法
File System.getJavaIoTempDir() // IO临时文件夹
File System.getJavaHomeDir() // JRE的安装目录
File System.getUserHomeDir() // 当前用户目录
File System.getUserDir() // 启动java进程时所在的目录5
5.Boolean类型反转,空指针安全,参与位运算
Boolean Booleans.negate(Boolean booleanObj)
True => False , False => True, Null => Null
boolean Booleans.and(boolean[] array)
boolean Booleans.or(boolean[] array)
boolean Booleans.xor(boolean[] array)
boolean Booleans.and(Boolean[] array)
boolean Booleans.or(Boolean[] array)
boolean Booleans.xor(Boolean[] array)
6.两个char间的equals
boolean Character.equalsIgnoreCase(char ch1, char ch2)
7.安全的加减乘除
int Math.safeToInt(long value)
int Math.safeNegate(int value)
long Math.safeSubtract(long value1, int value2)
long Math.safeSubtract(long value1, long value2)
int Math.safeMultiply(int value1, int value2)
long Math.safeMultiply(long value1, int value2)
long Math.safeMultiply(long value1, long value2)
long Math.safeNegate(long value)
int Math.safeAdd(int value1, int value2)
long Math.safeAdd(long value1, int value2)
long Math.safeAdd(long value1, long value2)
int Math.safeSubtract(int value1, int value2)
8.map集合支持并发请求,且可以写成 Map map = {name:"xxx",age:18};
jdk7和8的一些新特性介绍
分类: java 2013-07-06 16:09 1381人阅读 评论(0) 收藏 举报
更多ppt内容请查看:http://www.javaarch.net/jiagoushi/927.htm
[java] view plaincopy
- 本文是我学习了解了jdk7和jdk8的一些新特性的一些资料,有兴趣的大家可以浏览下下面的内容。
- 官方文档:http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html
-
- 在jdk7的新特性方面主要有下面几方面的增强:
-
- 1.jdk7语法上
-
- 1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。
-
- // 所有整数 int, short,long,byte都可以用二进制表示
- // An 8-bit 'byte' value:
- byte aByte = (byte) 0b00100001;
-
- // A 16-bit 'short' value:
- short aShort = (short) 0b1010000101000101;
-
- // Some 32-bit 'int' values:
- intanInt1 = 0b10100001010001011010000101000101;
- intanInt2 = 0b101;
- intanInt3 = 0B101; // The B can be upper or lower case.
-
- // A 64-bit 'long' value. Note the "L" suffix:
- long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;
-
- // 二进制在数组等的使用
- final int[] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001,
- 0b00010011, 0b00100110, 0b01001100, 0b10011000 };
-
- 1.2 Switch语句支持string类型
-
- public static String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
- String typeOfDay;
- switch (dayOfWeekArg) {
- case "Monday":
- typeOfDay = "Start of work week";
- break;
- case "Tuesday":
- case "Wednesday":
- case "Thursday":
- typeOfDay = "Midweek";
- break;
- case "Friday":
- typeOfDay = "End of work week";
- break;
- case "Saturday":
- case "Sunday":
- typeOfDay = "Weekend";
- break;
- default:
- throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
- }
- return typeOfDay;
- }
-
- 1.3 Try-with-resource语句
-
- 注意:实现java.lang.AutoCloseable接口的资源都可以放到try中,跟final里面的关闭资源类似; 按照声明逆序关闭资源 ;Try块抛出的异常通过Throwable.getSuppressed获取
-
- try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
- java.io.BufferedWriter writer = java.nio.file.Files
- .newBufferedWriter(outputFilePath, charset)) {
- // Enumerate each entry
- for (java.util.Enumeration entries = zf.entries(); entries
- .hasMoreElements();) {
- // Get the entry name and write it to the output file
- String newLine = System.getProperty("line.separator");
- String zipEntryName = ((java.util.zip.ZipEntry) entries
- .nextElement()).getName() + newLine;
- writer.write(zipEntryName, 0, zipEntryName.length());
- }
- }
-
- 1.4 Catch多个异常 说明:Catch异常类型为final; 生成Bytecode 会比多个catch小; Rethrow时保持异常类型
-
- public static void main(String[] args) throws Exception {
- try {
- testthrows();
- } catch (IOException | SQLException ex) {
- throw ex;
- }
- }
- public static void testthrows() throws IOException, SQLException {
- }
-
- 1.5 数字类型的下划线表示 更友好的表示方式,不过要注意下划线添加的一些标准,可以参考下面的示例
-
- 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;
- //float pi1 = 3_.1415F; // Invalid; cannot put underscores adjacent to a decimal point
- //float pi2 = 3._1415F; // Invalid; cannot put underscores adjacent to a decimal point
- //long socialSecurityNumber1= 999_99_9999_L; // Invalid; cannot put underscores prior to an L suffix
- //int x1 = _52; // This is an identifier, not a numeric literal
- int x2 = 5_2; // OK (decimal literal)
- //int x3 = 52_; // Invalid; cannot put underscores at the end of a literal
- int x4 = 5_______2; // OK (decimal literal)
- //int x5 = 0_x52; // Invalid; cannot put underscores in the 0x radix prefix
- //int x6 = 0x_52; // Invalid; cannot put underscores at the beginning of a number
- int x7 = 0x5_2; // OK (hexadecimal literal)
- //int x8 = 0x52_; // Invalid; cannot put underscores at the end of a number
- int x9 = 0_52; // OK (octal literal)
- int x10 = 05_2; // OK (octal literal)
- //int x11 = 052_; // Invalid; cannot put underscores at the end of a number
- 1.6 泛型实例的创建可以通过类型推断来简化 可以去掉后面new部分的泛型类型,只用<>就可以了。
- //使用泛型前
- List strList = new ArrayList();
- List strList4 = new ArrayList();
- List
-
-
- //编译器使用尖括号 (<>) 推断类型
- List strList0 = new ArrayList();
- List>> strList1 = new ArrayList>>();
- List strList2 = new ArrayList<>();
- List>> strList3 = new ArrayList<>();
- List list = new ArrayList<>();
- list.add("A");
- // The following statement should fail since addAll expects
- // Collection extends String>
- //list.addAll(new ArrayList<>());
-
- 1.7在可变参数方法中传递非具体化参数,改进编译警告和错误
-
- Heap pollution 指一个变量被指向另外一个不是相同类型的变量。例如
-
- List l = new ArrayList();
- List ls = l; // unchecked warning
- l.add(0, new Integer(42)); // another unchecked warning
- String s = ls.get(0); // ClassCastException is thrown
- Jdk7:
- public static void addToList (List listArg, T... elements) {
- for (T x : elements) {
- listArg.add(x);
- }
- }
- 你会得到一个warning
- warning: [varargs] Possible heap pollution from parameterized vararg type
- 要消除警告,可以有三种方式
- 1.加 annotation @SafeVarargs
- 2.加 annotation @SuppressWarnings({"unchecked", "varargs"})
- 3.使用编译器参数 –Xlint:varargs;
-
- 1.8 信息更丰富的回溯追踪 就是上面try中try语句和里面的语句同时抛出异常时,异常栈的信息
-
- java.io.IOException
- §? at Suppress.write(Suppress.java:19)
- §? at Suppress.main(Suppress.java:8)
- §? Suppressed: java.io.IOException
- §? at Suppress.close(Suppress.java:24)
- §? at Suppress.main(Suppress.java:9)
- §? Suppressed: java.io.IOException
- §? at Suppress.close(Suppress.java:24)
- §? at Suppress.main(Suppress.java:9)
-
-
- 2. NIO2的一些新特性
-
- 1.java.nio.file 和java.nio.file.attribute包 支持更详细属性,比如权限,所有者
- 2. symbolic and hard links支持
- 3. Path访问文件系统,Files支持各种文件操作
- 4.高效的访问metadata信息
- 5.递归查找文件树,文件扩展搜索
- 6.文件系统修改通知机制
- 7.File类操作API兼容
- 8.文件随机访问增强 mapping a region,locl a region,绝对位置读取
- 9. AIO Reactor(基于事件)和Proactor
-
- 下面列一些示例:
-
- 2.1IO and New IO 监听文件系统变化通知
-
- 通 过FileSystems.getDefault().newWatchService()获取watchService,然后将需要监听的path目录 注册到这个watchservice中,对于这个目录的文件修改,新增,删除等实践可以配置,然后就自动能监听到响应的事件。
-
- private WatchService watcher;
- public TestWatcherService(Path path) throws IOException {
- watcher = FileSystems.getDefault().newWatchService();
- path.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
- }
- public void handleEvents() throws InterruptedException {
- while (true) {
- WatchKey key = watcher.take();
- for (WatchEvent> event : key.pollEvents()) {
- WatchEvent.Kind kind = event.kind();
- if (kind == OVERFLOW) {// 事件可能lost or discarded
- continue;
- }
- WatchEvent e = (WatchEvent) event;
- Path fileName = e.context();
- System.out.printf("Event %s has happened,which fileName is %s%n",kind.name(), fileName);
- }
- if (!key.reset()) {
- break;
- }
-
- 2.2 IO and New IO 遍历文件树 ,通过继承SimpleFileVisitor类,实现事件遍历目录树的操作,然后通过 Files.walkFileTree(listDir, opts, Integer.MAX_VALUE, walk);这个API来遍历目录树
-
- private void workFilePath() {
- Path listDir = Paths.get("/tmp"); // define the starting file
- ListTree walk = new ListTree();
- …Files.walkFileTree(listDir, walk);…
- // 遍历的时候跟踪链接
- EnumSet opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
- try {
- Files.walkFileTree(listDir, opts, Integer.MAX_VALUE, walk);
- } catch (IOException e) {
- System.err.println(e);
- }
- class ListTree extends SimpleFileVisitor {// NIO2 递归遍历文件目录的接口
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
- System.out.println("Visited directory: " + dir.toString());
- return FileVisitResult.CONTINUE;
- }
- @Override
- public FileVisitResult visitFileFailed(Path file, IOException exc) {
- System.out.println(exc);
- return FileVisitResult.CONTINUE;
- }
- }
-
-
- 2.3 AIO异步IO 文件和网络 异步IO在java
- NIO2 实现了,都是用AsynchronousFileChannel,AsynchronousSocketChanne等实现,关于同步阻塞IO,同步非阻 塞IO,异步阻塞IO和异步非阻塞IO在ppt的这页上下面备注有说明,有兴趣的可以深入了解下。Java NIO2中就实现了操作系统的异步非阻塞 IO。
-
- // 使用AsynchronousFileChannel.open(path, withOptions(),
- // taskExecutor))这个API对异步文件IO的处理
- public static void asyFileChannel2() {
- final int THREADS = 5;
- ExecutorService taskExecutor = Executors.newFixedThreadPool(THREADS);
- String encoding = System.getProperty("file.encoding");
- List> list = new ArrayList<>();
- int sheeps = 0;
- Path path = Paths.get("/tmp",
- "store.txt");
- try (AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel
- .open(path, withOptions(), taskExecutor)) {
- for (int i = 0; i < 50; i++) {
- Callable worker = new Callable() {
- @Override
- public ByteBuffer call() throws Exception {
- ByteBuffer buffer = ByteBuffer
- .allocateDirect(ThreadLocalRandom.current()
- .nextInt(100, 200));
- asynchronousFileChannel.read(buffer, ThreadLocalRandom
- ……
-
-
- 3. JDBC 4.1
-
- 3.1.可以使用try-with-resources自动关闭Connection, ResultSet, 和 Statement资源对象
-
- 3.2. RowSet 1.1:引入RowSetFactory接口和RowSetProvider类,可以创建JDBC driver支持的各种 row sets,这里的rowset实现其实就是将sql语句上的一些操作转为方法的操作,封装了一些功能。
-
- 3.3. JDBC-ODBC驱动会在jdk8中删除
-
- try (Statement stmt = con.createStatement()) {
- RowSetFactory aFactory = RowSetProvider.newFactory();
- CachedRowSet crs = aFactory.createCachedRowSet();
-
- RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl", null);
- WebRowSet wrs = rsf.createWebRowSet();
- createCachedRowSet
- createFilteredRowSet
- createJdbcRowSet
- createJoinRowSet
- createWebRowSet
-
-
- 4. 并发工具增强
-
- 4.1.fork-join
- 最大的增强,充分利用多核特性,将大问题分解成各个子问题,由多个cpu可以同时解决多个子问题,最后合并结果,继承RecursiveTask,实现compute方法,然后调用fork计算,最后用join合并结果。
-
- class Fibonacci extends RecursiveTask {
- final int n;
- Fibonacci(int n) {
- this.n = n;
- }
- private int compute(int small) {
- final int[] results = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
- return results[small];
- }
- public Integer compute() {
- if (n <= 10) {
- return compute(n);
- }
- Fibonacci f1 = new Fibonacci(n - 1);
- Fibonacci f2 = new Fibonacci(n - 2);
- System.out.println("fork new thread for " + (n - 1));
- f1.fork();
- System.out.println("fork new thread for " + (n - 2));
- f2.fork();
- return f1.join() + f2.join();
- }
- }
-
- 4.2.ThreadLocalRandon 并发下随机数生成类,保证并发下的随机数生成的线程安全,实际上就是使用threadlocal
-
- final int MAX = 100000;
- ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
- long start = System.nanoTime();
- for (int i = 0; i < MAX; i++) {
- threadLocalRandom.nextDouble();
- }
- long end = System.nanoTime() - start;
- System.out.println("use time1 : " + end);
- long start2 = System.nanoTime();
- for (int i = 0; i < MAX; i++) {
- Math.random();
- }
- long end2 = System.nanoTime() - start2;
- System.out.println("use time2 : " + end2);
-
-
- 4.3. phaser 类似cyclebarrier和countdownlatch,不过可以动态添加资源减少资源
-
- void runTasks(List tasks) {
- final Phaser phaser = new Phaser(1); // "1" to register self
- // create and start threads
- for (final Runnable task : tasks) {
- phaser.register();
- new Thread() {
- public void run() {
- phaser.arriveAndAwaitAdvance(); // await all creation
- task.run();
- }
- }.start();
- }
- // allow threads to start and deregister self
- phaser.arriveAndDeregister();
- }
-
- 5. Networking增强
-
- 新增URLClassLoader close方法,可以及时关闭资源,后续重新加载class文件时不会导致资源被占用或者无法释放问题
- URLClassLoader.newInstance(new URL[]{}).close();
- 新增Sockets Direct Protocol
- 绕过操作系统的数据拷贝,将数据从一台机器的内存数据通过网络直接传输到另外一台机器的内存中
-
- 6. Multithreaded Custom Class Loaders
-
- 解决并发下加载class可能导致的死锁问题,这个是jdk1.6的一些新版本就解决了,jdk7也做了一些优化。有兴趣可以仔细从官方文档详细了解
-
- jdk7前:
-
- Class Hierarchy:
- class A extends B
- class C extends D
- ClassLoader Delegation Hierarchy:
- Custom Classloader CL1:
- directly loads class A
- delegates to custom ClassLoader CL2 for class B
- Custom Classloader CL2:
- directly loads class C
- delegates to custom ClassLoader CL1 for class D
- Thread 1:
- Use CL1 to load class A (locks CL1)
- defineClass A triggers
- loadClass B (try to lock CL2)
- Thread 2:
- Use CL2 to load class C (locks CL2)
- defineClass C triggers
- loadClass D (try to lock CL1)
- Synchronization in the ClassLoader class wa
-
- jdk7
-
- Thread 1:
- Use CL1 to load class A (locks CL1+A)
- defineClass A triggers
- loadClass B (locks CL2+B)
- Thread 2:
- Use CL2 to load class C (locks CL2+C)
- defineClass C triggers
- loadClass D (locks CL1+D)
-
-
- 7. Security 增强
-
- 7.1.提供几种 ECC-based algorithms (ECDSA/ECDH) Elliptic Curve Cryptography (ECC)
- 7.2.禁用CertPath Algorithm Disabling
- 7.3. JSSE (SSL/TLS)的一些增强
-
- 8. Internationalization 增强 增加了对一些编码的支持和增加了一些显示方面的编码设置等
-
- 1. New Scripts and Characters from Unicode 6.0.0
- 2. Extensible Support for ISO 4217 Currency Codes
- Currency类添加:
- getAvailableCurrencies
- getNumericCode
- getDisplayName
- getDisplayName(Locale)
- 3. Category Locale Support
- getDefault(Locale.Category)FORMAT DISPLAY
- 4. Locale Class Supports BCP47 and UTR35
- UNICODE_LOCALE_EXTENSION
- PRIVATE_USE_EXTENSION
- Locale.Builder
- getExtensionKeys()
- getExtension(char)
- getUnicodeLocaleType(String
- ……
- 5. New NumericShaper Methods
- NumericShaper.Range
- getShaper(NumericShaper.Range)
- getContextualShaper(Set)……
-
-
- 9.jvm方面的一些特性增强,下面这些特性有些在jdk6中已经存在,这里做了一些优化和增强。
-
- 1.Jvm支持非java的语言 invokedynamic 指令
-
- 2. Garbage- First Collector 适合server端,多处理器下大内存,将heap分成大小相等的多个区域,mark阶段检测每个区域的存活对 象,compress阶段将存活对象最小的先做回收,这样会腾出很多空闲区域,这样并发回收其他区域就能减少停止时间,提高吞吐量。
-
- 3. HotSpot性能增强
- Tiered Compilation -XX:+UseTieredCompilation 多层编译,对于经常调用的代码会直接编译程本地代码,提高效率
- Compressed Oops 压缩对象指针,减少空间使用
- Zero-Based Compressed Ordinary Object Pointers (oops) 进一步优化零基压缩对象指针,进一步压缩空间
-
- 4. Escape Analysis 逃逸分析,对于只是在一个方法使用的一些变量,可以直接将对象分配到栈上,方法执行完自动释放内存,而不用通过栈的对象引用引用堆中的对象,那么对于对象的回收可能不是那么及时。
-
- 5. NUMA Collector Enhancements
-
- NUMA(Non Uniform Memory Access),NUMA在多种计算机系统中都得到实现,简而言之,就是将内存分段访问,类似于硬盘的RAID,Oracle中的分簇
-
- 10. Java 2D Enhancements
-
- 1. XRender-Based Rendering Pipeline -Dsun.java2d.xrender=True
- 2. Support for OpenType/CFF Fonts GraphicsEnvironment.getAvailableFontFamilyNames
- 3. TextLayout Support for Tibetan Script
- 4. Support for Linux Fonts
-
- 11. Swing Enhancements
-
- 1. JLayer
- 2. Nimbus Look & Feel
- 3. Heavyweight and Lightweight Components
- 4. Shaped and Translucent Windows
- 5. Hue-Saturation-Luminance (HSL) Color Selection in JColorChooser Class
-
-
-
- 12. Jdk8 lambda表达式 最大的新增的特性,不过在很多动态语言中都已经原生支持。
-
- 原来这么写:
-
- btn.setOnAction(new EventHandler() {
- @Override
- public void handle(ActionEvent event) {
- System.out.println("Hello World!");
- }
- });
-
- jdk8直接可以这么写:
-
- btn.setOnAction(
- event -> System.out.println("Hello World!")
- );
-
- 更多示例:
-
- public class Utils {
- public static int compareByLength(String in, String out){
- return in.length() - out.length();
- }
- }
-
- public class MyClass {
- public void doSomething() {
- String[] args = new String[] {"microsoft","apple","linux","oracle"}
- Arrays.sort(args, Utils::compareByLength);
- }
- }
-
- 13.jdk8的一些其他特性,当然jdk8的增强功能还有很多,大家可以参考http://openjdk.java.net/projects/jdk8/
-
- 用Metaspace代替PermGen
- 动态扩展,可以设置最大值,限制于本地内存的大小
- Parallel array sorting 新APIArrays#parallelSort.
-
- New Date & Time API
- Clock clock = Clock.systemUTC(); //return the current time based on your system clock and set to UTC.
-
- Clock clock = Clock.systemDefaultZone(); //return time based on system clock zone
-
- long time = clock.millis(); //time in milliseconds from January 1st, 1970
-
- 更多内容可以看看附件ppt,示例代码可以参考:https://github.com/zhwj184/jdk7-8demo
JavaJDK1.8新特性
分类: 日记 2013-07-15 16:022254人阅读 评论(1) 收藏 举报
一、lambda含义
lambda表示数学符号“λ”,计算机领域中λ代表“λ演算”,表达了计算机中最基本的概念:“调用”和“置换”。在很多动态语言和C#中都有相应的lambda语法,这类语法都为了简化代码,提高运行效率。
二、lambda 项目的背景,参考这里。
无论是面向对象语言还是函数式语言,基本数值都可以被动态的封装入程序动作:面向对象语言通过“方法”,函数式语言通过“函数。
介于“方法”和“函数”的定义有很多种,补充下IBM知识库的解释:
在面向对象语言中,方法不是一阶值(First-class value),在函数式语言中,函数是一阶值。在函数式语言中,函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值,函数可以嵌套定义,而在面向对象语言中的的“方法”做不到这点。
Java可以说是面向对象语言的代表,如果要调用其方法,需要先创建对象。不过Java对象都是“重量级”的,实例化具体的类的对象,需要经过定义和申明 两个阶段。比如定义方法,并给内部字段赋初始值。但是一个对象只包含一个方法的情况很多,比如实现API中的“回调接口”功能的类,在swing中有接 口:
Java代码
1. "">public interface ActionListener {
2. void actionPerformed(ActionEvent e);
3. }
现有的实现方式大多是:
Java代码
1. "">button.addActionListener(new ActionListener() {
2. public void actionPerformed(ActionEvent e) {
3. ui.dazzle(e.getModifiers());
4. }
5. });
很多现有的类库都基于这种设计实现,所以对于代码被明确定义运行在单独线程的API来说,匿名内部类尤为重要。这些匿名内部类只存在于创建它的线程中。但 是在并行计算领域,CPU的制造商着力发展多核技术来提升CPU的功能,这么做几乎无法依靠多核的优势来提升其性能。
鉴于回调函数和其他功能式语法的关系越来越密切,所以必须建立尽可能的轻量级的数据模型(从编码角度而言,性能方面下文再说)。对于这点来说匿名内部类的缺点如下:
1. 语法相对复杂。
2. 在调用内部类的上下文中,指引和this的指代容易混淆。
3. 类加载和实例创建语法不可避免。
4. 不能引用外部的非final对象。
5. 不能抽象化控制流程
针对这些问题,lambda项目致力于
1. 消除问题1和问题2,通过引入更简单的表达式和局部变量的定义规则。
2. 回避问题3,定义更灵活更友善的语法。这里只是回避,类加载和实例化本身不可避免。下文会解释。
3. 改善问题4,允许用户使用最终有效的局部变量。
不过lambda项目目前并不能解决所有关于内部类的问题。问题4和问题5没有完全解决,这计划在将类版本中继续改善。对于性能方面,原文也没有提,不过后面有些补充。
三、lambda用法
通过上文可以了解到,lambda语法是针对“回调接口”和“匿名内部类”作出的改进,所以lambda的语法目前仅对于部分接口,这些接口的特点是 只含一个抽象方法,在lambda项目中,早期称为SAM类型(SAM = single abstract method 即单一抽象方法)。在最新的文档中(即这个版本),它们有了新名字,叫函数接口(functionalinterface),比如:
1 java.lang.Runnable
2 java.util.concurrent.Callable
3 java.security.PrivilegedAction
4 java.util.Comparator
5 java.io.FileFilter
6 java.nio.file.PathMatcher
7 java.lang.reflect.InvocationHandler
8 java.beans.PropertyChangeListener
9 java.awt.event.ActionListener
10 javax.swing.event.ChangeListener
lambda的语法包括三部分
1、参数列表
2、箭头符号"->"
3、代码块。
其中代码块很像一个方法体,return语句将控制权交还给匿名方法(anonymous method,即lambda表达式)的调用者;break和continue不能出现在函数体的顶部,不过可以出现在内部的循环里;如果代码块得出最终 结果,那么每一个控制路径(control path) 必须都有返回或抛出异常。
如果代码块只有简单一行,可以省略return关键字和“{}”符号(以下所写的例子都是基于JDK 1.8 lambda预览版),比如:
Java代码
1. "">public class LambdaTest {
2. public static void main(String... args) {
3. //这里有{}和return 以及 ;
4. Runnable r = () -> { System.out.println("hello world"); };
5.
6. //这里不需要{}和return
7. java.util.Comparator c = (String s1, String s2) -> s2.length()-s1.length();
8. r.run();
9. System.out.println(c.compare("s1", "12323"));
10. }
11. }
输出为:
hello world
3
除了这些现有接口,我们还可以自定义函数接口:
Java代码
1. "">public class LambdaTest {
2. interface lambdaInterface {
3. public void me(String str);
4. }
5.
6. public static void main(String... args) {
7. lambdaInterface li = (String s)->{System.out.println(s);};
8. li.me("hello world!");
9. }
10. }
输出为:
hello world!
新的lambda方法从语法上的确是简化了很多。和lambda第一次发布的语法相比也优雅很多。
四、lambda代码块的字节码
看完了语法的确很简单,那么lambda是怎么实现的,就得从字节码考察了。这里和匿名内部类做个对比,编译如下代码:
Java代码
1. "">public class LambdaTest {
2. lambdaInterface li1 = ()->{System.out.println(this);};
3. lambdaInterface li2 = new lambdaInterface(){
4. public void me(){
5. System.out.println(this);
6. }
7. };
8.
9. public static void main(String... args) {
10. LambdaTest lt = new LambdaTest();
11. lt.li1.me();
12. lt.li2.me();
13. }
14. }
15.
16. interface lambdaInterface {
17. public void me();
18. }
编译后有会有四个文件:
LambdaTest.class
LambdaTest$1.class
LambdaTest$2.class
lambdaInterface.class
它的的输出结果为:
LambdaTest@200bde
LambdaTest$1@1eb41d6
结果很明显地显示,lambda代码块和内部类的this的指引是不一样的。lambda代码块输出的是调用者的this,即lambdaTest.class的实例。匿名内部类输出的是自己lambdaTest$1.class的实例。
先看看lambdaTest.class的字节码:
Txt代码
1. "">public class LambdaTest {
2. lambdaInterface li1;
3.
4. lambdaInterface li2;
5.
6. public LambdaTest();
7. Code:
8. 0: aload_0
9. 1: invokespecial #1 // Method java/lang/Object."":()V
10. 4: aload_0
11. 5: new #2 // class LambdaTest$2
12. 8: dup
13. 9: aload_0
14. 10: aload_0
15. 11: invokespecial #3 // Method LambdaTest$2."":(LLambdaTest;LLambdaTest;)V
16. 14: putfield #4 // Field li1:LlambdaInterface;
17. 17: aload_0
18. 18: new #5 // class LambdaTest$1
19. 21: dup
20. 22: aload_0
21. 23: invokespecial #6 // Method LambdaTest$1."":(LLambdaTest;)V
22. 26: putfield #7 // Field li2:LlambdaInterface;
23. 29: return
24.
25. public static void main(java.lang.String...);
26. Code:
27. 0: new #8 // class LambdaTest
28. 3: dup
29. 4: invokespecial #9 // Method "":()V
30. 7: astore_1
31. 8: aload_1
32. 9: getfield #4 // Field li1:LlambdaInterface;
33. 12: invokeinterface #10, 1 // InterfaceMethod lambdaInterface.me:()V
34. 17: aload_1
35. 18: getfield #7 // Field li2:LlambdaInterface;
36. 21: invokeinterface #10, 1 // InterfaceMethod lambdaInterface.me:()V
37. 26: return
38. }
从这里可以看出,lambda代码块和匿名内部类的调用方法是一样的,都是先创建一个方法,然后创建其句柄,这两个句柄分别对应lambdaTest$2.class和lambdaTest$1.class。
其中匿名内部类对应LambdaTest$1,它的字节码为:
Txt代码
1. "">class LambdaTest$1 implements lambdaInterface {
2. final LambdaTest this$0;
3.
4. LambdaTest$1(LambdaTest);
5. Code:
6. 0: aload_0
7. 1: aload_1
8. 2: putfield #1 // Field this$0:LLambdaTest;
9. 5: aload_0
10. 6: invokespecial #2 // Method java/lang/Object."":()V
11. 9: return
12.
13. public void me();
14. Code:
15. 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16. 3: aload_0
17. 4: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
18. 7: return
19. }
lambda代码块对应LambdaTest$2,它的字节码为:
Txt代码
1. "">class LambdaTest$2 implements lambdaInterface {
2. LambdaTest encl$0;
3.
4. final LambdaTest this$0;
5.
6. LambdaTest$2(LambdaTest, LambdaTest);
7. Code:
8. 0: aload_0
9. 1: aload_1
10. 2: putfield #1 // Field this$0:LLambdaTest;
11. 5: aload_0
12. 6: invokespecial #2 // Method java/lang/Object."":()V
13. 9: aload_0
14. 10: aload_2
15. 11: putfield #3 // Field encl$0:LLambdaTest;
16. 14: return
17.
18. public void me();
19. Code:
20. 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
21. 3: aload_0
22. 4: getfield #3 // Field encl$0:LLambdaTest;
23. 7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
24. 10: return
25. }
26.
从它们的字节码可以很明显的看出,lambda代码块的字节码在初始化时多了putfield指令,在me()运行时,有getfield指令。从解释 Field encl$0:LLambdaTest;中可以知道这步骤就是LambdaTest实例引用的入栈和出栈,所以lambda的代码块的this的引用是其 调用者LambdaTest.class,这里和匿名内部类不一样。相比较而言,lambda代码块对this的引用更精准。
至此lambda项目的前两个目标已经完成。即简化语法,和明确对象指引。
五、lamdab的性能
在JDK1.7中,引入了虚拟机新指令,invokedynamic(有点函数指针的味道),将用于支持lambda项目,具体可以参考Rémi Forax 的博客。
但是目前javap指令还不能直生成带有invokedynamic指令的字节码,不过可以通过别的工具来实现,可以参考周志明的文章。这里引用引用周志明的说明,简单的解释如下(详细的请看他的文章):
每一处含有invokedynamic指令的位置都被称作“动态调用点(Dynamic Call Site)”,这条指令的第一个参数不再是代表方法符号引用的CONSTANT_Methodref_info常量,而是变为JDK 7新加入的CONSTANT_InvokeDynamic_info常量,从这个新常量中可以得到3项信息:引导方法(Bootstrap Method,此方法存放在新增的BootstrapMethods属性中)、方法类型(MethodType)和名称。引导方法是有固定的参数,并且返 回值是java.lang.invoke.CallSite对象,这个代表真正要执行的目标方法调用。根据 CONSTANT_InvokeDynamic_info常量中提供的信息,虚拟机可以找到并且执行引导方法,从而获得一个CallSite对象,最终调 用要执行的目标方法上。
换句话说就是在虚拟机内部加入了类似C的函数指针功能。从以上的例子中可以看出,目前lambda的代码块是按照匿名内部类的方式进行工作的,即:内部 类+方法句柄,那么虚拟机工作的第一步是加载内部类,再调用对应方法,如果使用过于平频繁,那么内部类的new动作开销就比较大了。在invokedynamic指令的帮助下,可以直接调用lambda所对应的方法,而不用创建内部类,这样就避免了“内部类”所带来的加载等问题。目前从 lambda的话题库来看是这么解释invokedynamic如何工作和优势的。
在lamdba项目的说明中已经明确表示,在预览版中将lamdba代码块编译成内部类的形式是暂时的。最终将采用invokedynamic指令来实现,即提升性能的部分最终是invokedynamic指令对函数接口的支持行能力来提升。
不过手痒,还是自己测了下速度,仅供参考:
Java代码
1. "">public class LambdaTest {
2. lambdaInterface li1 = (String s)->{System.out.println(s);};
3.
4. lambdaInterface li2 = new lambdaInterface(){
5. public void me(String s){
6. System.out.println(s);
7. }
8. };
9.
10. public static void main(String... args) {
11. //这里原来有错误,已经纠正了。
12. LambdaTest lt = new LambdaTest();
13. long l1 = System.currentTimeMillis();
14. for (int i = 0; i < 1000; i++) {
15. lt.li1.me("21");
16. }
17. long l2 = System.currentTimeMillis();
18. for (int i = 0; i < 1000; i++) {
19. lt.li2.me("21");
20. }
21. long l3 = System.currentTimeMillis();
22. System.out.println(l2-l1);
23. System.out.println(l3-l2);
24. }
25. }
运行参数设置-server,其余默认,运行结果为
200
172
这么看来似乎lambda语法编译编译的代码块略慢一些。不过考虑的到是预览版,lamdab或者说invokedynamic指令的实际性能估计得等到JDK1.8正式发布才能一窥究竟。
仅管性能方面还没进步,不过灵活的语法已经带来了很多便利,对于脚本语言来说更方便。另外在闭包的意义上来说也更加完美。总的来说Lamdba还是很值得期待的。
Java 8 的lambda 表达式
Java 8 预计将在 2013 年发布,Java 8 将支持 Lambda 功能,尽管该规范还在不断的变化,但是 Java 8 的开发版已经实现了对 lambda 的支持。
关于 lambda 表达式的定义请看维基百科。
该文章将带你熟悉 lambda 语法,以及使用集合 API 中的 lambda 以及相关的语言增强,本文所有的代码都是在 JDK 8 lambda build b39 编译。
功能接口
只包含一个方法的接口被称为功能接口,Lambda 表达式用用于任何功能接口适用的地方。
java.awt.event.ActionListener
就是一个功能接口,因为它只有一个方法:void actionPerformed(ActionEvent)
. 在 Java 7 中我们会编写如下代码:
1 |
button.addActionListener(new ActionListener() { |
2 |
public void actionPerformed(ActionEvent e) { |
3 |
ui.dazzle(e.getModifiers()); |
4 |
} |
而 Java 8 中可以简化为:
1 |
button.addActionListener(e -> { ui.dazzle(e.getModifiers()); }); |
编译器知道lambda 表达式必须符合 void actionPerformed(ActionEvent)
方法的定义。看起来 lambda 实体返回 void,实际上它可以推断出参数 e 的类型是 java.awt.event.ActionEvent
.
函数集合
Java 8 的类库包含一个新的包 java.util.functions
,这个包中有很多新的功能接口,这些接口可与集合 API 一起使用。
java.util.functions.Predicate
使用谓词 (Predicate) 来筛选集合:
1 |
List names = Arrays.asList("Alice", "Bob", "Charlie", "Dave"); |
2 |
List filteredNames = names |
3 |
.filter(e -> e.length() >= 4) |
4 |
.into(new ArrayList()); |
5 |
for (String name : filteredNames) { |
6 |
System.out.println(name); |
这里我们有两个新方法:
Iterable filter(Predicate super T>)
用于获取元素满足某个谓词返回 true 的结果
> A into(A)
将用返回的结果填充 ArrayList
java.util.functions.Block
我们可使用一个新的迭代器方法来替换 for 循环 void forEach(Block super T>)
:
1 |
List names = Arrays.asList("Alice", "Bob", "Charlie", "Dave"); |
2 |
names |
3 |
.filter(e -> e.length() >= 4) |
4 |
.forEach(e -> { System.out.println(e); }); |
forEach()
方法是 internaliteration 的一个实例:迭代过程在 Iterable
和 Block
内部进行,每次可访问一个元素。
最后的结果就是用更少的代码来处理集合:
1 |
List names = Arrays.asList("Alice", "Bob", "Charlie", "Dave"); |
2 |
names |
3 |
.mapped(e -> { return e.length(); }) |
4 |
.asIterable() // returns an Iterable of BiValue elements |
5 |
// an element's key is the person's name, its value is the string length |
6 |
.filter(e -> e.getValue() >= 4) |
7 |
.sorted((a, b) -> a.getValue() - b.getValue()) |
8 |
.forEach(e -> { System.out.println(e.getKey() + '\t' + e.getValue()); }); |
这样做的优点是:
元素在需要的时候才进行计算
如果我们取一个上千个元素的集合的前三条时,其他元素就不会被映射
鼓励使用方法链
我们无需才存储中间结果来构建新的集合
内部迭代过程因此大多数细节
例如,我们可以通过下面代码来并行 map() 操作
writing myCollection.parallel().map(e ‑> e.length())
.
方法引用
我们可通过 :: 语法来引用某个方法。方法引用被认为是跟 lambda表达式一样的,可用于功能接口所适用的地方。
我们可以引用一个静态方法:
1 |
executorService.submit(MethodReference::sayHello); |
2 |
|
3 |
private static void sayHello() { |
4 |
System.out.println("hello"); |
或者是一个实例的方法:
1 |
Arrays.asList("Alice", "Bob", "Charlie", "Dave").forEach(System.out::println); |
我们也可以创建工程方法并将构造器引用赋值给 java.util.functions.Factory
:
1 |
Factory biscuitFactory = Biscuit::new; |
2 |
Biscuit biscuit = biscuitFactory.make(); |
最后,我们创建一个引用到随意实例的例子:
1 |
interface Accessor { |
2 |
PROPERTY access(BEAN bean); |
5 |
public static void main(String[] args) { |
6 |
Address address = new Address("29 Acacia Road", "Tunbridge Wells"); |
7 |
Accessor accessor = Address::getCity; |
8 |
System.out.println(accessor.access(address)); |
这里我们无需绑定方法引用到某个实例,我们直接将实例做为功能接口的参数进行传递。
默认方法
直到今天的 Java ,都不可能为一个接口添加方法而不会影响到已有的实现类。而 Java 8 允许你为接口自身指定一个默认的实现:
01 |
interface Queue { |
02 |
Message read(); |
03 |
void delete(Message message); |
04 |
void deleteAll() default { |
05 |
Message message; |
06 |
while ((message = read()) != null) { |
子接口可以覆盖默认的方法:
1 |
interface BatchQueue extends Queue { |
2 |
void setBatchSize(int batchSize); |
3 |
void deleteAll() default { |
4 |
setBatchSize(100); |
5 |
Queue.super.deleteAll(); |
6 |
} |
或者子接口也可以通过重新声明一个没有方法体的方法来删除默认的方法:
1 |
interface FastQueue extends Queue { |
2 |
void deleteAll(); |
这个将强制所有实现了 FastQueue 的类必须实现 deleteAll() 方法。
HotSpot 实现
lambda 不只是可以减少很多代码的编写,其字节码和运行时的实现也比Java 7 中的匿名类的效率更高。针对每一个 lambda 表达式,编译器都会创建一个对应的形如lambda$1() 这样的方法。这个过程被称之为 lambdabody desugaring. 当遇见一个 lambda 表达式,编译器将会发起一个 invokedynamic
调用,并从目标功能接口中获取返回值。
深入阅读