已更新至完整版
文章目录
- 通俗易懂的Java8-Lambda与StreamAPI与新DateAPI
- 接口的默认方法(Default Methods for Interfaces)
- Lambda表达式(Lambda expressions)
- 函数式接口(Functional Interfaces)
- Lambda表达式的几种常用情况
- 内置的四种基本接口
- 引用
- StreamAPI
- 创建流的几种方式
- 中间操作_筛选/切片
- 映射
- 排序
- 查找、匹配
- 规约
- 新的DateAPI
- Clock
- Timezones(时区)
- LocalDate(本地日期)
- DateTimeFormatter
Java 8使我们能够通过使用 default
关键字向接口添加非抽象方法实现。 此功能也称虚拟扩展方法
interface Formula {
double calculate(int a);
//接口的默认实现方法
default double sqrt(int a) {
return Math.sqrt(a);
}
}
主函数:
public class GoJava8 {
public static void main(String[] args) {
// 通过匿名内部类方式访问接口
Formula formula = new Formula() {
//实现接口方法
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
System.out.println(formula.calculate(100)); // 100.0
//接口默认实现方法可以直接调用
System.out.println(formula.sqrt(16)); // 4.0
}
}
formula 是作为匿名对象实现的,我们可以这样理解:一个内部类实现了接口里的抽象方法并且返回一个内部类对象,之后我们让接口的引用来指向这个对象
关于匿名内部类,这不是Java8的新特性,如果你没有掌握,参考我之前写的
Java基础-内部类与匿名内部类总结笔记
首先来看看,我们之前是如果对集合进行比较的
//要排序的字符串
List<String> names = Arrays.asList("bb","aa","cc") ;
//传统方式,匿名内部类
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//字符串的比较方法
return o1.compareTo(o2);
}
});
:通过匿名内部类,实现比较器Comparator接口
如果使用Lambda表达式呢?
Collections.sort(names, (a,b)->a.compareTo(b));
只需要一行代码即可实现
Lambda表达式的基本语法为:
使用Lambda表达式需要函数式接口的支持
“函数式接口”是指仅仅只包含一个抽象方法,但是可以有多个非抽象方法(也就是上面提到的默认方法)的接口
像这样的接口,可以被隐式转换为lambda表达式,java.lang.Runnable
与 java.util.concurrent.Callable
是函数式接口最典型的两个例子
Java 8增加了一种特殊的注解@FunctionalInterface
,但是这个注解通常不是必须的(某些情况建议使用),只要接口只包含一个抽象方法,虚拟机会自动判断该接口为函数式接口
一般建议在接口上使用@FunctionalInterface
注解进行声明,这样的话,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会进行报错
注意:
在上述代码中,Comparator就是一个函数式接口
1.无参数,无返回值的Lambda
Runnable runnable = ()-> System.out.println("it is run method");
runnable.run();
@FunctionalInterface
public interface Runnable { //jdk提供
public abstract void run();
}
2.有一个参数,无返回值(小括号可省略)
Consumer consumer = (x)-> System.out.println(x) ;
consumer.accept("it accept method");
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
3.有两个及以上参数,有返回值
Collections.sort(names, (a,b)->a.compareTo(b));
这个就是
可以看下比较器接口(sort的第二个参数)
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
...
}
函数式接口我们一般无需自己定义,根据返回值和参数的不同,函数式接口的形式是有限的,每次要用lambda都去写个函数式接口那不是更麻烦了吗
1.消费型接口-Consumer
2.提供型接口
无参有返回值
模拟返回随机数
Supplier<Integer> supplier1 = ()->(int)(Math.random()*10);
System.out.println(supplier1.get());
@FunctionalInterface
public interface Supplier<T> {
T get();
}
注意,在有返回值的情况下,如有多句语句,必须写成:
Supplier<Integer> supplier1 = ()->{
System.out.println("hehe");
return (int)(Math.random()*10);//一定要有return
};
System.out.println(supplier1.get());
3.函数型接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
String str = "abcdefg" ;
Function<String,String> function = (st)->st.substring(1,3);
System.out.println(function.apply(str));
典型的传入参数与返回参数型接口
其他函数式接口:
4.断言型接口
Predicate<Integer> predicate = (i)->i>=35 ;
System.out.println(predicate.test(25)); ;
@FunctionalInterface
public interface Predicate<T> {
//传入参数,返回布尔值
boolean test(T t);
}
若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
分为:
/*对象::实例方法
* 要求:实现的方法返回值与参数与函数式接口中的方法保持一致
* */
PrintStream out = System.out;//PrintStream中实现了println方法
Consumer<String> consumer = out::println; //实例::方法名
consumer.accept("hello");
/*类::静态方法
* 要求:实现的方法返回值与参数与函数式接口中的方法保持一致
* */
Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
System.out.println(comparator.compare(2,1));
Comparator<Integer> comparator1 = Integer::compare ;
System.out.println(comparator1.compare(1,2));
/*类::实例方法
* Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
* 如 "ab".equal("cd")
* */
BiPredicate<String,String > biPredicate = String::equals ;
System.out.println(biPredicate.test("ab","cd"));
/*构造器引用
* className::new
* */
//传统
Supplier<List> supplier = ()-> new ArrayList<>();
//引用
supplier = ArrayList::new ;
流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
集合讲数据,流讲计算
注意:
Stream操作的三个步骤
/*创建流的几种方式*/
//集合流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
//数组流
String[] strings = {"aa","bb","cc"} ;
Stream<String> stream1 = Arrays.stream(strings);
//Stream类 -> of方法 返回的其实就是Arrays.stream(strings)
Stream<String> stream2 = Stream.of("aa", "bb", "cc");
/*
* 无限流 seed:the initial element
* */
Stream<Integer> iterate = Stream.iterate(0, (i) -> ++i+i++);
iterate.forEach(System.out::println);
/*生成流*/
Stream.generate(()->Math.random())
.limit(3)
.forEach(System.out::println);
实例:
//模拟数据库->表
class Employee{
Integer id ;
String name ;
Integer age ;
double sal ;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sal=" + sal +
'}';
}
public Employee(Integer id, String name, Integer age, double sal) {
this.id = id;
this.name = name;
this.age = age;
this.sal = sal;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public double getSal() {
return sal;
}
}
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
emps.stream()
.filter((x)->x.getAge()>20)
.limit(3)
.distinct()
.skip(1)
.forEach(System.out::println);
结果:
Employee{id=104, name=‘Tom’, age=44, sal=1111.11}
Employee{id=105, name=‘Jerry’, age=60, sal=4444.44}
List<String> list = Arrays.asList("bb","aa","cc") ;
list.stream()
.map((str)->str.toUpperCase())
.forEach(System.out::println);
result:
BB
AA
CC
sorted():自然排序
sorted(Comparator c):定制排序
list.stream()
.sorted()
.forEach(System.out::println);
emps.stream()
.sorted((e1,e2)->{
return e1.getAge().compareTo(e2.getAge()) ;
})
.forEach(System.out::println);
List<Status> statuses = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION) ;
boolean flag1 = statuses.stream()
.allMatch((x)->x.equals(Status.BUSY));
System.out.println(flag1);
boolean flag2 = statuses.stream()
.anyMatch((x)->x.equals(Status.BUSY));
System.out.println(flag2);
Optional<Status> first = statuses.stream()
.findFirst();
System.out.println(first);
public enum Status{
FREE,BUSY,VOCATION ;
}
归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
List<Integer> list1 = Arrays.asList(1,2,3,4) ;
Integer reduce = list1.stream()
.reduce(3, (x, y) -> x - y);//x代表3,y代表list中数字
System.out.println(reduce); //-7
System.currentTimeMillis()
来获取当前的微秒数。某一个特定的时间点也可以使用 Instant
类来表示,Instant
类也可以用来创建旧版本的java.util.Date
对象。ZoneId
(在java.time
包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds
的静态方法,它返回所有区域标识符。Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis()
来获取当前的微秒数。某一个特定的时间点也可以使用 Instant
类来表示,Instant
类也可以用来创建旧版本的java.util.Date
对象
Clock clock = Clock.systemDefaultZone() ;
System.out.println(clock);
System.out.println(System.currentTimeMillis());
long millis = clock.millis();
System.out.println(millis);
Instant instant = clock.instant() ;
System.out.println(instant);
Date from = Date.from(instant);
System.out.println(from);
在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类ZoneId
(在java.time
包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds
的静态方法,它返回所有区域标识符。
/*Timezones*/
//所有区域标识符
System.out.println(ZoneId.getAvailableZoneIds());
ZoneId zoneId = ZoneId.of("Asia/Aden") ;
System.out.println(zoneId.getRules()); //有偏移量
LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例,这保证了线程的安全性
/*LocalData*/
LocalDate now = LocalDate.now();
System.out.println(now);
LocalDate tomorrow = now.plus(1, ChronoUnit.DAYS);
System.out.println(tomorrow);
LocalDate yest = now.minusDays(1);
System.out.println(yest);
LocalDate date = LocalDate.of(2020, 6, 18);
System.out.println(date);
String str = "2020==06==18 03时06分09秒" ;
// 根据需要解析的日期、时间字符串定义解析所用的格式器
DateTimeFormatter fomatter = DateTimeFormatter
.ofPattern("yyyy==MM==dd HH时mm分ss秒");
LocalDateTime parse = LocalDateTime.parse(str,fomatter);
System.out.println(parse);
LocalDateTime now1 = LocalDateTime.now();
String format = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(now1);
System.out.println(format);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
System.out.println(dateTimeFormatter.format(now1));