Java 8 新特性
1.简介
2.新特性优点
3.并行流与串行流
4.lombda表达式
5.函数式接口
6.方法引用和构造器引用
7.强大的Stream API
1.简介
java 8又称(jdk1.8),是java语言开发的一个主要版本
Java 8是oracle公司于2014年3月发布的,可以看成是自Java5以来最具有革命性的版本
Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性
2.新特性优点
- 速度更快
- 代码更少(增添新的表达式 : Lombad表达式)
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常: Optional
- Nashorn引擎,允许在JVM上运行JS
3.并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流.相比较串行的流,并行的流可以很大程度上提高程序的执行效率
Java 8对并行进行了优化,我们可以很容易对数据进行并行操作
Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流 之间进行切换
并行流 75
/**
* 计算一个1-100000之间的总和
* 并行
*/
@Test
public void parallel(){
//开始
long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(1L, 100000000L).parallel().sum();
System.out.println(sum);
long end = System.currentTimeMillis();
//结束
System.out.println(end-start);
}
for 373
/**
* 计算一个1-100000000之间所有整数和
* for
* */
@Test
public void forParallel(){
long start = System.currentTimeMillis();
Long sum = 0L;
for (long i = 0L; i <= 100000000L; i++) {
sum+=i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println(end-start);
}
当数值过大时使用串行流进行计算比基础的for循环所花费的时间更短.
4.lombda表达式
为什么使用lambda
lambda是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码(见代码想数据一样可以传递)
使用它可以写出更简洁,更灵活的代码,作为一种更紧凑的代码风格,是Java表达能力得到了提升
重要特性
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义
可选的大括号:如果主题只包含了一个语句,就不需大括号
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回,大括号需要指定表达式返回一个数值
匿名函数与Lombda转换
匿名函数
//
@Test
public void test(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("sss");
}
};
}
Lombda
@Test
public void test(){
Runnable runnable = () -> System.out.println("sss");
}
匿名函数
@Test
public void test2(){
TreeSet<String> strings = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(),o2.length());
}
});
}
Lombda
@Test
public void test2(){
TreeSet<String> strings = new TreeSet<String>((o1,o2)->Integer.compare(o1.length(),o2.length()));
}
语法
Lombda表达式: 在Java 8语言中引进了一种新的语法元素和操作符.这个操作符为"->"
该操作符被称为Lombda操作符活箭头操作符.
它将Lombda表达式分为两个部分:
左侧:指定了Lombda表达式需要的参数列表
右侧:指定了Lombda体,是抽象方法的实现逻辑,也即Lombda表达式要执行的功能
Lombda表达式语法 |
语法格式一:无参无返回值 Runnable r1 = () -> System.out.println(); |
语法格式二:有参无返回值 Consumer con = (String s) -> System.out.println(s); |
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” Consumer con = (s) -> System.out.print(s); |
语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 Consumer con = s -> System.out.print(s); |
语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 Comparator com = (x,y) -> { } |
语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 Comparator com = (x,y) -> Intger.compare(x,y); |
简单例子
//1.不需要返回值,返回值为5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x,y) -> x-y
// 4. 接收2个int型整数,返回他们的和
(int x,int y) -> x-y
// 5. 接受一个 string 对象,并在控制台打印,
// 不返回任何值(看起来像是返回void)
(String x) -> System.out.print(x)
类型推断
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。
Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序 的上下文,在后台推断出了参数的类型。
Lambda 表达式的类型依赖于 上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。
5.函数式接口
什么是函数式接口
只包含一个抽象方法的接口,称为函数式接口
函数式接口可以隐式转换成Lombda表达式
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检 查它是否是一个函数式接口。同时 javadoc 也会包含一条声 明,说明这个 接口是一个函数式接口。
@FunctionalInterfaceinterface
GreetingService {
void sayMessage(String message);
}
如何理解函数式接口
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。
这就是 Lambda表达式和函数式接口的关系。
也就是说,只要一个对象是函数式接口 的实例,那么该对象就可以用Lambda表达式来表示。
函数式接口实例
//Predicate <T> 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。
public class Functional {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
System.out.println("输出所有数据");
//n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
eval(list,n->true);
System.out.println("输出所有偶数---------------");
/*如果n%2等于0则test返回true*/
eval(list,n->n%2==0);
/*如果n>3则test返回true*/
System.out.println("输出所有大于3的数字-------------");
eval(list,n->n>3);
}
public static void eval(List<Integer> list, Predicate<Integer> predicate){
for (Integer n : list) {
if(predicate.test(n)){
System.out.println(n+"");
}
}
}
}
四大核心函数式接口
Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回值类型 | 用途 |
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法: T get() |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果.结果是R类型的对象. 包含方法 : R apply(T t) |
Predicate 断定型接口 | T | boolean | 确定类行为T的对象是否满足某约束,并返回Boolean值. 包含方法: Boolean test(T t) |
6.方法引用和构造器引用
什么是方法引用
方法引用是对Lambda表达式符合某种情况下的一种缩写,使得我们的Lambda表达式更加的精简, 也可以理解为Lambda表达式 的另一种表现形式(缩写)
什么时候使用
当要传递给Lambda体内的操作,已经有实现的方法了,就可以使用方法引用了
前提条件:
1.方法引用所引用的方法的参数列表必须和函数式接口中抽象方法的参数列表一致(完全一致)
2.方法引用所引用的方法的返回值必须和函数式接口中抽象方法的返回值相同(完全一致)
语法格式
三种格式:
1.实例化对象名::实例化方法名
2.类名::静态方法名
3.类名::实例化方法名
我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
package cn.zed;
@FunctionalInterfacepublic
interface Quote<T> {
T get();
}
class car {
public static car create1(final Quote<car> carQuote){
return carQuote.get();
}
public static void create2(final car car){
System.out.println("create2"+car.toString());
}
public void create3(final car car){
System.out.println("create3"+car.toString());
}
public void create4(){
System.out.println("create4"+this.toString());
}
}
构造器引用:它的语法是Class::new,或者Class< T >::new
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
静态方法引用:它的语法是class::static_method
cars.foreach(Car::create2)
特定类的任意对象的方法引用:它的语法是Class::method
cars.forEach( Car::create3 );
特定对象的方法引用:它的语法是instance::method
final Car police = Car.create( Car::new );
cars.forEach( police::create4 );
方法引用实例
package cn.zed;
import java.util.ArrayList;public class oooo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.forEach(System.out::println);
}
}
数组引用
格式:type[]::new
7.强大的Stream API
Stream说明
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进 行的操作,可以执行非常复杂的查找、过滤和映射数据等 操作。 使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。 简言之,Stream API 提供了一种 高效且易于使用的处理数据的方式。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。
这 是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程 序员的生产力,让程序员写出高效率、干净、简洁的代 码。
为什么要使用Stream
实际开发中,项目中多数数据源都来自于Mysql,Oracle等。
但现在数 据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要 Java层面去处理。
Stream和collection集合的区别
collection是一种静态的内存数据结构,而Stream是有关计算的.
前者主要面向内存,储存在内存中
后者主要面向CPU,通过CPU实现计算
什么是Stream
是数据渠道,用于操作数据源(数组,集合等)所产生的元素序列
“集合讲的是数据,Stream讲的是计算!”
注意
- Stream自己不会存储元素
- Stream不会改变源对象,相反,它会返回一个持有结果的新Stream
- Stream的操作时延迟执行的,这意味这他们会等到需要结果的时候才执行
常用生成流的方式
1.使用Collection下的 stream() 和 parallelStream() 方法
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流Stream<String>
parallelStream = list.parallelStream(); //获取一个并行流
2.使用Arrays 中的 stream() 方法,将数组转成流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
3.使用Stream中的静态方法:of()、iterate()、generate()
//生成一个有限流Stream<Integer>
stream = Stream.of(1,2,3,4,5,6);
//根据条件 迭代去 生成无限流,参数1:迭代的起点,参数2:条件
Stream<Integer> it = Stream.iterate(1, x -> x + 2);
it.limit(10).forEach(System.out::println);
//根据条件去生成无限流 参数:生成条件
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
4.使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\AAA.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);