Java版本:JDK8的⼗⼤新特性介绍
JDK8常⽤包及概述
java.applet 提供了需要创建⼀个⼩程序和⽤来跟其他⼩程序交流上下⽂的类。
Java.awt 包含⽤于创建⽤户界⾯和绘制图形图像的所有类
Java.io 提供与输⼊输出相关的类
Java.beans 包含与开发javaBeans相关的类
Java.lang 提供java语⾔程序设计的基础类
Java 提供实现⽹络操作相关的类
Java.nio 为输⼊输出提供缓冲区的类
< 提供处理⽂本、⽇期、数字和消息的类和接⼝
Java.util 提供处理⽇期、时间、随机数⽣成等各种使⽤⼯具的类
Javax 提供⽤于⽹络应⽤程序的类、⽹络应⽤扩展类
Java.swing 提供⼀组与AWT功能相同的纯java的组件类
java.sql 该包提供了使⽤Java语⾔访问并处理存储在数据源(通常是⼀个关系型数据库)中的数据API。
java.RMI 该包提供远程⽅法调⽤相关API
JDK8新特性:
1.Lambda表达式
2.新的⽇期API
3.引⼊Optional
4.使⽤Base64
5.接⼝的默认⽅法和静态⽅法
6.新增⽅法引⽤格式
7.新增Stream类
8.注解相关的改变
9.⽀持并⾏(parallel)数组
10.对并发类(Concurrency)的扩展。
⼀、Lambda表达式
Lambda 表达式也可称为闭包,是推动Java 8 发布的最重要新特性。lambda表达式本质上是⼀个匿名⽅法。Lambda允许把函数作为⼀个⽅法的参数(函数作为参数传递进⽅法中)或者把代码看成数据。使⽤Lambda 表达式可以使代码变的更加简洁紧凑。在最简单的形式中,⼀个lambda可以由:⽤逗号分隔的参数列表、–>符号、函数体三部分表⽰,在某些情况下lambda的函数体会更加复杂,这时可以把函数体放到在⼀对花括号中,就像在Java中定义普通函数⼀样。Lambda可以引⽤类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更⾼)。Lambda可能会返回⼀个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有⼀⾏的话,那么没有必要显式使⽤return语句。
如何使现有的函数友好地⽀持lambda。最终采取的⽅法是:增加函数式接⼝的概念。函数式接⼝就是
接⼝⾥⾯必须有且只有⼀个抽象⽅法的普通接⼝,像这样的接⼝可以被隐式转换为lambda表达式成为函数式接⼝。在可以使⽤lambda表达式的地⽅,⽅法声明时必须包含⼀个函数式的接⼝。任何函数式接⼝都可以使⽤lambda表达式替换,例如:ActionListener、Comparator、Runnable。
函数式接⼝是容易出错的:如有某个⼈在接⼝定义中增加了另⼀个⽅法,这时,这个接⼝就不再是函数式的了,并且编译过程也会失败。为了克服函数式接⼝的这种脆弱性并且能够明确声明接⼝作为函数式接⼝的意图,Java 8增加了⼀种特殊的注解@FunctionalInterface,但是默认⽅法与静态⽅法并不影响函数式接⼝的契约,可以任意使⽤。
使⽤lambda表达式替换匿名类,⽽实现Runnable接⼝是匿名类的最好⽰例。通过() -> {}代码块替代了整个匿名类。
java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
Java 8⽅式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
Lambda 表达式免去了使⽤匿名⽅法的⿇烦,并且给予Java简单但是强⼤的函数化的编程能⼒。
⼆、新的⽇期API
Java 8通过发布新的Date-Time API (JSR 310)来进⼀步加强对⽇期与时间的处理。在旧版的 Java 中,⽇期时间 API 存在诸多问题,⽐如:
1.⾮线程安全 − java.util.Date 是⾮线程安全的,所有的⽇期类都是可变的,这是Java⽇期类最⼤的问题之⼀。
2.设计很差 − Java的⽇期/时间类的定义并不⼀致,在java.util和java.sql的包中都有⽇期类,此外⽤于格式化和解析的类在包中定义。java.util.Date同时包含⽇期和时间,⽽java.sql.Date仅包含⽇期,将其纳⼊java.sql包并不合理。另外这两个类都有相同的名字,这本⾝就是⼀个⾮常糟糕的设计。
3.时区处理⿇烦 − ⽇期类并不提供国际化,没有时区⽀持,因此Java引⼊了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个⽐较重要的 API:
1.Local(本地) − 简化了⽇期时间的处理,没有时区的问题。
2.Zoned(时区) − 通过制定的时区处理⽇期时间。
新的java.time包涵盖了所有处理⽇期,时间,⽇期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
三、Optional
Optional类实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional 类的引⼊很好的解决空指针异常。Optional提供很多有⽤的⽅法,这样我们就不⽤显式进⾏空值检测。尽量避免在程序中直接调⽤Optional对象的get()和isPresent()⽅法,避免使⽤Optional类型声明实体类的属性。
(1)Optional.of(T t) : 创建⼀个 Optional 实例
(2)pty() : 创建⼀个空的 Optional 实例
(3)Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
(4)isPresent() : 判断是否包含值
(5)orElse(T t) : 如果调⽤对象包含值,返回该值,否则返回t
(6)orElseGet(Supplier s) :如果调⽤对象包含值,返回该值,否则返回 s 获取的值
(7)map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回pty()
(8)flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
1.创建optional对象,⼀般⽤ofNullable()⽽不⽤of():
(1)empty() :⽤于创建⼀个没有值的Optional对象:Optional<String> emptyOpt = pty();
(2)of() :使⽤⼀个⾮空的值创建Optional对象:Optional<String> notNullOpt = Optional.of(str);
(3)ofNullable() :接收⼀个可以为null的值:Optional<String> nullableOpt = Optional.ofNullable(str);
2.判断Null:
(1)isPresent():如果创建的对象实例为⾮空值的话,isPresent()返回true,调⽤get()⽅法会返回该对象,如果没有值,调⽤isPresent()⽅法会返回false,调⽤get()⽅法抛出NullPointerException异常。
3.获取对象:
(1)get()
4.使⽤map提取对象的值,如果我们要获取User对象中的roleId属性值,常见的⽅式是先判断是否为null然后直接获取,但使⽤Optional中提供的map()⽅法可以以更简单的⽅式实现
5.使⽤orElse⽅法设置默认值,Optional类还包含其他⽅法⽤于获取值,这些⽅法分别为:
(1)orElse():如果有值就返回,否则返回⼀个给定的值作为默认值;
(2)orElseGet():与orElse()⽅法作⽤类似,区别在于⽣成默认值的⽅式不同。该⽅法接受⼀个Supplier<? extends T>函数式接⼝参数,⽤于⽣成默认值;
(3)orElseThrow():与前⾯介绍的get()⽅法类似,当值为null时调⽤这两个⽅法都会抛出NullPointerException异常,区别在于该⽅法可以指定抛出的异常类型。
6.使⽤filter()⽅法过滤,filter()⽅法可⽤于判断Optional对象是否满⾜给定条件,⼀般⽤于条件过滤,在代码中,如果filter()⽅法中的Lambda表达式成⽴,filter()⽅法会返回当前Optional对象值,否则,返回⼀个值为空的Optional对象。:
四、Base64
Base64编码的作⽤:
由于某些系统中只能使⽤ASCII字符。Base64就是⽤来将⾮ASCII字符的数据转换成ASCII字符的⼀种⽅法。 Base64其实不是安全领域下的加密解密算法,⽽是⼀种编码,也就是说,它是可以被翻译回原来的样⼦。它并不是⼀种加密过程。所以base64只能算是⼀个编码算法,对数据内容进⾏编码来适合传输。虽然base64编码过后原⽂也变成不能看到的字符格式,但是这种⽅式很初级,很简单。
使⽤Base64编码原因:
1.base64是⽹络上最常见的⽤于传输8bit字节代码的编码⽅式之⼀。有时我们需要把⼆进制数据编码为适合放在URL中的形式。这时采⽤base64编码具有不可读性,即所编码的数据不会被⼈直接看出。
2.⽤于在http环境下传递较长的标识信息。
在Java 8中,Base64编码已经成为Java类库的标准,并内置了 Base64 编码的编码器和解码器。Base64⼯具类提供了⼀套静态⽅法获取下⾯三种BASE64编解码器:
基本:输出被映射到⼀组字符A-Za-z0-9+/,编码不添加任何⾏标,输出的解码仅⽀持A-Za-z0-9+/。
URL:输出映射到⼀组字符A-Za-z0-9+_,输出是URL和⽂件。
MIME:输出隐射到MIME友好格式。输出每⾏不超过76字符,并且使⽤'\r'并跟随'\n'作为分割。编码输出最后没有⾏分割。
五、接⼝的默认⽅法和静态⽅法
Java 8⽤默认⽅法与静态⽅法这两个新概念来扩展接⼝的声明。默认⽅法与抽象⽅法不同之处在于抽象⽅法必须要求实现,但是默认⽅法则没有这个要求,就是接⼝可以有实现⽅法,⽽且不需要实现类去实现其⽅法。我们只需在⽅法名前⾯加个default关键字即可实现默认⽅法。为什么要有这个特性?以前当需要修改接⼝的时候,需要修改全部实现该接⼝的类。⽽引进的默认⽅法的⽬的是为了解决接⼝的修改与现有的实现不兼容的问题。
默认⽅法语法格式如下:
public interface Vehicle {
default void print(){
System.out.println("我是⼀辆车!");
}
}
当出现这样的情况,⼀个类实现了多个接⼝,且这些接⼝有相同的默认⽅法,这种情况的解决⽅法:
1.是创建⾃⼰的默认⽅法,来覆盖重写接⼝的默认⽅法
2.可以使⽤ super 来调⽤指定接⼝的默认⽅法
Java 8 的另⼀个特性是接⼝可以声明(并且可以提供实现)静态⽅法。在JVM中,默认⽅法的实现是⾮常⾼效的,并且通过字节码指令为⽅法调⽤提供了⽀持。默认⽅法允许继续使⽤现有的Java接⼝,⽽同时能够保障正常的编译过程。尽管默认⽅法⾮常强⼤,但是在使⽤默认⽅法时我们需要⼩⼼注意⼀个地⽅:在声明⼀个默认⽅法前,请仔细思考是不是真的有必要使⽤默认⽅法,因为默认⽅法会带给程序歧义,并且在复杂的继承体系中容易产⽣编译错误。
六、⽅法引⽤
⽅法引⽤提供了⾮常有⽤的语法,可以直接引⽤已有Java类或对象(实例)的⽅法或构造器。与lambda联合使⽤,⽅法引⽤可以使语⾔的构造更紧凑简洁,减少冗余代码。
定义了4个⽅法的Car这个类作为例⼦,区分Java中⽀持的4种不同的⽅法引⽤。
public static class Car {
制作查询类小程序public static Car create( final Supplier< Car > supplier ) {
();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + String() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + String() );
}
public void repair() {
System.out.println( "Repaired " + String() );
}
}
第⼀种⽅法引⽤是构造器引⽤,它的语法是Class::new,或者更⼀般的Class< T >::new。请注意构造器没有参数。
final Car car = ate( Car::new );
final List< Car > cars = Arrays.asList( car );
第⼆种⽅法引⽤是静态⽅法引⽤,它的语法是Class::static_method。请注意这个⽅法接受⼀个Car类型的参数
cars.forEach( Car::collide );
第三种⽅法引⽤是特定类的任意对象的⽅法引⽤,它的语法是Class::method。请注意,这个⽅法没有参数。
cars.forEach( Car::repair );
第四种⽅法引⽤是特定对象的⽅法引⽤,它的语法是instance::method。请注意,这个⽅法接受⼀个Car类型的参数
final Car police = ate( Car::new );
cars.forEach( police::follow );
七、Stream
Java 8 API添加了⼀个新的抽象称为流Stream把真正的函数式编程风格引⼊到Java中,可以让你以⼀种声明的⽅式处理数据。Stream 使⽤⼀种类似⽤ SQL 语句从数据库查询数据的直观⽅式来提供⼀种对 Java 集合运算和表达的⾼阶抽象。Stream API极⼤简化了集合框架的处理,这种风格将要处理的元素集合看作⼀种流,流在管道中传输,并且可以在管道的节点上进⾏处理,⽐如筛选,排序,聚合等。
Stream流有⼀些特性:
1.Stream流不是⼀种数据结构,不保存数据,它只是在原数据集上定义了⼀组操作。
2.这些操作是惰性的,即每当访问到流中的⼀个元素,才会在此元素上执⾏这⼀系列操作。
3.Stream不保存数据,故每个Stream流只能使⽤⼀次。
所以这边有两个概念:流、管道。元素流在管道中经过中间操作的处理,最后由最终操作得到前⾯处理的结果。这⾥有2个操作:中间操作、最终操作。
中间操作:返回结果都是Stream,故可以多个中间操作叠加。
终⽌操作:⽤于返回我们最终需要的数据,只能有⼀个终⽌操作。
使⽤Stream流,可以清楚地知道我们要对⼀个数据集做何种操作,可读性强。⽽且可以很轻松地获取并⾏化Stream流,不⽤⾃⼰编写多线程代码,可以更加专注于业务逻辑。默认情况下,从有序集合、⽣成器、迭代器产⽣的流或者通过调⽤Stream.sorted产⽣的流都是有序流,有序流在并⾏处理时会在处理完成之后恢复原顺序。⽆限流的存在,侧⾯说明了流是惰性的,即每当⽤到⼀个元素时,才会在这个元素上执⾏这⼀系列操作。
使⽤Stream的基本步骤:
1.创建Stream
2.转换Stream,每次转换原有Stream对象不改变,返回⼀个新的Stream对象(可以有多次转换)
3.对Stream进⾏聚合操作,获取想要的结果
注:如果数据在1万以内的话,for循环效率⾼于foreach和stream;
如果数据量在10万的时候,stream效率最⾼,其次是foreach,最后是for。
另外需要注意的是如果数据达到100万的话,parallelStream异步并⾏处理效率最⾼,⾼于foreach和for。
⼀、流的⽣成⽅法
1.Collection接⼝的stream()或parallelStream()⽅法
2.静态的Stream.of()、pty()⽅法
3.Arrays.stream(array, from, to)
4.静态的ate()⽅法⽣成⽆限流,接受⼀个不包含引元的函数
5.静态的Stream.iterate()⽅法⽣成⽆限流,接受⼀个种⼦值以及⼀个迭代函数
6.Pattern接⼝的splitAsStream(input)⽅法
7.静态的Files.lines(path)、Files.lines(path, charSet)⽅法
8.静态的at()⽅法将两个流连接起来
⼆、流的Intermediate⽅法(中间操作)
1.filter(Predicate) :将结果为false的元素过滤掉
2.map(fun) :转换元素的值,可以⽤⽅法引元或者lambda表达式
3.flatMap(fun) :若元素是流,将流摊平为正常元素,再进⾏元素转换
4.limit(n) :保留前n个元素
5.skip(n) :跳过前n个元素
6.distinct() :剔除重复元素
7.sorted() :将Comparable元素的流排序
8.sorted(Comparator) :将流元素按Comparator排序
9.peek(fun) :流不变,但会把每个元素传⼊fun执⾏,可以⽤作调试
三、流的Terminal⽅法(终结操作)
(1)约简操作
(2)收集操作
1.iterator():
2.forEach(fun):
3.forEachOrdered(fun) :可以应⽤在并⾏流上以保持元素顺序
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论