JDK8
JDK8
Oracle 于 2014 发布了 Java8(jdk1.8)
Interface的默认方法
因为interface修改后,实现类也必须要修改。
为了优化这个问题, interface 的方法可以用default 或 static修饰,这样就可以有方法体,实现类也不必重写此方法。
- default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
- static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
public interface InterfaceNew {
static void stc() {
System.out.println("interface提供的方式实现");
}
default void def() {
System.out.println("interface default方法");
}
//须要实现类重写
void f();
}
functional interface 函数式接口
也称 SAM 接口,即 Single Abstract Method interfaces,有且只有一个抽象方法,但可以有多个非抽象方法的接口。
函数式接口有@FunctionalInterface
的注解,提供函数式编程。在Lambda表达式中经常使用。
Lambda 表达式
Lambda 表达式是一个匿名函数,java 8 允许把函数作为参数传递进方法中。
语法格式
(parameters) -> expression 或
(parameters) ->{ statements; }
常用案例
1.Runnable 接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("guan feng!");
}
}).start();
//用lambda
new Thread(() -> System.out.println("guan feng!")).start();
2.Comparator 接口
List<Integer> strings = Arrays.asList(1, 2, 3);
Collections.sort(strings, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;}
});
//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
//分解开
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 - o2;
Collections.sort(strings, comparator);
3.Listener 接口
Button button = new Button();
button.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
e.getItem();
}
});
//lambda
button.addItemListener(e -> e.getItem());
4.集合迭代
void lamndaFor() {
List<String> strings = Arrays.asList("1", "2", "3");
//传统foreach
for (String s : strings) {
System.out.println(s);
}
//Lambda foreach
strings.forEach((s) -> System.out.println(s));
//or
strings.forEach(System.out::println);
//map
Map<Integer, String> map = new HashMap<>();
map.forEach((k,v)->System.out.println(v));
}
5.方法调用
Java 8 允许使用 :: 关键字来传递方法或者构造函数引用,无论如何,表达式返回的类型必须是 functional-interface。
public class LambdaClassSuper {
LambdaInterface sf(){
return null;
}
}
public class LambdaClass extends LambdaClassSuper {
public static LambdaInterface staticF() {
return null;
}
public LambdaInterface f() {
return null;
}
void show() {
//1.调用静态函数,返回类型必须是functional-interface
LambdaInterface t = LambdaClass::staticF;
//2.实例方法调用
LambdaClass lambdaClass = new LambdaClass();
LambdaInterface lambdaInterface = lambdaClass::f;
//3.超类上的方法调用
LambdaInterface superf = super::sf;
//4. 构造方法调用
LambdaInterface tt = LambdaClassSuper::new;
}
}
Stream
Stream流可以检索和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。
流类型
- stream 串行流
- parallelStream 并行流,可多线程执行
filter
过滤方法filter用于对Stream中的元素进行筛选,只保留符合指定条件的元素。其函数式接口为Predicate<T>
,其方法为boolean test(T t),接受一个T类型的对象,并返回一个boolean类型值。当该方法返回true时,说明该元素符合条件,将被保留在Stream 中
List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().filter(s -> s.length() > 5);
其中的lamdba表达式s -> s.length() > 5
用于筛选长度大于5的字符串。
map
映射方法map
用于将Stream中的元素根据指定规则进行转换。其函数式接口为Function<T, R>
,其方法为R apply(T t)
,接受一个T类型的对象,并返回一个R类型的对象。实质上,map方法就是对Stream中各元素做一个同类型的映射。
List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<Integer> stream = list.stream().map(String::length);
sorted
排序方法sorted用于对Stream中的元素进行排序。其函数式接口为Comparator<T>
,其方法为int compare(T o1, T o2),接受两个T类型的对象,并返回一个int类型值。当返回值为负数时,说明o1应排在o2前面;当返回值为正数时,说明o1应排在o2后面;当返回值为0时,说明o1与o2的顺序不确定。
List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().sorted();
distinct
去重方法distinct
用于将Stream中的重复元素去除,只保留一个。其使用equals方法进行比较,因此需要保证数据源中的元素正确实现了equals方法。
List<String> list = Arrays.asList("apple", "banana", "orange", "banana");
Stream<String> stream = list.stream().distinct();
count
计数方法count
用于返回Stream中元素的数量,返回值为long
类型。
List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
long count = list.stream().count();
reduce
归约方法reduce用于将Stream中的元素归约成一个值。其函数式接口为BinaryOperator<T>
,其方法为apply(T t1, T t2),用于对两个T类型值进行归约,返回一个T类型值。reduce方法接受两个参数:第一个参数表示归约操作的初始值,可以为任意类型的对象;第二个参数为一个BinaryOperator类型的对象,用于对Stream中所有元素递归地进行归约操作。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, Integer::sum);
forEach
forEach
方法用于对Stream中的每个元素执行指定的操作,其函数式接口为Consumer<T>
,其方法为void accept(T t)
。forEach
是一个终端操作,对于同一个Stream只能进行一次,一旦执行了终端操作,该Stream就不能再重复使用了。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::println);
collect
collect
方法用于将流转为Collection中List等对象
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List res = list.stream().filter(s->s<3).collect(Colletors.toList());
并发流
@Test
public void parallelStreamTest(){
List<Integer> numbers = Arrays.asList(1, 2, 5, 4);
numbers.parallelStream() .forEach(num->System.out.println(Thread.currentThread().getName()+">>"+num));
}
//执行结果
main>>5
ForkJoinPool.commonPool-worker-2>>4
ForkJoinPool.commonPool-worker-11>>1
ForkJoinPool.commonPool-worker-9>>2
Optional
Optional是为了防止出现NullPointerException的。
出现NullPointerException的情况:
- 1) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
- 2) 数据库的查询结果可能为 null。
- 3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
- 4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
- 5) 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
- 6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
传统解决NPE的方式:
Zoo zoo = getZoo();
if(zoo != null){
Dog dog = zoo.getDog();
if(dog != null){
int age = dog.getAge();
System.out.println(age);
}
}
Optional 是这样的实现的:
Optional.ofNullable(zoo).map(o -> o.getDog()).map(d -> d.getAge()).ifPresent(age ->
System.out.println(age)
);
Date-Time API
解决了 Date 类的大部分痛点:
- 非线程安全
- 时区处理麻烦
- 各种格式化、和时间计算繁琐
- 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。
java.time 主要类java.util.Date 既包含日期又包含时间,而 java.time 把它们进行了分离
LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss
格式化
public void newFormat(){
//format yyyy-MM-dd
LocalDate date = LocalDate.now();
System.out.println(String.format("date format : %s", date));
//format HH:mm:ss
LocalTime time = LocalTime.now().withNano(0);
System.out.println(String.format("time format : %s", time));
//format yyyy-MM-dd HH:mm:ss
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateTimeStr = dateTime.format(dateTimeFormatter);
System.out.println(String.format("dateTime format : %s", dateTimeStr));
}
字符串转日期格式
LocalDate date = LocalDate.of(2021, 1, 26);
LocalDate.parse("2021-01-26");
LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime.parse("2021-01-26 12:12:22");
LocalTime time = LocalTime.of(12, 12, 22);
LocalTime.parse("12:12:22");
日期计算
public void pushWeek(){
//一周后的日期
LocalDate localDate = LocalDate.now();
//方法1
LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);
//方法2
LocalDate after2 = localDate.plusWeeks(1);
System.out.println("一周后日期:" + after);
//算两个日期间隔多少天,计算间隔多少年,多少月
LocalDate date1 = LocalDate.parse("2021-02-26");
LocalDate date2 = LocalDate.parse("2021-12-23");
Period period = Period.between(date1, date2);
System.out.println("date1 到 date2 相隔:"
+ period.getYears() + "年"
+ period.getMonths() + "月"
+ period.getDays() + "天");
//打印结果是 “date1 到 date2 相隔:0年9月27天”
//这里period.getDays()得到的天是抛去年月以外的天数,并不是总天数
//如果要获取纯粹的总天数应该用下面的方法
long day = date2.toEpochDay() - date1.toEpochDay();
System.out.println(date1 + "和" + date2 + "相差" + day + "天");
//打印结果:2021-02-26和2021-12-23相差300天
}
获取指定日期
public void getDayNew() {
LocalDate today = LocalDate.now();
//获取当前月第一天:
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
// 取本月最后一天
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
//取下一天:
LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
//当年最后一天
LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
//2021年最后一个周日,如果用Calendar是不得烦死。
LocalDate lastMondayOf2021 = LocalDate.parse("2021-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}