Java8是一个较大改变的版本,包含了API和库方面的修正,它还对我们常用的API进行很多微小的调整, 下面我会带你了解字符串、集合、注解等新方法。
字符串
经常会遇到这样一种情况, 需要将一组字符串按特定格式拼接起来。
之前的写法
String message = "Java"+"-"+"is"+"-"+"cool";
Java 8的join写法
String message = String.join("-", "Java", "is", "cool");
当然更多的时候是要遍历集合里的字符串,然后按特定格式拼接:
之前的写法
List<String> strings = new LinkedList<>();
strings.add("Java");
strings.add("is");
strings.add("cool"); String message = "";
for (int i = 0; i < strings.size(); i++) {
message += strings.get(i)+"-";
}
message = message.substring(0, message.length()-1);
join写法
List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
第一个参数是分隔符,后面接收一个CharSequence类型的可变参数数组或一个Iterable。
集合
集合改变中最大的当属前面章节中提到的Stream API,除此之外还有一些小的改动。
- Map中的很多方法对并发访问十分重要,我们将在后面的章节中介绍
- Iterator提供forEachRemaining将剩余的元素传递给一个函数
- BitSet可以产生一个Stream对象
通用目标类型判断
Java8对泛型参数的推断进行了增强。相信你对Java8之前版本中的类型推断已经比较熟悉了。 比如,Collections中的方法emptyList方法定义如下:
static <T> List<T> emptyList();
emptyList方法使用了类型参数T进行参数化。 你可以像下面这样为该类型参数提供一个显式的类型进行函数调用:
List<Person> persons = Collections.<Person>emptyList();
不过编译器也可以推断泛型参数的类型,上面的代码和下面这段代码是等价的:
List<Person> persons = Collections.emptyList();
我还是习惯于这样书写。
注解
Java 8在两个方面对注解机制进行了改进,分别为:
- 可以定义重复注解
- 可以为任何类型添加注解
重复注解
之前版本的Java禁止对同样的注解类型声明多次。由于这个原因,下面的第二句代码是无效的:
@interface Basic {
String name();
}
@Basic(name="fix")
@Basic(name="todo")
class Person{ }
我们之前可能会通过数组的做法绕过这一限制:
@interface Basic {
String name();
}
@interface Basics {
Basic[] value();
}
@Basics( { @Basic(name="fix") , @Basic(name="todo") } )
class Person{ }
Book类的嵌套注解相当难看。这就是Java8想要从根本上移除这一限制的原因,去掉这一限制后, 代码的可读性会好很多。现在,如果你的配置允许重复注解,你可以毫无顾虑地一次声明多个同一种类型的注解。 它目前还不是默认行为,你需要显式地要求进行重复注解。
创建一个重复注解
如果一个注解在设计之初就是可重复的,你可以直接使用它。但是,如果你提供的注解是为用户提供的, 那么就需要做一些工作,说明该注解可以重复。下面是你需要执行的两个步骤:
- 将注解标记为@Repeatable
- 提供一个注解的容器下面的例子展示了如何将@Basic注解修改为可重复注解
@Repeatable(Basics.class)
@interface Basic {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
@interface Basics {
Basic[] value();
}
完成了这样的定义之后,Person类可以通过多个@Basic注解进行注释,如下所示:
@Basic(name="fix")
@Basic(name="todo")
class Person{ }
编译时, Person 会被认为使用了 @Basics( { @Basic(name=”fix”) , @Basic(name=”todo”)} ) 这样的形式进行了注解,所以,你可以把这种新的机制看成是一种语法糖, 它提供了程序员之前利用的惯用法类似的功能。为了确保与反射方法在行为上的一致性, 注解会被封装到一个容器中。 Java API中的getAnnotation(Class<T> annotationClass)方法会为注解元素返回类型为T的注解。 如果实际情况有多个类型为T的注解,该方法的返回到底是哪一个呢?
我们不希望一下子就陷入细节的魔咒,类Class提供了一个新的getAnnotationsByType方法, 它可以帮助我们更好地使用重复注解。比如,你可以像下面这样打印输出Person类的所有Basic注解:
返回一个由重复注解Basic组成的数组
public static void main(String[] args) {
Basic[] basics = Person.class.getAnnotationsByType(Basic.class);
Arrays.asList(basics).forEach(a -> {
System.out.println(a.name());
});
}
Null检查
Objects类添加了两个静态方法isNull和nonNull,在使用流的时候非常有用。
例如获取一个流的所有不为null的对象:
Stream.of("a", "c", null, "d")
.filter(Objects::nonNull)
.forEach(System.out::println);