1. Stream
java 新增了 java.util.stream
包,它和之前的流大同小异。之前接触最多的是资源流,比如java.io.FileInputStream
,通过流把文件从一个地方输入到另一个地方,它只是内容搬运工,对文件内容不做任何_CRUD_。
Stream
依然不存储数据,不同的是它可以检索(Retrieve)和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。
- 流类型
- stream 串行流
- parallelStream 并行流,
- 可多线程执行,只有集合才用。
- 不需要额外配置,直接用就行
1.1 常用方法
/**
* 返回一个串行流
*/
default Stream<E> stream()
/**
* 返回一个并行流
*/
default Stream<E> parallelStream()
/**
* 返回T的流
*/
public static<T> Stream<T> of(T t)
/**
* 返回其元素是指定值的顺序流。
*/
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
/**
* 过滤,返回由与给定predicate匹配的该流的元素组成的流
*/
Stream<T> filter(Predicate<? super T> predicate);
/**
* 此流的所有元素是否与提供的predicate匹配。
*/
boolean allMatch(Predicate<? super T> predicate)
/**
* 此流任意元素是否有与提供的predicate匹配。
*/
boolean anyMatch(Predicate<? super T> predicate);
/**
* 返回一个 Stream的构建器。
*/
public static<T> Builder<T> builder();
/**
* 使用 Collector对此流的元素进行归纳
*/
<R, A> R collect(Collector<? super T, A, R> collector);
/**
* 返回此流中的元素数。
*/
long count();
/**
* 返回由该流的不同元素(根据 Object.equals(Object) )组成的流。
*/
Stream<T> distinct();
/**
* 遍历
*/
void forEach(Consumer<? super T> action);
/**
* 用于获取指定数量的流,截短长度不能超过 maxSize 。
*/
Stream<T> limit(long maxSize);
/**
* 用于映射每个元素到对应的结果
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
/**
* 根据提供的 Comparator进行排序。
*/
Stream<T> sorted(Comparator<? super T> comparator);
/**
* 在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。
*/
Stream<T> skip(long n);
/**
* 返回一个包含此流的元素的数组。
*/
Object[] toArray();
/**
* 使用提供的 generator函数返回一个包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组。
*/
<A> A[] toArray(IntFunction<A[]> generator);
/**
* 合并流
*/
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
1.2 方法示例
List strings = Arrays.asList(“abc”, “def”, “gkh”, “abc”);
1.2.1 filter
返回符合条件的stream
Stream<String> stringStream = strings.stream().filter(s -> "abc".equals(s));
//计算流符合条件的流的数量
long count = stringStream.count();
1.2.2 forEach
forEach遍历->打印元素
strings.stream().forEach(System.out::println);
1.2.3 limit
获取到n个元素的stream
Stream<String> limit = strings.stream().limit(1);
1.2.4 toArray
比如转换成String[]
String[] array = strings.toArray(String[]::new);
1.2.5 map
对每个元素进行操作并返回新流
Stream<String> map = strings.stream().map(s -> s + "22");
1.2.5 sorted
排序并打印
strings.stream().sorted().forEach(System.out::println);
1.2.6 collect
把Steam流的元素通通放入容器中
List<String> collect = strings.stream().collect(Collectors.toList());
把list转为string,各元素用,号隔开
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(","));
1.2.7 对数组统计
//对数组的统计,比如用
List<Integer> number = Arrays.asList(1, 2, 5, 4);
IntSummaryStatistics statistics = number.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : "+statistics.getMax());
System.out.println("列表中最小的数 : "+statistics.getMin());
System.out.println("平均数 : "+statistics.getAverage());
System.out.println("所有数之和 : "+statistics.getSum());
1.2.8 concat
合并流
List<String> strings2 = Arrays.asList("xyz", "jqx");
Stream.concat(strings2.stream(),strings.stream()).count();
1.2.9 注意
一个Stream只能操作一次,不能断开,否则会报错。
Stream stream = strings.stream();
//第一次使用
stream.limit(2);
//第二次使用
stream.forEach(System.out::println);
//报错
正确
stream.limit(2).forEach(System.out::println);
2. Optional
身为一名Java程序员,大家可能都有这样的经历: 调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数。
其可以避免一些空指针异常。
防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
- 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。 - 数据库的查询结果可能为 null。
- 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
- 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
- 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
- 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。
2.1 常用方法
2.1.1 of
为非null的值创建一个Optional,入参为null跑NPE
//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);
2.1.2 ofNullable
为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。
//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);
2.1.3 isPresent
如果值存在返回true,否则返回false。
//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
//在Optional实例内调用get()返回已存在的值
System.out.println(name.get());//输出Sanaulla
}
2.1.4 get
如果Optional有值则将其返回,否则抛出NoSuchElementException。
//执行下面的代码会输出: No value present
try {
//在空的Optional实例上调用get(),抛出NoSuchElementException
System.out.println(empty.get());
} catch (NoSuchElementException ex) {
System.out.println(ex.getMessage());
}
2.1.5 ifPresent
如果Optional实例有值则为其调用consumer,否则不做处理
//ifPresent方法接受lambda表达式作为参数。
//lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
System.out.println("The length of the value is: " + value.length());
});
2.1.6 orElse
如果有值则将其返回,否则返回指定的其它值。
//如果值不为null,orElse方法返回Optional实例的值。
//如果为null,返回传入的消息。
//输出: There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//输出: Sanaulla
System.out.println(name.orElse("There is some value!"));
2.1.7 orElseGet
orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:
//orElseGet可以接受一个lambda表达式生成默认值。
//输出: Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//输出: Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));
2.1.8 orElseThrow
如果有值则将其返回,否则抛出supplier接口创建的异常。
try {
empty.orElseThrow(NullPointException::new);
} catch (Throwable ex) {
//输出: No value present in the Optional instance
System.out.println(ex.getMessage());
}
2.1.9 map
如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional.of(new Father())
.map(father->{
father.setAge(100);
return father.getAge();
});
2.1.10 filter
filter个方法通过传入限定条件对Optional实例的值进行过滤。
//如果父亲的年龄大于30岁则保留,否则设置成默认30岁
Optional.of(new Father(10))
.filter(father->father.getAge()>30)
.orElse(new Father(31))
3. 时间API
Java 8之前的时间API非常拉胯,所以有了新的API
- 主要类
- Instant——它代表的是时间戳
- LocalDate——不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。
- LocalTime——它代表的是不含日期的时间
- LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。
3.1 方法概括
该包的API提供了大量相关的方法,这些方法一般有一致的方法前缀:
- of: 静态工厂方法。
- parse: 静态工厂方法,关注于解析。
- get: 获取某些东西的值。
- is: 检查某些东西的是否是true。
- with: 不可变的setter等价物。
- plus: 加一些量到某个对象。
- minus: 从某个对象减去一些量。
- to: 转换到另一个类型。
- at: 把这个对象与另一个对象组合起来,例如: date.atTime(time)
3.2 Clock
时钟提供给我们用于访问某个特定 时区的 瞬时时间、日期 和 时间的API
Clock.systemUTC(); //系统默认UTC时钟(当前瞬时时间 System.currentTimeMillis())
Clock.system(ZoneId.of("Asia/Shanghai"));//上海时区
Clock.systemXXX().millis(); //获取时间戳
3.3 时间格式化
- LocalDate
//format yyyy-MM-dd
LocalDate date = LocalDate.now();
System.out.println(String.format("date format : %s", date));
- LocalTime
//format HH:mm:ss
LocalTime time = LocalTime.now().withNano(0);
System.out.println(String.format("time format : %s", time));
- LocalDateTime
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));
3.4 字符串转时间
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");
3.5 日期计算
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(date2 + "和" + date2 + "相差" + day + "天");
//打印结果:2021-12-23和2021-12-23相差300天
}
3.6 获取指定日期
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));
}
3.7 获取不同单位时间
instant
//瞬时时间 相当于以前的System.currentTimeMillis()
Instant instant1 = Instant.now();
System.out.println(instant1.getEpochSecond());//精确到秒 得到相对于1970-01-01 00:00:00 UTC的一个时间
System.out.println(instant1.toEpochMilli()); //精确到毫秒
Clock clock1 = Clock.systemUTC(); //获取系统UTC默认时钟
Instant instant2 = Instant.now(clock1);//得到时钟的瞬时时间
System.out.println(instant2.toEpochMilli());
Clock clock2 = Clock.fixed(instant1, ZoneId.systemDefault()); //固定瞬时时间时钟
Instant instant3 = Instant.now(clock2);//得到时钟的瞬时时间
System.out.println(instant3.toEpochMilli());//equals instant1
3.7 其他
现在 jdbc 时间类型和 java8 时间类型对应关系是
Date
—>LocalDate
Time
—>LocalTime
Timestamp
—>LocalDateTime
而之前统统对应 Date
,也只有 Date