最后
各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了
| with | 否 | 以该 Temporal 对象为模板,对某些状态进行修改创建该对象的副本 |
LocalDate date = LocalDate.of(2014, 3, 18);
date = date.with(ChronoField.MONTH_OF_YEAR, 9);
date = date.plusYears(2).minusDays(10);
date.withYear(2011);
答案: 2016-09-08 。
每个动作都会创建一个新的 LocalDate 对象,后续的方法调用可以操纵前一方法创建的对象。这段代码的最后一句不会产生任何我们能看到的效果,因为它像前面的那些操作一样,会创建一个新的 LocalDate 实例,不过我们并没有将这个新创建的值赋给任何的变量。
Duration
用于比较 LocalTime 之间的时间差, Duration 类主要用于以秒和纳秒衡量时间的长短。
LocalTime time1 = LocalTime.now();
LocalTime time2 = LocalTime.of(11, 0, 0);
Duration d1 = Duration.between(time1, time2);
Period
用于比较 LocalDate 之间的时间差, Period 类主要用于以年月日衡量时间的长短。
Period tenDays = Period.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 3, 18));
Duration 和 Period 通用方法
| 方法名 | 是否是静态方法 | 方法描述 |
| — | — | — |
| between | 是 | 创建两个时间点之间的 interval |
| from | 是 | 由一个临时时间点创建 interval |
| of | 是 | 由它的组成部分创建 interval的实例 |
| parse | 是 | 由字符串创建 interval 的实例 |
| addTo | 否 | 创建该 interval 的副本,并将其叠加到某个指定的 temporal 对象 |
| get | 否 | 读取该 interval 的状态 |
| isNegative | 否 | 检查该 interval 是否为负值,不包含零 |
| isZero | 否 | 检查该 interval 的时长是否为零 |
| minus | 否 | 通过减去一定的时间创建该 interval 的副本 |
| multipliedBy | 否 | 将 interval 的值乘以某个标量创建该 interval 的副本 |
| negated | 否 | 以忽略某个时长的方式创建该 interval 的副本 |
| plus | 否 | 以增加某个指定的时长的方式创建该 interval 的副本 |
| subtractFrom | 否 | 从指定的 temporal 对象中减去该 interval |
TemporalAdjuster
将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的 with 方法, 向其传递一个提供了更多定制化选择的 TemporalAdjuster 对象,更加灵活地处理日期。
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(TemporalAdjusters.lastDayOfMonth());
| 方法名 | 描述 |
| — | — |
| dayOfWeekInMonth | 创建一个新的日期,它的值为同一个月中每一周的第几天 |
| firstDayOfMonth | 创建一个新的日期,它的值为当月的第一天 |
| firstDayOfNextMonth | 创建一个新的日期,它的值为下月的第一天 |
| firstDayOfNextYear | 创建一个新的日期,它的值为明年的第一天 |
| firstDayOfYear | 创建一个新的日期,它的值为当年的第一天 |
| firstInMonth | 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值 |
| lastDayOfMonth | 创建一个新的日期,它的值为当月的最后一天 |
| lastDayOfNextMonth | 创建一个新的日期,它的值为下月的最后一天 |
| lastDayOfNextYear | 创建一个新的日期,它的值为明年的最后一天 |
| lastDayOfYear | 创建一个新的日期,它的值为今年的最后一天 |
| lastInMonth | 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值 |
| next/previous | 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期 |
| nextOrSame/previousOrSame | 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象 |
DateTimeFormatter
处理日期和时间对象时,格式化以及解析日期时间对象是另一个非常重要的功能。新的 java.time.format 包就是特别为这个目的而设计的。这个包中,最重要的类是 DateTime-Formatter。 创建格式器最简单的方法是通过它的静态工厂方法以及常量。 像 BASIC_ISO_DATE和 ISO_LOCAL_DATE 这 样 的 常 量 是 DateTimeFormatter 类 的 预 定 义 实 例 。 所 有 的 DateTimeFormatter 实例都能用于以一定的格式创建代表特定日期或时间的字符串。
LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); // 20140318
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2014-03-18
等同于
LocalDate date1 = LocalDate.parse(“20140318”, DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse(“2014-03-18”, DateTimeFormatter.ISO_LOCAL_DATE);
和老的 java.util.DateFormat 相比较,所有的 DateTimeFormatter 实例都是线程安全的。所以,你能够以单例模式创建格式器实例,就像 DateTimeFormatter 所定义的那些常量,并能在多个线程间共享这些实例。 DateTimeFormatter 类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“dd/MM/yyyy”)
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
如果还需要更加细粒度的控制,DateTimeFormatterBuilder 类还提供了更复杂的格式器,你可以选择恰当的方法,一步一步地构造自己的格式器。另外,它还提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精确地匹配指定的模式) 、填充,以及在格式器中指定可选节。
DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
ZoneId
之前看到的日期和时间的种类都不包含时区信息。时区的处理是新版日期和时间 API 新增加的重要功能,使用新版日期和时间 API 时区的处理被极大地简化了。新的 java.time.ZoneId 类是老版 java.util.TimeZone 的替代品。它的设计目标就是要让你无需为时区处理的复杂和繁琐而操心,比如处理日光时(Daylight Saving Time,DST)这种问题。跟其他日期和时间类一样, ZoneId 类也是无法修改的。
ZoneId romeZone = ZoneId.of(“Europe/Rome”);
地区ID都为 “{区域}/{城市}” 的格式, 这些地区集合的设定都由英特网编号分配机构 (IANA)的时区数据库提供。你可以通过 Java8 的新方法 toZoneId 将一个老的时区对象转换为 ZoneId :
ZoneId zoneId = TimeZone.getDefault().toZoneId();
一旦得到一个 ZoneId 对象,你就可以将它与 LocalDate 、 LocalDateTime 或者是 Instant 对象整合起来,构造为一个 ZonedDateTime 实例,它代表了相对于指定时区的时间点。
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
ZonedDateTime
将 LocalDateTime 转换为 Instant :
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone);
将 Instant 转换为 LocalDateTime :
Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
计算时区
ZoneOffset newYorkOffset = ZoneOffset.of(“-05:00”);
日历系统
Java8 中另外还提供了4种其他的日历系统。这些日历系统中的每一个都有一个对应的日志类,分别是 ThaiBuddhistDate 、MinguoDate 、 JapaneseDate 以及 HijrahDate 。所有这些类以及 LocalDate 都实现了 ChronoLocalDate 接口,能够对公历的日期进行建模。利用 LocalDate 对象,你可以创建这些类的实例。
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(date);
等同于
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow();
java8类库
@Repeatable
如果一个注解在设计之初就是可重复的,你可以直接使用它。但是,如果你提供的注解是为用户提供的,那么就需要做一些工作,说明该注解可以重复。
新增方法
| 类/接口 | 新方法 |
| — | — |
| Map | getOrDefault , forEach , compute , computeIfAbsent , computeIfPresent , merge ,putIfAbsent , remove(key,value) , replace , replaceAll |
| Iterable | forEach , spliterator |
| Iterator | forEachRemaining |
| Collection | removeIf , stream , parallelStream |
| List | replaceAll , sort |
| BitSet | stream |
Map
forEach 该方法签名为 void forEach(BiConsumer<? super K,? super V> action),作用是对 Map 中的每个映射执行 action 指定的操作,其中 BiConsumer 是一个函数接口,里面有一个待实现方法 void accept(T t, U u)。
java8之前写法:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, “one”);
map.put(2, “two”);
map.put(3, “three”);
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + “=” + entry.getValue());
}
java8:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, “one”);
map.put(2, “two”);
map.put(3, “three”);
map.forEach((k, v) -> System.out.println(k + “=” + v));
getOrDefault 方法就可以替换现在检测 Map 中是否包含给定键映射的惯用方法。如果 Map 中不存在这样的键映射,你可以提供一个默认值,方法会返回该默认值。
java8 之前写法:
Map<String, Integer> carInventory = new HashMap<>();
Integer count = 0;
if (map.containsKey(“Aston Martin”)) {
count = map.get(“Aston Martin”);
}
java8:
Integer count = map.getOrDefault(“Aston Martin”, 0);
putIfAbsent 方法签名为V putIfAbsent(K key, V value),作用是只有在不存在 key 值的映射或映射值为 null 时,才将 value 指定的值放入到 Map 中,否则不对 Map 做更改.该方法将条件判断和赋值合二为一,使用起来更加方便。
remove(Object key, Object value) 方法,只有在当前 Map 中 key 正好映射到 value 时才删除该映射,否则什么也不做。
replace 在 Java7 及以前,要想替换 Map 中的映射关系可通过 put(K key, V value) 方法实现,该方法总是会用新值替换原来的值.为了更精确的控制替换行为,Java8 在 Map 中加入了两个 replace() 方法,分别如下:
-
replace(K key, V value),只有在当前 Map 中 key 的映射存在时才用 value 去替换原来的值,否则什么也不做。
-
replace(K key, V oldValue, V newValue),只有在当前 Map 中 key 的映射存在且等于 oldValue 时才用 newValue 去替换原来的值,否则什么也不做。
replaceAll 该方法签名为 replaceAll(BiFunction<? super K,? super V,? extends V> function),作用是对 Map 中的每个映射执行 function 指定的操作,并用 function 的执行结果替换原来的 value,其中 BiFunction 是一个函数接口,里面有一个待实现方法 R apply(T t, U u)。
java8 之前写法:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, “one”);
map.put(2, “two”);
map.put(3, “three”);
for (Map.Entry<Integer, String> entry : map.entrySet()) {
entry.setValue(entry.getValue().toUpperCase());
}
java8:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, “one”);
map.put(2, “two”);
map.put(3, “three”);
map.replaceAll((k, v) -> v.toUpperCase());
merge 该方法签名为 merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction),作用是:
如果 Map 中 key 对应的映射不存在或者为 null,则将 value(不能是 null)关联到 key 上;
否则执行 remappingFunction,如果执行结果非 null 则用该结果跟 key 关联,否则在 Map 中删除 key 的映射。
Map<String, String> myMap = new HashMap<>();
myMap.put(“A”, “str01A”);
myMap.merge(“A”, “merge01”, String::concat); // str01A merge01
compute 该方法签名为 compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) ,如果 map 里有这个 key,那么 remappingFunction 输入的 v 就是现在的值,返回的是对应 value,如果没有这个 key,那么输入的 v 是 null。
map.compute(key, (k, v) -> v == null ? newMsg : v.concat(newMsg));
computeIfAbsent 该方法签名为 V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction),作用是:只有在当前 Map 中不存在 key 值的映射或映射值为 null 时,才调用 mappingFunction,并在 mappingFunction 执行结果非 null 时,将结果跟 key 关联。
java8 之前写法:
Map<Integer, Set> map = new HashMap<>();
if (map.containsKey(1)) {
map.get(1).add(“one”);
} else {
Set valueSet = new HashSet();
valueSet.add(“one”);
map.put(1, valueSet);
}
java8:
map.computeIfAbsent(1, v -> new HashSet()).add(“yi”);
computeIfPresent 该方法签名为 V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用跟 computeIfAbsent() 相反,即,只有在当前 Map 中存在 key 值的映射且非 null 时,才调用 remappingFunction,如果 remappingFunction 执行结果为 null,则删除 key 的映射,否则使用该结果替换 key 原来的映射。
Collection
removeIf 该方法签名为 boolean removeIf(Predicate<? super E> filter),作用是删除容器中所有满足 filter 指定条件的元素,其中 Predicate 是一个函数接口,里面只有一个待实现方法 boolean test(T t),同样的这个方法的名字根本不重要,因为用的时候不需要书写这个名字。
java8之前写法:
// 使用迭代器删除列表元素
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
Iterator it = list.iterator();
while (it.hasNext()) {
if (it.next().length()>3) { // 删除长度大于3的元素
it.remove();
}
}
java8:
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
// 删除长度大于3的元素
list.removeIf(str -> str.length() > 3);
replaceAll 该方法签名为 void replaceAll(UnaryOperator operator),作用是对每个元素执行 operator 指定的操作,并用操作结果来替换原来的元素。其中 UnaryOperator 是一个函数接口,里面只有一个待实现函数 T apply(T t)。
java8 之前写法:
// 使用下标实现元素替换
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
for (int i=0; i<list.size(); i++) {
String str = list.get(i);
if (str.length()>3) {
list.set(i, str.toUpperCase());
}
}
java8:
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
list.replaceAll(str -> {
if (str.length() > 3) {
return str.toUpperCase();
}
return str;
});
sort 该方法定义在List接口中,方法签名为 void sort(Comparator<? super E> c),该方法根据c指定的比较规则对容器元素进行排序。Comparator 接口我们并不陌生,其中有一个方法int compare(T o1, T o2) 需要实现,显然该接口是个函数接口。
java8 之前写法:
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
Collections.sort(list, new Comparator() {
@Override public int compare(String str1, String str2) {
return str1.length() - str2.length();
}
});
java8:
ArrayList list = new ArrayList<>(Arrays.asList(“I”, “love”, “you”, “too”));
list.sort((str1, str2) -> str1.length() - str2.length());
spliterator 方法签名为 Spliterator spliterator(),该方法返回容器的可拆分迭代器。从名字来看该方法跟 iterator() 方法有点像,我们知道Iterator是用来迭代容器的, Spliterator 也有类似作用,但二者有如下不同:
-
Spliterator 既可以像 Iterator 那样逐个迭代,也可以批量迭代。批量迭代可以降低迭代的开销。
-
Spliterator 是可拆分的,一个 Spliterator 可以通过调用 Spliterator trySplit() 方法来尝试分成两个。一个是 this,另一个是新返回的那个,这两个迭代器代表的元素没有重叠。
可通过(多次)调用 Spliterator.trySplit() 方法来分解负载,以便多线程处理。
stream 和 parallelStream 分别返回该容器的 Stream 视图表示,不同之处在于parallelStream() 返回并行的 Stream。Stream 是 Java 函数式编程的核心类。
并发包
原子操作
java.util.concurrent.atomic 包提供了多个对数字类型进行操作的类,比如 AtomicInteger 和 AtomicLong ,它们支持对单一变量的原子操作。这些类在 Java8 中新增了更多的方法支持。
-
getAndUpdate —— 以原子方式用给定的方法更新当前值,并返回变更之前的值。
-
updateAndGet —— 以原子方式用给定的方法更新当前值,并返回变更之后的值。
-
getAndAccumulate —— 以原子方式用给定的方法对当前及给定的值进行更新,并返回变更之前的值。
-
accumulateAndGet —— 以原子方式用给定的方法对当前及给定的值进行更新,并返回变更之后的值。
Adder 和 Accumulator:
多线程的环境中,如果多个线程需要频繁地进行更新操作,且很少有读取的动作(比如,在统计计算的上下文中) ,Java API 文档中推荐大家使用新的类 LongAdder 、 LongAccumulator 、DoubleAdder 以及 DoubleAccumulator ,尽量避免使用它们对应的原子类型。这些新的类在设计之初就考虑了动态增长的需求,可以有效地减少线程间的竞争。
LongAddr 和 DoubleAdder 类都支持加法操作,而 LongAccumulator 和 DoubleAccumulator 可以使用给定的方法整合多个值。
LongAdder adder = new LongAdder();
adder.add(10);
long sum = adder.sum();
等同于
LongAccumulator acc = new LongAccumulator(Long::sum, 0);
acc.accumulate(10);
long result = acc.get();
ConcurrentHashMap
ConcurrentHashMap 类的引入极大地提升了 HashMap 现代化的程度,新引入的ConcurrentHashMap 对并发的支持非常友好。 ConcurrentHashMap 允许并发地进行新增和更新操作,因为它仅对内部数据结构的某些部分上锁。因此,和另一种选择,即同步式的 Hashtable 比较起来,它具有更高的读写性能。
- 性能
为了改善性能,要对 ConcurrentHashMap 的内部数据结构进行调整。典型情况下, map 的条目会被存储在桶中,依据键生成哈希值进行访问。但是,如果大量键返回相同的哈希值,由于桶是由 List 实现的,它的查询复杂度为O(n),这种情况下性能会恶化。在 Java8 中,当桶过于臃肿时,它们会被动态地替换为排序树(sorted tree) ,新的数据结构具有更好的查询性能(排序树的查询复杂度为O(log(n))) 。注意,这种优化只有当键是可以比较的(比如 String 或者 Number类)时才可能发生。
- 类流操作
ConcurrentHashMap 支持三种新的操作,这些操作和你之前在流中所见的很像:
-
forEach ——对每个键值对进行特定的操作
-
reduce ——使用给定的精简函数(reduction function) ,将所有的键值对整合出一个结果
-
search ——对每一个键值对执行一个函数,直到函数的返回值为一个非空值
以上每一种操作都支持四种形式,接受使用键、值、 Map.Entry 以及键值对的函数:
-
使用键和值的操作( forEach 、 reduce 、 search )
-
使用键的操作( forEachKey 、 reduceKeys 、 searchKeys )
-
使用值的操作 ( forEachValue 、 reduceValues 、 searchValues )
-
使用 Map.Entry 对象的操作( forEachEntry 、 reduceEntries 、 searchEntries )
注意,这些操作不会对 ConcurrentHashMap 的状态上锁。它们只会在运行过程中对元素进行操作。应用到这些操作上的函数不应该对任何的顺序,或者其他对象,抑或在计算过程发生变化的值,有依赖。
除此之外,你需要为这些操作指定一个并发阈值。如果经过预估当前 map 的大小小于设定的阈值,操作会顺序执行。使用值 1 开启基于通用线程池的最大并行。使用值 Long.MAX_VALUE 设定程序以单线程执行操作。
下面这个例子中,我们使用 reduceValues 试图找出 map 中的最大值:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
Optional maxValue = Optional.of(map.reduceValues(1, Integer::max));
注意,对 int 、 long 和 double ,它们的 reduce 操作各有不同(比如 reduceValuesToInt 、reduceKeysToLong 等) 。
- 计数
ConcurrentHashMap 类提供了一个新的方法,名叫 mappingCount ,它以长整型 long 返回 map 中映射的数目。我们应该尽量使用这个新方法,而不是老的 size 方法, size 方法返回的类型为 int 。这是因为映射的数量可能是 int 无法表示的。
- 集合视图
ConcurrentHashMap 类还提供了一个名为 KeySet 的新方法,该方法以 Set 的形式返回ConcurrentHashMap 的一个视图(对 map 的修改会反映在该 Set 中,反之亦然) 。你也可以使用新的静态方法 newKeySet ,由 ConcurrentHashMap 创建一个 Set 。
Arrays
使用 parallelSort
parallelSort 方法会以并发的方式对指定的数组进行排序,你可以使用自然顺序,也可以为数组对象定义特别的 Comparator 。
使用 setAll 和 parallelSetAll
setAll 和 parallelSetAll 方法可以以顺序的方式也可以用并发的方式,使用提供的函数计算每一个元素的值,对指定数组中的所有元素进行设置。该函数接受元素的索引,返回该索引元素对应的值。由于 parallelSetAll 需要并发执行,所以提供的函数必须没有任何副作用。
int[] evenNumbers = new int[10];
Arrays.setAll(evenNumbers, i -> i * 2); // 0, 2, 4, 6…
使用 parallelPrefix
parallelPrefix 方法以并发的方式, 用用户提供的二进制操作符对给定数组中的每个元素进行累积计算。
int[] ones = new int[10];
Arrays.fill(ones, 1);
Arrays.parallelPrefix(ones, (a, b) -> a + b);
Number
Number 类中新增的方法如下。
-
Short 、 Integer 、 Long 、 Float 和 Double 类提供了静态方法 sum 、 min 和 max 。
-
Integer 和 Long 类提供了 compareUnsigned 、 divideUnsigned 、 remainderUnsigned 和 toUnsignedLong 方法来处理无符号数。
-
Integer 和 Long 类也分别提供了静态方法 parseUnsignedInt 和 parseUnsignedLong将字符解析为无符号 int 或者 long 类型。
-
Byte 和 Short 类提供了 toUnsignedInt 和 toUnsignedLong 方法通过无符号转换将参数转化为 int 或 者 long 类型 。 类似地 , Integer 类现在也提供了静态方法toUnsignedLong 。
-
Double 和 Float 类提供了静态方法 isFinite ,可以检查参数是否为有限浮点数。
-
Boolean 类现在提供了静态方法 logicalAnd 、 logicalOr 和 logicalXor ,可以在两个boolean 之间执行 and 、 or 和 xor 操作。
-
BigInteger 类提供了 byteValueExact 、 shortValueExact 、 intValueExact 和longValueExact 可以将 BigInteger 类型的值转换为对应的基础类型。不过,如果在转换过程中有信息的丢失,方法会抛出算术异常。
Math
如果 Math 中的方法在操作中出现溢出, Math 类提供了新的方法可以抛出算术异常。支持这一异常的方法包括使用 int 和 long 参数的 addExact 、 subtractExact 、 multipleExact 、incrementExact 、 decrementExact 和 negateExact 。此外, Math 类还新增了一个静态方法toIntExact , 可以将 long 值转换为 int 值。 其他的新增内容包括静态方法 floorMod 、 floorDiv和 nextDown 。
Files
Files 类最引人注目的改变是,你现在可以用文件直接产生流。通过 Files.lines 方法你可以以延迟方式读取文件的内容,并将其作为一个流。此外,还有一些非常有用的静态方法可以返回流。
-
Files.list —— 生成由指定目录中所有条目构成的 Stream
。这个列表不是递归包含的。由于流是延迟消费的,处理包含内容非常庞大的目录时,这个方法非常有用。 -
Files.walk —— 和 Files.list 有些类似,它也生成包含给定目录中所有条目的Stream
。不过这个列表是递归的,你可以设定递归的深度。注意,该遍历是依照深度优先进行的。 -
Files.find —— 通过递归地遍历一个目录找到符合条件的条目,并生成一个Stream
对象。
Reflection
Relection 接口的另一个变化是新增了可以查询方法参数信息的API,比如,你现在可以使用新增的 java.lang.reflect.Parameter 类查询方法参数的名称和修饰符,这个类被新的java.lang.reflect.Executable 类所引用, 而 java.lang.reflect.Executable 通用函数和构造函数共享的父类。
如果你想要学习Java的话,我给你分享一些Java的学习资料,你不用浪费时间到处搜了,从Java入门到精通的资料我都给你整理好了,这些资料都是我做Java这几年整理的Java最新学习路线,Java笔试题,Java面试题,Java零基础到精通视频课程,Java开发工具,Java练手项目,Java电子书,Java学习笔记,PDF文档教程,Java程序员面经,Java求职简历模板等,这些资料对你接下来学习Java一定会带来非常大的帮助,每个Java初学者都必备,请你进我的**Java技术qq交流群127522921**自行下载,所有资料都在群文件里,进去要跟大家多交流学习哦。
String
String 类也新增了一个静态方法,名叫 join 。你大概已经猜出它的功能了,它可以用一个分隔符将多个字符串连接起来。
String authors = String.join(", ", “Raoul”, “Mario”, “Alan”);
PS
–
泛型
Java类型要么是引用类型(比如 Byte 、 Integer 、 Object 、 List ) ,要么是原始类型(比如 int 、 double 、 byte 、 char ) 。但是泛型(比如 Consumer 中的 T )只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应accept 方法的实现Lambda是 Function接口的 apply 方法的实现的原始类型,叫作拆箱(unboxing) 。Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。
工具类库
Guava、Apache和lambdaj
广义归约( Collectors.reducing)
架构学习资料
由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
增了一个静态方法,名叫 join 。你大概已经猜出它的功能了,它可以用一个分隔符将多个字符串连接起来。
String authors = String.join(", ", “Raoul”, “Mario”, “Alan”);
PS
–
泛型
Java类型要么是引用类型(比如 Byte 、 Integer 、 Object 、 List ) ,要么是原始类型(比如 int 、 double 、 byte 、 char ) 。但是泛型(比如 Consumer 中的 T )只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应accept 方法的实现Lambda是 Function接口的 apply 方法的实现的原始类型,叫作拆箱(unboxing) 。Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。
工具类库
Guava、Apache和lambdaj
广义归约( Collectors.reducing)
架构学习资料
[外链图片转存中…(img-bdh7kxGQ-1715630705302)]
[外链图片转存中…(img-pYi4L45R-1715630705303)]
[外链图片转存中…(img-L0N9b2w4-1715630705303)]
[外链图片转存中…(img-UDq7BF9W-1715630705303)]
[外链图片转存中…(img-57z3UA1M-1715630705304)]
由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!