看了下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
礼品代发、一件代发快递就发www.danhw.com