JDK8的一些新特性整理、最近一直在看关于spring boot 和spring cloud的一些知识、
JDK8的一个明显的新特点就是lambda表达式。
一、lambda 表达式
学习过c/c++的应该对lambda不陌生、java中的lambda的形式一般为
(parameters) -> expression
或
(parameters) ->{statements; }
第一个()内的是lambda捕获的参数、而在->的部分是在lambda表达式的执行部分,可以理解成一个匿名的方法。
lambda的实例
创建两个接口
public interface MathOperation {
int operation(int a ,int b);
}
public interface GreetingService {
void sayMessage(String message);
}
public static void main(String[] args) {
LambdaTest lambdaTest = new LambdaTest();
//添加了类型声明
MathOperation addition = (int a,int b)->a+b;
//没有类型声明
MathOperation subtraction =(a,b)->a-b;
//大括号内的返回语句
MathOperation multi = (a,b)->(a*b);
//不在大括号内的返回语句
MathOperation div = (a,b)->a/b;
System.out.println("10 + 5 = "+lambdaTest.operator(10,5, addition ));
System.out.println("10 - 5 = "+lambdaTest.operator(10,5, subtraction ));
System.out.println("10 * 5 = "+lambdaTest.operator(10,5, multi ));
System.out.println("10 / 5 = "+lambdaTest.operator(10,5, div ));
// 在GreetingService添加方法mean(String s) ,void mean(String s,String t);会报错啊
// 看来在interface中的接口只能有一个
GreetingService greetingService = (s)->System.out.println(s);
greetingService.sayMessage( "hello" );
GreetingService greetingService2 = (message) -> System.out.println(salutation+message);
greetingService2.sayMessage( "david" );
}
private int operator(int a,int b,MathOperation mathOperation){
return mathOperation.operation(a,b);
}
从这个例子中可以看到lamdba定义了接口中执行的方法、而且lambda提供的方法接口不需要再创建匿名类,提升了java的编程效率。
public interface MathOperation {
int operation(int a ,int b);
int operation2(int a );
int operatione(int a,int b);
}
如果把接口添加方法、程序会报错no-overrding abstract methods found
需要注意的是又lambda提供接口方法的接口中只能有一个方法、无论在接口中添加接口方法参数是否改变、程序依然会报错.
还一个需要注意的地方是被lambda表达式引用的对象,需要是最终变量或是实际上的不被后面程序修改的最终变量
如:
public interface Converter<T1,T2> {
void convert(int i);
}
public class LambdaTest2 {
public static void main(String[] args) {
int num = 1;
// Converter<Integer,String> converter=(n,s)-> System.out.println((n+num)+s);
// converter.convert( 3,"add" );
Converter<Integer,String> converter = (n)->System.out.println(String.valueOf(n + num));
converter.convert( 3 );
// num=6; lambda所引用的变量必须为最终变量或实际上的最终的变量
}
}
如果不将num=6注释掉的话、运行时会报错,因为num被lambda表达式下方的程序修改了。
public static void main(String[] args) {
String first = "";
Compare<Integer> compare = (first,second) ->
System.out.println((Integer.compare( first.length(),second.length() )));
compare.com( "abc","def" );
}
当然lambda表达式中的捕获形参名称不能和程序中的局部变量名称相同。
二、JDK8中的方法引用
通过方法的名称来指出引用的方法
java中的方法引用是通过:: ,在方法作用域中引用。
@FunctionalInterface注解的作用是
标记的接口只能有一个抽象方法、
JDK8接口中的静态方法和默认方法,都不算是抽象方法、接口默认继承java.lang.Object。所以显示覆盖的父类的方法也不算抽象方法
public class Car {
@FunctionalInterface
public interface Supplier<T> {
T get();
}
//Supplier是jdk1.8的接口,这里和lamda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
public static void main(String[] args) {
//构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
Car car = Car.create(Car::new);
Car car1 = Car.create(Car::new);
Car car2 = Car.create(Car::new);
Car car3 = new Car();
List<Car> cars = Arrays.asList(car,car1,car2,car3);
//静态方法引用
cars.forEach(Car::collide);
System.out.println("===================静态方法引用========================");
cars.forEach(Car::repair);
System.out.println("==============特定类的任意对象的方法引用================");
List<String> list = new ArrayList<>( );
list.add( "first" );
list.add( "second" );
list.add( "third" );
list.add( "forth" );
list.forEach( System.out::println );
}
}
三、默认方法
jdk8中接口可以添加默认方法、只需要在方法前面添加一个default。
接口的好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类。默认方法是为了解决接口的修改与现有的实现不兼容的问题。
当一个类继承了多个接口且默认方法冲突的解决方法
public interface Vehicle {
default void print(){
System.out.println("我是一辆车");
}
static void blow(){
System.out.println("按喇叭");
}
}
public interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车");
}
}
public class Car implements Vehicle,FourWheeler {
// //指定调用的方法
// @Override
// public void print() {
// vehicle.super.print();
// }
// //两个继承的接口又重复方法
// //重写继承的方法
// @Override
// public void print() {
// System.out.println("我是一辆车");
// }
public void print(){
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blow();
System.out.println("我是一辆车");
}
public static void main(String[] args) {
Vehicle vehicle = new Car();
vehicle.print();
}
}
两种方法分别是继承类自己定义重写继承得方法、显示指定继承接口的默认方法;
四、函数接口式实例
函数式接口的特点:
函数式接口(FunctionalInterface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为lambda表达式。
函数式接口可以现有的函数友好地支持 lambda。
JDK8之后又引入了许多函数接口。
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果 |
BiPredicate<T,U> | 代表了一个两个参数的boolean值方法 |
Predicate <T> | 它接受一个输入参数 T,返回一个布尔值结果。 |
ToIntFunction<T> | 接受一个输入参数,返回一个int类型结果。 |
函数式接口的实例
利用Predicate <T> 、将lambda表达式中的判断是否为空来遍历所有元素
public class InterfaceJDK8Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9 );
// Predicate<Integer> predicate = n->true;
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有的数据");
eval( list,n->true ); //n->true 创建了一个匿名的lambda
// 输出所有的偶数
// 传入n到Predicate中、n能整出2的是偶数
System.out.println("输出所有的偶数");
eval( list,n->n%2==0 );
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
五、JDK8 steam 流
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素:是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
数据源 :流的来源。可以是集合,数组,I/O channel,产生器generator等。
聚合操作: 类似SQL语句一样的操作,比如filter, map, reduce, find,match, sorted等。
java中流的使用
生成串行流(stream())和并行流(parallelStream() )
public static void main(String[] args) {
//创建串行流
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
long begins = System.currentTimeMillis();
List<String> filtered = strings.stream().filter( string -> !string.isEmpty()).collect( Collectors.toList());
long ends = System.currentTimeMillis();
System.out.println("串行消耗时间"+(ends-begins)+"ms");
//创建并行流
List<String> stringp = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
long beginp = System.currentTimeMillis();
int countp = (int) stringp.parallelStream().filter(string -> string.isEmpty()).count();
long endp = System.currentTimeMillis();
System.out.println("并行消耗时间"+(endp-beginp)+"ms");
}
//打印
串行消耗时间82ms
并行消耗时间5ms
forEach()
// forEach 生成10个随机数, 并且打印 limit(n) 用于获取流的个数
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map() map 方法用于映射每个元素到对应的结果
// map方法,使用map方法一一对应结果映射
List<Integer> list = Arrays.asList( 3, 2, 2, 3, 7, 3, 5 );
List<Integer> squaresList = list.stream().map(i -> i * i).distinct().collect(Collectors.toList());
eval(squaresList,n->true);
filter() 用于通过设置条件过滤出元素
// filter方法 设置条件过滤出元素
List<String> stringss = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = (int) stringss.stream().filter(string -> !string.isEmpty()).count();
System.out.println("filter后的数量:"+count);
sorted () 于对流进行排序。默认排序是字典排序
// sorted 获取10个随机数、并把他们排序打印
Random randoms = new Random();
System.out.println("打印出排序的随机数");
randoms.ints().limit(10).sorted().forEach(System.out::println);
Collectors() 实现了很多归约操作,例如将流转换成集合和聚合元素,实现了很多归约操作,例如将流转换成集合和聚合元素
统计
List<Integer> numbers = Arrays.asList(3, -2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
六、JDK8 Optional类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。
Optional 类的引入很好的解决空指针异常。
Optional提供的方法
static <T> Optional<T> empty() | 返回空的 Optional 实例。 |
boolean equals(Object obj) | 判断其他对象是否等于 Optional |
Optional<T> filter(Predicate<? super <T> predicate) | 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Option Optional。 |
T get() | 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException |
void ifPresent(Consumer<? super T> consumer) | 如果值存在则使用该值调用 consumer , 否则不做任何事情 |
static <T> Optional<T> ofNullable(T value) | 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional |
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);
// Optional.ofNullable - 允许传递为 null 参数
Optional<Integer> a = Optional.ofNullable(value1);
// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a, b));
}
public Integer sum(Optional<Integer> a, Optional<Integer> b) {
// Optional.isPresent - 判断值是否存在
System.out.println("第一个参数值存在: " + a.isPresent());
System.out.println("第二个参数值存在: " + b.isPresent());
// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = a.orElse(new Integer(0));
//Optional.get - 获取值,值需要存在
Integer value2 = b.get();
return value1 + value2;
}
}
参考博客:https://blog.youkuaiyun.com/yitian_66/article/details/81010434