java1.8新特性
- Lambda表达式
- 方法引用
- Stream API
- 新时间日期API
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。
- Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
- Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API − 加强对日期与时间的处理。
- Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
lambda表达式
Lambda表达式可以看成是匿名内部类,Lambda允许把函数作为一个方法的参数(函数作为方法的参数),将代码像数据一样传递,使用Lambda表达式可以使代码变得更加简洁紧凑。
使用lambda表达式的注意事项
左侧是参数->右侧是方法体
形参列表的数据类型会自动推断
如果形参列表为空只需保留()
如果只有一个参数,可以将()省略,
如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,想省略{}则必须同时省略return,且执行语句也保证只有一句
lambda不会单独生成一个内部类文件
lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,那么系统会自用加,以后再修改局部变量会报错
函数式接口
如果一个接口只有一个抽象方法,则该接口称为函数式接口,函数式接口可以使用lambda表达式,lambda表达式会被匹配带这个抽象方法上。
为了确保你的每一个接口一定达到这个要求,你只需在接口是上添加@Functionallnterface注解,编译器会自动为你检查,多于一个抽象方法会报错。
函数式接口 | 参数类型 | 返回类型 | 说明 |
---|---|---|---|
Consumer 消费型接口 | T | void | void accept(T t);对类型为T的对象应用操作 |
Supplier 供给型接口 | 无 | T | T get(); 返回类型为T的对象 |
Function<T,R> 函数型接口 | T | R | R apply(T t);对类型为T的对象应用操作,并返回类型为R类型的对象。 |
Predicate 断言型接口 | T | boolean | boolean test(T t);确定类型为T的对象是否满足条件,并返回boolean类型。 |
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/*
* created by 夏晓林 in 2019/8/21/021 at 8:09
* lambda表达式的应用(四个核心函数式接口)
* Consumer<T>消费型接口 有参数没有返回值
* Supplier<T>供给型接口 没有参数有返回值
* Function<T,R>函数型接口 参数是T返回值是R
* Predicate<T>断言型接口 有参数,返回值是boolean型
*/
public class Demo2 {
public static void main(String[] args) {
happy(1000,
m-> System.out.println("吃烤全羊消费"+m)
);
happy(1000,m->System.out.print("消费"+m));
List<Integer> nums=getNumbers(10,
()-> new Random().nextInt(100)
);
for (Integer num : nums) {
System.out.println(num);
}
System.out.println(stringOpe("hello",
s-> s.toUpperCase()));
System.out.println(stringOpe("Wo", s->s.toLowerCase()));
List<String> names=new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六");
List<String> list=filterString(names,
s-> s.startsWith("张"));
List<String> list1=filterString(names,s->s.equals("张三"));
for (String s : list1) {
System.out.println(s);
}
}
//消费性接口
public static void happy(double money, Consumer<Double> consumer){
consumer.accept(money);
}
//供给型接口
public static List<Integer> getNumbers(int count, Supplier<Integer> supplier){
List<Integer> list=new ArrayList<>();
for (int i = 0; i <count ; i++) {
list.add(supplier.get());
}
return list;
}
//函数式接口
public static String stringOpe(String s, Function<String,String> function){
return function.apply(s);
}
//断言型接口
public static List<String> filterString(List<String> list, Predicate<String> predicate){
List<String> list1=new ArrayList<>();
for (String s : list) {
if(predicate.test(s)){
list1.add(s);
}
}
return list1;
}
}
方法引用
方法引用是lambda的一种简写方式。如果lambda表达式只是调用一个特定的已经存在的方法,则可以使用方法引用
- 对象::实例方法
- 类::静态方法
- 类::实例方法
- 类::new
import java.util.Comparator;
import java.util.TreeSet;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
/*
* created by 夏晓林 in 2019/8/21/021 at 22:21
* 方法引用:如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用
* 对象::实例方法
* 类::静态方法
* 类::实例方法
* 类::new
*
*/
public class Demo3 {
public static void main(String[] args) {
//方法引用 对象::方法名
Consumer<String> con=s-> System.out.println(s);
//等价于
Consumer<String> con1=System.out::println;
con.accept("hello");
con1.accept("world");
//类名::静态方法
Comparator<Integer> comparator=(x,y)->Integer.compare(x, y);
Comparator<Integer> comparator1=Integer::compare;
TreeSet<Integer> treeSet=new TreeSet<>(comparator1);
//类名::实例方法名
BiPredicate<String,String> bpre=(x,y)->x.equals(y);
BiPredicate<String,String> bpre1=String::equals;
//构造方法引用:
// Supplier<Employee> sup3=()->new Employee();
// Supplier<Employee> sup4=Employee::new;
}
}
理解Stream API
Stream是java8 中处理数组、集合的抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合进行操作,就类似于使用SQL执行的数据库查询。也可以使用stream API 来并行执行操作。
Stream API特点:
Stream 自己不会存储元素
Stream不会改变原对象,相反,他们会返回一个持有结果的新Stream
Stream操作时延迟执行的,这意味着他们会等到需要结果的时候才会执行
Stream遵循“做什么而不是怎么去做”的原则。只需要描述需要做什么,而不用考虑程序是怎么实现的。
使用Stream的三个步骤:
- 创建一个Stream(创建)
- 在一个或者是多个步骤中,将初始化Stream转化到另一个Stream的中间操作(中间操作)
- 使用一个终止操作来产生一个结果。该操作会强制他之前的延迟操作立即执行。在这之后,该Stream就不会再被使用了。(终止操作)
Stream的中间操作:
包括:map、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered
多个中间操作形成了一个流水线,除非流水线上触发终止操作,否则中间操作不会被执行的,而在终止操作时一次性全部处理,称为“惰性求值”
终止操作:
forEach forEachOrdered toArray rreduce collect min max count anyMatch allMatch noneMatch findFirst findAny iterator
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/*
* created by 夏晓林 in 2019/8/22/022 at 11:23
*/
public class Demo5 {
/*public static void main(String[] args) {
//Stream的创建方法
//Stream.of方法
Stream stream=Stream.of("a","b","c");
//Arrays.stream方法
String[] arr=new String[]{"a","b","c"};
stream =Stream.of(arr);
stream= Arrays.stream(arr);
//集合方法
List<String> list=Arrays.asList(arr);
stream=list.stream();
stream=list.parallelStream();//并行流
//创建无限流
//迭代
Stream<Integer> stream1=Stream.iterate(0, x->x+2);
stream1.limit(10).forEach(System.out::println);
//生成
Stream<Double>stream2=Stream.generate(()->Math.random());
stream2.limit(5).forEach(System.out::println);
}*/
public static void main(String[] args) {
List<Employee> employees=new ArrayList<>();
employees.add(new Employee("xxx",30,1000,"男"));
employees.add(new Employee("yyy",20,3000,"女"));
employees.add(new Employee("zzz",23,1200,"女"));
employees.add(new Employee("张三",42,2000,"男"));
employees.add(new Employee("李四",19,1900,"男"));
employees.add(new Employee("李四",19,1900,"男"));
//筛选和切片
System.out.println("--------filter排除------");
Stream<Employee> stream=employees.stream()
.filter(e->e.getAge()>25);
stream.forEach(System.out::println);
System.out.println("---------limit限制个数-----------");
Stream<Employee> stream1=employees.stream().limit(3);
stream1.forEach(System.out::println);
System.out.println("---------skip跳过元素,返回一个扔掉了前n个元素的流-----------");
Stream<Employee> stream3=employees.stream().skip(2);
stream3.forEach(System.out::println);
System.out.println("---------distinct筛选通过流所生成元素的equals()去除重复元素-----------");
Stream<Employee> stream4=employees.stream().distinct();
stream4.forEach(System.out::println);
//映射map--接收Lambda
//将元素转换为其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
System.out.println("-------获取员工姓名--------");
Stream<String> str=employees.stream().map(e->e.getName());
str.forEach(System.out::println);
System.out.println("------转换为大写-------");
List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
Stream<String> stream2=list.stream().map(String::toUpperCase);
stream2.forEach(System.out::println);
//排序
/*
* sorted()---自然排序
* sorted(Comparator com)----定制排序
* */
System.out.println("------sorted()自然排序--------");
employees.stream().map(Employee::getName)
.sorted()
.forEach(System.out::println);
System.out.println("定制排序");
employees.stream().sorted((x,y)-> {
if(x.getAge()==y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(),y.getAge() );
}
}
).forEach(System.out::println);
//
System.out.println("---------foreach------------");
employees.stream().forEach(System.out::println);
/*
* allMatch---检查是否匹配所有元素
* anyMatch---检查是否至少匹配一个元素
* noneMatch---检查是否没有匹配的元素
*findFirst---返回第一个元素
* findAny----返回当前流中的任意元素,默认是第一个
* count----返回流中元素的总个数
* max----返回流中的最大值
* min----返回流中最小值
* */
boolean b = employees.stream().allMatch(e -> e.getSex().equals("男"));
boolean b1=employees.stream().anyMatch(e->e.getSex().equals("女"));
boolean b2=employees.stream().noneMatch(e->e.getSex().equals("男"));//没有匹配的元素返回true
System.out.println(b);
System.out.println(b1);
System.out.println(b2);
Optional<Employee> first = employees.stream().findFirst();
Optional<Employee> any = employees.stream().findAny();
System.out.println(first.get());
System.out.println(any.get());
Optional<Employee> max=employees.stream().max((o1,o2)->o1.getAge()-o2.getAge());
System.out.println(max.get());
Optional<Double> min=employees.stream().map(Employee::getSalary).min(Double::compareTo);
System.out.println(min.get());
//归纳和收集
/*
* reduce归约 reduce(T identity, BinaryOperator)/reduce(BinaryOperator)
* 可以将流中的元素反复结合起来,得到一个值
* */
List<Integer> list1=Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum=list1.stream().reduce(0,(x,y)->x+y );//第一个参数是初始值,第二个运算法则
System.out.println(sum);
System.out.println("--------------------");
Optional<Double> op = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op.get());
/*
* collect---将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中的元素做汇总的方法
*
* */
List<String> list2 = employees.stream().map(Employee::getName).collect(Collectors.toList());
list2.forEach(System.out::println);
System.out.println("--------Set集合----------");
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
set.forEach(System.out::println);
}
}
理解新时间日期API
旧版时间API存在的问题:
- 非线程安全—java.util.Date是非线程安全的,所有日期类都是可变的,这是java日期类最大的问题之一
- 设计差—java的日期时间类的定义不一致,在util和sql包中都有日期类,此处用于格式化和解析的类在java.text包中定义。java.util.Date同时含有日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包中并不合理。另外这两个类具有相同的名字,本身就是一个很糟糕的设计
- 时区处理麻烦–日期类并不提供国际化,没有时区支持,因此java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题
java8在java.time包中提供了很多新的API,以下为两个比较重要的API:
- Local(本地)简化了日期时间的处理,没有时区的问题
- Zoneld(时区)通过定制的时区处理日期时间
LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日 历系统的日期、时间 、日期和时间。它们提供了简单的日期或时间,不包含与时区相关的信息。
Instant 时间戳 类似以前的Date、Timestamp
它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算
TemporalAdjuster : 时间校正器。有时我们可能需要获 取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常 用 TemporalAdjuster 的实现。
java.time.format.DateTimeFormatter 类:该类提供了三种 格式化方法:
预定义的标准格式
语言环境相关的格式
自定义的格式
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
/*
* created by 夏晓林 in 2019/8/22/022 at 15:33
*/
public class Demo7 {
/*public static void main(String[] args) {
//当前时间
LocalDateTime ldt=LocalDateTime.now();
System.out.println(ldt);
//其他时间
LocalDateTime ldt2=LocalDateTime.of(2012,10,1,2,3,4);
System.out.println(ldt2);
//加时间
LocalDateTime ldt3=ldt2.plusDays(2);
System.out.println(ldt3);
//减时间
LocalDateTime ldt4=ldt3.minusDays(2);
System.out.println(ldt4);
//获取时间部分
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
}*/
public static void main(String[] args) throws Exception{
//时间戳
Instant instant=Instant.now();
System.out.println(instant);
Thread.sleep(1000);
Instant instant1=Instant.now();
long mills= Duration.between(instant, instant1).toMillis();
System.out.println(mills);
//时区
System.out.println(ZoneId.getAvailableZoneIds());
ZoneId zoneId=ZoneId.of("Europe/Berlin");
ZoneId zoneId2=ZoneId.of("Brazil/East");
System.out.println(zoneId.getRules());
System.out.println(zoneId2.getRules());
System.out.println("-----------Date--Instant---LocalDateTime-------");
Date date=new Date();
Instant instant2=date.toInstant();
LocalDateTime localDateTime = instant2.atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(localDateTime);
System.out.println("--------LocalDateTime---Instant----Date--------");
Instant instant3 = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date date1 = Date.from(instant3);
System.out.println(date1);
}
}
来源:https://blog.csdn.net/yandaxiaxiaolin/article/details/100018727