看了下openjdk官网 http://openjdk.java.net/projects/jdk/14/ jdk14今年就要发布稳定版本了,连java8都没有系统的学习过。那就来学习下java8吧,这是一个长期维护的版本。这里主要来学习java8的一些新特性。对应demo可以下载链接:
https://github.com/wenqy/java-study
接口默认方法
使用default
关键字,为接口声明添加非抽象的方法实现接口默认方法
public interface DefaultInterfaceMethod {
double calc(int a);
/**
* 接口使用 <b>default</b>关键字,添加非抽象方法实现(扩展方法)
* @param a
* @return
* @author wenqy
* @date 2020年1月16日 上午9:55:20
*/
default double sqrt(int a) {
return Math.sqrt(a);
}
}
然后实现匿名内部类,并调用默认方法
DefaultInterfaceMethod interfaceMethod = new DefaultInterfaceMethod() {
@Override
public double calc(int a) {
return sqrt(a * 100);
}
};
System.out.println(“calc:” + interfaceMethod.calc(100)); // calc:100.0
System.out.println(“sqrt:” + interfaceMethod.sqrt(16)); // sqrt:4.0
Lambda表达式
来看下排序的例子,静态工具方法Collections.sort
接受一个list,和一个Comparator
接口作为输入参数,java8之前需要一个匿名对象,java8可以用Lambda表达式变得简洁
/**
* Java8之前方法(匿名对象)实现排序
* @param names
* @author wenqy
* @date 2020年1月16日 上午10:07:45
*/
private static void sortByBefore8Method(List<String> names) {
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
}
/**
* 用Lambda表达式排序,Java编译器能够自动识别参数类型
* @param names
* @author wenqy
* @date 2020年1月16日 上午10:09:40
*/
private static void sortByLambda(List<String> names) {
// 可以这样写
// Collections.sort(names, (String a, String b) -> {
// return b.compareTo(a);
// });
// 也可以这样写
// Collections.sort(names, (String a, String b) -> b.compareTo(a));
// 还可以这样写
Collections.sort(names, (a, b) -> b.compareTo(a));
}
Java编译器能够自动识别参数的类型,所以可以省略掉类型不写。
函数式接口
每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。一个所谓的函数式接口必须要有且仅有一个抽象方法声明。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。
任意只包含一个抽象方法的接口,我们都可以用来做成lambda表达式。为了让你定义的接口满足要求,你应当在接口前加上@FunctionalInterface
标注。编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。
/**
*
* 每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。
* 一个所谓的函数式接口必须要有且仅有一个抽象方法声明。
* 每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。
*
* @FunctionalInterface 标注。
* 编译器会注意到这个标注,如果接口中定义了第二个抽象方法的话,编译器会抛出异常
*
* @version V5.0
* @author wenqy
* @date 2020年1月16日
*/
@FunctionalInterface
public interface Converter<F, T> {
/**
* 类型转换 F->T
* @param from
* @return
* @author wenqy
* @date 2020年1月16日 上午10:22:32
*/
T convert(F from);
// T mapping(F from);
}
方法和构造函数引用
函数式接口可以通过方法或构造函数引用使代码变得更简洁。Java 8 允许你通过::
关键字获取方法或者构造函数的的引用。
static class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
private static <F, T> void convert(Converter<F,T> converter, F from) {
T converted = converter.convert(from);
System.out.println(converted);
}
public static void main(String[] args) {
Converter<String, Integer> converter1 = Integer::valueOf; // 静态方法引用
convert(converter1,“123”); // “1”
Something something = new Something();
Converter<String, String> converter2 = something::startsWith; // 对象方法引用
convert(converter2,“Java”); // “J”
PersonFactory<Person> personFactory = Person::new; // 构造函数引用(构造参数需一致)
Person person = personFactory.create(“Peter”, “Parker”);
System.out.println(person); // Person [firstName=Peter, lastName=Parker]
}
Lambda访问范围
对于lambda表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。默认方法无法在lambda表达式内部被访问。
我们可以访问lambda表达式外部的final
局部变量,但是与匿名对象不同的是,变量num并不需要一定是final
,然而,num在编译的时候被隐式地当做final
变量来处理。
static class Lambda {
static int outerStaticNum;
int outerNum;
/**
* 访问成员变量
*
* @author wenqy
* @date 2020年1月16日 上午11:15:31
*/
void accessOuterNum() {
Converter<Integer, String> converter = (from) -> {
outerNum = 23;
return String.valueOf(outerNum + from);
};
System.out.println(“accessOuterNum:” + converter.convert(2)); // 25
// outerNum = 3;
}
/**
* 访问静态变量
*
* @author wenqy
* @date 2020年1月16日 上午11:18:41
*/
void accessOuterStaticNum() {
Converter<Integer, String> converter = (from) -> {
outerStaticNum = 72;
return String.valueOf(outerStaticNum + from);
};
System.out.println(“accessOuterStaticNum:” + converter.convert(2)); // 74
// outerStaticNum = 3;
}
/**
* 访问局部变量
* 可以访问lambda表达式外部的final局部变量
* 但是与匿名对象不同的是,变量num并不需要一定是final
* @author wenqy
* @date 2020年1月16日 上午11:06:09
*/
void accessLocalVariable() {
// final int num = 1;
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
System.out.println(“accessLocalVariable:” + stringConverter.convert(2)); // 3
// num = 3; // 编译的时候被隐式地当做final变量来处理,无法改变值
}
}
public static void main(String[] args) {
Lambda lambda = new Lambda();
lambda.accessLocalVariable();
lambda.accessOuterNum();
lambda.accessOuterStaticNum();
// 默认方法无法在lambda表达式内部被访问,无法通过编译
// DefaultInterfaceMethod formula = (a) -> sqrt( a * 100);
}
内置函数式接口
Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。让我们来看下几个内置的函数式接口
Predicate
是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)
Function
接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)
Supplier
接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数
Consumer
代表了在一个输入参数上需要进行的操作
Comparator
接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法
Optional
不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerException
产生。他是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返回一个non-null的值,也可能返回一个空值。为了不直接返回null,我们在Java 8中就返回一个Optional
/**
* 布尔类型函数
*
* @author wenqy
* @date 2020年1月16日 上午11:39:26
*/
private static void testPredicates() {
System.out.println(“——>testPredicates——>”);
Predicate<String> predicate = (s) -> s.length() > 0;
System.out.println(predicate.test(“foo”)); // true
System.out.println(predicate.negate().test(“foo”)); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
System.out.println(nonNull.test(null)); // false
System.out.println(isNull.test(null)); // true
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
System.out.println(isEmpty.test(“”)); // true
System.out.println(isNotEmpty.test(“”)); // false
}
/**
* 内置 Function 函数,将多个函数串联
*
* @author wenqy
* @date 2020年1月16日 上午11:41:35
*/
private static void testFunctions() {
System.out.println(“——>testFunctions——>”);
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
System.out.println(backToString.apply(“123”)); // “123”
}
/**
* 内置 Supplier 函数
* 产生一个给定类型的结果
* @author wenqy
* @date 2020年1月16日 上午11:52:51
*/
private static void testSuppliers() {
System.out.println(“——>testSuppliers——>”);
Supplier<Person> personSupplier = Person::new;
Person person = personSupplier.get(); // new Person
person.setFirstName(“wen”);
person.setLastName(“qy”);
System.out.println(person);
}
/**
* 内置 Consumer 函数
* 输入参数上需要进行操作
* @author wenqy
* @date 2020年1月16日 下午1:48:33
*/
private static void testConsumers() {
System.out.println(“——>testConsumers——>”);
Consumer<Person> greeter = (p) -> System.out.println(“Hello, “ + p.getFirstName());
greeter.accept(new Person(“Luke”, “Skywalker”));
}
/**
*
* 内置 Comparator 函数
* 用于比较
* @author wenqy
* @date 2020年1月16日 下午1:54:29
*/
private static void testComparators() {
System.out.println(“——>testComparators——>”);
Comparator<Person> comparator = (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName());
Person p1 = new Person(“John”, “Doe”);
Person p2 = new Person(“Alice”, “Wonderland”);
System.out.println(comparator.compare(p1, p2)); // > 0
System.out.println(comparator.reversed().compare(p1, p2)); // < 0
}
/**
* Optional
* 值容器,用来防止NullPointerException产生
* @author wenqy
* @date 2020年1月16日 下午1:58:44
*/
private static void testOptionals() {
System.out.println(“——>testOptionals——>”);
Optional<String> optional = Optional.of(“bam”);
System.out.println(optional.isPresent()); // true
System.out.println(optional.get()); // “bam”
System.out.println(optional.orElse(“fallback”)); // “bam”
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // “b”
}
流Stream
java.util.Stream
表示了某一种元素的序列,在这些元素上可以进行各种操作。Stream操作可以是中间操作,也可以是完结操作。完结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你可以通过多次调用同一个流操作方法来将操作结果串起来(就像StringBuffer
的append方法一样)。Stream是在一个源的基础上创建出来的,例如java.util.Collection
中的list或者set(map不能作为Stream的源)。Stream操作往往可以通过顺序或者并行两种方式来执行。Stream并不会改变原有的序列。
List<String> stringCollection = new ArrayList<>();
public StreamsMain() {
init();
}
private void init() {
if (stringCollection.isEmpty()) {
stringCollection.add(“ddd2”);
stringCollection.add(“aaa2”);
stringCollection.add(“bbb1”);
stringCollection.add(“aaa1”);
stringCollection.add(“bbb3”);
stringCollection.add(“ccc”);
stringCollection.add(“bbb2”);
stringCollection.add(“ddd1”);
}
}
/**
*
* Filter接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。
* 该操作是一个中间操作,因此它允许我们在返回结果的基础上再进行其他的流操作(forEach)。
* ForEach接受一个function接口类型的变量,用来执行对每一个元素的操作。
* ForEach是一个中止操作。它不返回流,所以我们不能再调用其他的流操作。
* @author wenqy
* @date 2020年1月16日 下午2:34:58
*/
private void testFilter() {
System.out.println(“—–>testFilter—–>”);
stringCollection
.stream()
.filter((s) -> s.startsWith(“a”)) // 过滤以“a”开头的集合
.forEach(System.out::println);
}
/**
* Sorted是一个中间操作,能够返回一个排过序的流对象的视图。
* 流对象中的元素会默认按照自然顺序进行排序,除非你自己指定一个Comparator接口来改变排序规则
*
* @author wenqy
* @date 2020年1月16日 下午2:38:28
*/
private void testSorted() {
System.out.println(“—–>testSorted—–>”);
stringCollection
.stream()
.sorted() // 自然排序
.filter((s) -> s.startsWith(“a”)) // 过滤以“a”开头的集合
.forEach(System.out::println);
// sorted不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。
System.out.println(stringCollection);
}
/**
* map是一个对于流对象的中间操作,通过给定的方法,它能够把流对象中的每一个元素对应到另外一个对象上。
* 下面的例子就演示了如何把每个string都转换成大写的string.
* 不但如此,你还可以把每一种对象映射成为其他类型。
* 对于带泛型结果的流对象,具体的类型还要由传递给map的泛型方法来决定。
*
* @author wenqy
* @date 2020年1月16日 下午2:43:38
*/
private void testMap() {
System.out.println(“—–>testMap—–>”);
stringCollection
.stream()
.map(String::toUpperCase) // 每个元素转大写
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// sorted不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。
System.out.println(stringCollection);
}
/**
* 匹配操作有多种不同的类型,都是用来判断某一种规则是否与流对象相互吻合的。
* 所有的匹配操作都是终结操作,只返回一个boolean类型的结果。
*
* @author wenqy
* @date 2020年1月16日 下午2:47:48
*/
private void testMatch() {
System.out.println(“—–>testMatch—–>”);
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith(“a”));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith(“a”));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith(“z”));
System.out.println(noneStartsWithZ); // true
}
/**
* Count是一个终结操作,它的作用是返回一个数值,用来标识当前流对象中包含的元素数量。
*
* @author wenqy
* @date 2020年1月16日 下午2:51:40
*/
private void testCount() {
System.out.println(“—–>testCount—–>”);
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith(“b”))
.count();
System.out.println(startsWithB); // 3
}
/**
* 该操作是一个终结操作,它能够通过某一个方法,对元素进行削减操作。
* 该操作的结果会放在一个Optional变量里返回。
*
* @author wenqy
* @date 2020年1月16日 下午2:54:00
*/
private void testReduce() {
System.out.println(“—–>testReduce—–>”);
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + “#” + s2);
reduced.ifPresent(System.out::println); // “aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2”
}
并发流Stream
数量量大时,可以使用并行流进行操作来提高运行效率。
// int max = 1000000; // 大数据量并发才有优势
int max = 400000;
List<String> values = new ArrayList<>(max);
public ParallelStreamsMain() {
init();
}
private void init() {
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
}
/**
* 顺序排序
*
* @author wenqy
* @date 2020年1月16日 下午3:20:22
*/
private void sequenceSorted() {
System.out.println(“——>sequenceSorted—–>”);
long t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 – t0);
System.out.println(String.format(“sequential sort took: %d ms”, millis));
}
/**
* 并行排序
*
* @author wenqy
* @date 2020年1月16日 下午3:21:56
*/
private void parallelSorted() {
System.out.println(“——>parallelSorted—–>”);
long t0 = System.nanoTime();
long count = values.parallelStream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 – t0);
System.out.println(String.format(“parallel sort took: %d ms”, millis));
}
Map
map是不支持流操作的。而更新后的map现在则支持多种实用的新方法
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, “val” + i); // 旧值存在时返回旧值,不进行替换
}
map.forEach((id, val) -> System.out.println(val));
map.computeIfPresent(3, (num, val) -> val + num);
map.get(3); // val33
map.computeIfPresent(9, (num, val) -> null); // 返回null时,remove(key)
map.containsKey(9); // false
map.computeIfAbsent(23, num -> “val” + num);
map.containsKey(23); // true
map.computeIfAbsent(3, num -> “bam”); // 旧值存在时返回旧值,不进行替换
map.get(3); // val33
map.remove(3, “val3”); // 校验value删除
map.get(3); // val33
map.remove(3, “val33”);
map.get(3); // null
map.getOrDefault(42, “not found”); // not found 不存在则返回默认值
map.merge(9, “val9”, (value, newValue) -> value.concat(newValue)); // 先前key 9 已经删除了,不存在key,则返回 value
System.out.println(map.get(9)); // val9
map.merge(9, “concat”, (value, newValue) -> value.concat(newValue)); // 存在key,才进行合并
System.out.println(map.get(9)); // val9concat
时间日期API
Java 8 包含了全新的时间日期API,这些功能都放在了java.time
包下。新的时间日期API是基于Joda-Time库开发的,但是也不尽相同。
/**
* Clock提供了对当前时间和日期的访问功能。Clock是对当前时区敏感的
*
* @author wenqy
* @date 2020年1月16日 下午4:36:49
*/
private static void testClock() {
System.out.println(“—–>testClock—–>”);
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
System.out.println(millis); // 获取当前毫秒时间
Instant instant = clock.instant();
Date date = Date.from(instant); // java.util.Date
System.out.println(date); // 获取日期 Thu Jan 16 16:38:01 CST 2020
}
/**
* 时区类可以用一个ZoneId来表示。
* 时区类还定义了一个偏移量,用来在当前时刻或某时间与目标时区时间之间进行转换。
*
* @author wenqy
* @date 2020年1月16日 下午4:40:38
*/
private static void testTimezones() {
System.out.println(“—–>testTimezones—–>”);
System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids
ZoneId zone1 = ZoneId.of(“Europe/Berlin”);
ZoneId zone2 = ZoneId.of(“Brazil/East”);
ZoneId zone3 = ZoneId.of(“Asia/Shanghai”);
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
System.out.println(zone3.getRules());
}
/**
* 本地时间类表示一个没有指定时区的时间
*
* @author wenqy
* @date 2020年1月16日 下午4:49:11
*/
private static void testLocalTime() {
System.out.println(“—–>testLocalTime—–>”);
LocalTime now1 = LocalTime.now(ZoneId.of(“Europe/Berlin”));
LocalTime now2 = LocalTime.now(ZoneId.of(“Asia/Shanghai”));
// 比较两个时间
System.out.println(now1.isBefore(now2)); // true
long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);
// 柏林时区在东一区,上海时区在东八区 柏林比北京时间慢7小时
System.out.println(hoursBetween); // 7
System.out.println(minutesBetween); // 420
// 时间字符串解析操作
LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late); // 23:59:59
DateTimeFormatter germanFormatter =
DateTimeFormatter
.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.GERMAN);
LocalTime leetTime = LocalTime.parse(“13:37”, germanFormatter);
System.out.println(leetTime); // 13:37
}
/**
* 本地日期
* 每一次操作都会返回一个新的时间对象
*
* @author wenqy
* @date 2020年1月16日 下午5:05:52
*/
private static void testLocalDate() {
System.out.println(“—–>testLocalDate—–>”);
LocalDate today = LocalDate.now(); // 今天
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); // 明天=今天+1
LocalDate yesterday = tomorrow.minusDays(2); // 昨天=明天-2
System.out.println(yesterday);
LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); // 获取周
System.out.println(dayOfWeek); // FRIDAY
}
/**
* 日期-时间
* final对象
* @author wenqy
* @date 2020年1月16日 下午5:12:37
*/
private static void testLocalDateTime() {
System.out.println(“—–>testLocalDateTime—–>”);
LocalDateTime sylvester = LocalDateTime.of(2019, Month.DECEMBER, 31, 23, 59, 59);
DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek); // TUESDAY
Month month = sylvester.getMonth();
System.out.println(month); // DECEMBER
long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay); // 1439
// 加上时区,转日期Date
Instant instant = sylvester
.atZone(ZoneId.systemDefault())
.toInstant();
Date legacyDate = Date.from(instant);
System.out.println(legacyDate); // Tue Dec 31 23:59:59 CST 2019
// 自定义格式对象
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern(“yyyy-MM-dd HH:mm:ss”); // 线程安全,不可变
LocalDateTime parsed = LocalDateTime.parse(“2020-01-16 17:13:00”, formatter);
String string = formatter.format(parsed);
System.out.println(string); // 2020-01-16 17:13:00
}
可重复注解
Java 8中的注解是可重复的
/**
* @Repeatable,Java 8 允许我们对同一类型使用多重注解
*
* @version V5.0
* @author wenqy
* @date 2020年1月16日
*/
@Repeatable(Hints.class)
@Retention(RetentionPolicy.RUNTIME) // 需要指定runtime,否则测试会失败
public @interface Hint {
String value();
}
在Person类添加注解,尝试获取类注解
/**
* @Hint 可重复注解(新方法)
* 使用注解容器(老方法): @Hints({@Hint(“hint1”), @Hint(“hint2”)})
* @version V5.0
* @author wenqy
* @date 2020年1月16日
*/
@Hint(“hint1”)
@Hint(“hint2”)
//@Hints({@Hint(“hint1”), @Hint(“hint2”)})
public class Person {
// …
}
Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint); // null
Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length); // 2
Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length); // 2
String
java8添加了对字符数据流的流式操作
System.out.println(String.join(“:”, “foobar”, “foo”, “bar”)); // foobar:foo:bar
// 指定分割符,将字符串拼接
System.out.println(
“foobar:foo:bar”
.chars() // 字符数据流
.distinct()
.mapToObj(c -> String.valueOf((char)c))
.sorted()
.collect(Collectors.joining())
); // :abfor
System.out.println(
Pattern.compile(“:”)
.splitAsStream(“foobar:foo:bar”)
.filter(s -> s.contains(“bar”))
.sorted()
.collect(Collectors.joining(“:”))
); // bar:foobar
System.out.println(
Stream.of(“bob@gmail.com”, “alice@hotmail.com”)
.filter(Pattern.compile(“.*@gmail\\.com”).asPredicate())
.count()
); // 1
数值处理
Java8添加了对无符号数的额外支持。新增了一些方法来处理数值溢出
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
long maxUnsignedInt = (1l << 32) – 1; //
String string = String.valueOf(maxUnsignedInt);
int unsignedInt = Integer.parseUnsignedInt(string, 10); // 无符号转换
String string2 = Integer.toUnsignedString(unsignedInt, 10);
System.out.println(string2);
try {
Integer.parseInt(string, 10);
} catch (NumberFormatException e) {
System.err.println(“could not parse signed int of “ + maxUnsignedInt);
}
try {
Math.addExact(Integer.MAX_VALUE, 1);
}
catch (ArithmeticException e) {
System.err.println(e.getMessage());
// => integer overflow
}
try {
Math.toIntExact(Long.MAX_VALUE);
}
catch (ArithmeticException e) {
System.err.println(e.getMessage());
// => integer overflow
}
文件处理
java8新增了对文件处理的方法
/**
* 文件查找
* @throws IOException
* @author wenqy
* @date 2020年1月18日 下午3:02:05
*/
private static void findFiles() throws IOException {
Path start = Paths.get(“”);
int maxDepth = 5;
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) ->
String.valueOf(path).endsWith(“.js”))) {
String joined = stream
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(“; “));
System.out.println(“Found: “ + joined);
}
}
/**
* 获取文件列表
* @throws IOException
* @author wenqy
* @date 2020年1月18日 下午3:02:26
*/
private static void listFiles() throws IOException {
try (Stream<Path> stream = Files.list(Paths.get(“”))) {
String joined = stream
.map(String::valueOf)
.filter(path -> !path.startsWith(“.”))
.sorted()
.collect(Collectors.joining(“; “));
System.out.println(“List: “ + joined);
// 列出了当前工作目录的所有文件,之后将每个路径都映射为它的字符串表示。之后结果被过滤、排序,最后连接为一个字符串
}
}
/**
* 文件读写处理
* @throws IOException
* @author wenqy
* @date 2020年1月18日 下午3:02:52
*/
private static void handleFiles() throws IOException {
List<String> lines = Files.readAllLines(Paths.get(“res/nashorn1.js”)); // 整个文件都会读进内存,不高效。文件越大,所用的堆区也就越大
lines.add(“print(‘foobar’);”);
Files.write(Paths.get(“res/nashorn1-modified.js”), lines);
try (Stream<String> stream = Files.lines(Paths.get(“res/nashorn1.js”))) { // 行读取,高效点
stream
.filter(line -> line.contains(“print”))
.map(String::trim)
.forEach(System.out::println);
}
try (BufferedReader reader = Files.newBufferedReader(Paths.get(“res/nashorn1.js”))) { // BufferedReader 更精细
System.out.println(reader.readLine());
}
Path pathOut = Paths.get(“res/output.js”);
try (BufferedWriter writer = Files.newBufferedWriter(pathOut)) { // BufferedWriter 写入文件
writer.write(“print(‘Hello World’);”);
}
try (BufferedReader reader = Files.newBufferedReader(Paths.get(“res/nashorn1.js”))) {
long countPrints = reader
.lines() // 流式处理
.filter(line -> line.contains(“print”))
.count();
System.out.println(countPrints);
}
}
避免 Null 检查
java8之前,如果是多层内嵌对象,需要多次判空,引入Optional 类型提高安全性
public static void main(String[] args) {
Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
Optional.of(new Outer())
.map(Outer::getNested)
.map(Nested::getInner)
.map(Inner::getFoo)
.ifPresent(System.out::println);
Outer obj = new Outer();
resolve(() -> obj.getNested().getInner().getFoo())
.ifPresent(System.out::println);
// 这两个解决方案可能没有传统 null 检查那么高的性能
}
private static <T> Optional<T> resolve(Supplier<T> resolver) {
try {
T result = resolver.get();
return Optional.ofNullable(result);
}
catch (NullPointerException e) {
return Optional.empty();
}
}
初识java8新特性,java8语法糖还是挺香的,java8之前方法调用一堆的回调,让人看的眼花缭乱,不整洁。但让人想吐槽的是调试,加难了调试力度,可能是新手错觉。。。
参考
https://github.com/winterbe/java8-tutorial java8教程
https://wizardforcel.gitbooks.io/modern-java/content/ 中文译站
https://github.com/wenqy/java-study
https://github.com/wenqy/java-study
本文由 wenqy 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Nov 7,2020