原文链接
非常简单地整理了一下Java 8 中出现的新特性,Lambda 部分的例子借鉴了这里 。
Lambda 表达式
Java 8 中最大特性,函数式编程
语法
Lambda 表达式:
parameter -> expression body
- 可选参数类型声明 - 不需要声明参数类型,编译器可以自动推断
- 可选小括号 - 只有一个参数时可以省略左侧的括号,多参数时小括号是必须的
- 可选大括号 - 当函数体只有一条语句时可以省略大括号
- 可选return关键字 - 函数体只有一条语句时,编译器会自动返回结果
字符串排序 pre Java 8 实现:
List<String> strings = Arrays.asList("ab", "a", "bd", "bc", "c", "ca");
Collections.sort(strings, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
System.out.println(strings);
out : [a, ab, bc, bd, c, ca]
如果用 Lambda 表达式:
List<String> stringsLambda = Arrays.asList("ab", "a", "bd", "bc", "c", "ca");
Collections.sort(stringsLambda, (a, b) -> a.compareTo(b));
System.out.println(stringsLambda);
可选参数类型声明:(a, b)
等价于 (String a, String b)
可选小括号:此处有两个参数,不可省略
可选大括号、return关键字:a.compareTo(b)
等价于 {return a.compareTo(b);}
Lambda 范围
可以使用 final 类型的变量
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]) {
GreetingService greetService1 = message -> System.out.println(salutation + message);
greetService1.sayMessage("Mahesh");
}
interface GreetingService {
void sayMessage(String message);
}
}
Method references
Method references 可以通过名称指向方法,使用符号 ::
,可以指向:
- 静态方法
- 实例方法
- 使用operator的构造函数
TreeSet::new
List names = new ArrayList();
names.add("Alice");
names.add("Bob");
names.add("Chirs");
names.forEach(System.out::println);
out:
Alice
Bob
Chris
Functional interfaces
Functional interfaces 具有单一功能,举例来说 Comparable
接口仅仅有一个用于比较大小的方法CompareTo
, Java 8 中定义了很多 Functional interfaces,以便在 lambda表达式 中广泛使用。
Predicate <T>
是一个包含一个方法test(Object)
的实用接口,这个方法返回一个布尔值,用来表示传入对象的测试结果是 true 或 false。
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
测试一下:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("Print all numbers:");
eval(list, n->true);
System.out.println("Print even numbers:");
eval(list, n-> n%2 == 0 );
System.out.println("Print numbers greater than 3:");
eval(list, n-> n > 3 );
out :
Print all numbers:
1 2 3 4 5 6 7 8 9
Print even numbers:
2 4 6 8
Print numbers greater than 3:
4 5 6 7 8 9
Default Methods
Default Methods,可以让原有接口使用 Lambda 表达式。
比如,List
和Collection
接口都没有声明 forEach
方法,强行加上这类方法又会破坏集合原有的实现框架。
语法
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
Multiple Defaults
当接口中有 default 函数时,有可能一个类实现了2个接口,而这2个接口中包含了同名的 default 函数
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
public interface fourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
第一种解决办法:方法重写
public class car implements vehicle, fourWheeler {
default void print() {
System.out.println("I am a four wheeler car vehicle!");
}
}
第二种解决办法:通过super
调用特定接口的default方法
public class car implements vehicle, fourWheeler {
default void print() {
vehicle.super.print();
}
}
Static Default Methods
接口同时可以拥有静态函数
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
Stream
Stream 是一个抽象层的东西,使用Stream可以像SQL语句那样操作数据(Stream 并不能处理SQL语句):
SELECT max(salary), employee_id FROM Employee
上面的SQL语句可以直接返回最高工资员工的工资和ID,而使用 Collections 集合框架,则必须遍历和多次check才能查找出上面的信息。
Stream 的另一个好处就是高效,为避免并发下容易产生的错误,Java 8 中引入 Stream 的概念,让我们可以直接使用多核系统而不用考虑具体的代码实现。
Stream 表示一系列来自Source
的对象
- 元素序列 - Stream 提供一个元素的集合,其中的元素连续排列,且类型确定。
- 源Source - Stream 可以把集合类、Arrays、I/O源当做源。
- Aggregate operations - Stream 支持集合运算:filter、map、limit、reduce、find、match等等
- Pipelining - 多数 stream 操作返回 stream 本身,所以其操作结果可以用流水线的方式处理。这些操作可以叫 intermediate operations,它们接收输入、处理、并将结果返回。
collect()
操作是一个末端操作,通常在 pipelining 操作结束时出现以标记流的结束。 - 自动迭代 - 在源元素内部遍历,与需要显示迭代的集合类不同。
生成 Stream
Collection 接口有两个方法生成 Stream
-
stream()
返回一个将当前集合内容作为源的 sequential stream -
parallelStream()
返回一个将当前集合内容作为源的 parallel Stream
Collectors
一个 Stream 经过流操作之后还是 Stream 对象,可以通过 Collectors
将流处理的结果转换为List
或 String
。下面例子中生成Stream对象,没有做任何流操作,再将其转换为List,故filtered
和 strings
内容一致。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().collect(Collectors.toList());
另外,Collectors
也可以将不同流操作的结果合并起来。
String mergedString = strings.stream().collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
//out : Merged String: abc, , bc, efg, abcd, , jkl
map
把每一个元素映射为相应的结果。下面例子中将数字映射为其平方,并打印不重复的值
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream()
.map( i -> i*i)
.distinct()
.collect(Collectors.toList());
System.out.println(squaresList); // [9, 4, 49, 25]
filter
滤除不符合条件 (保留符合条件)的元素,filter(paramters -> condition)
limit
限制Stream的大小,limit(5)
流中元素不够5个时不报错,取当前所有元素。
sorted
排序
将滤除空字符串,取前5个元素,并排序:
List<String> strings = Arrays.asList("123", "", "456", " ", "78", "", "9");
List<String> result = strings.stream()
.filter(str -> !str.isEmpty())
.limit(5)
.sorted()
.collect(Collectors.toList());
System.out.println(result); //[ , 123, 456, 78, 9]
forEach
Stream 提供了遍历元素的新方法forEach
,通过它打印10个随机数:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
random.ints()
会返回一个 Stream对象
并行处理
做并行处理时可选用 parallelStream
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
统计计算
流处理结束后,可以计算统计数据
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
out:
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9.444444444444445
Optional Class
Optional 是一个包含非null对象的容器, Optional 对象用值的缺失来表示null。
Optional 类有很多实用方法,可以将一个值处理为可用或不可用状态,从而不用进行是否为空值的检查。类似于Guava中的Optional。
java.util.Optional
定义如下:
public final class Optional<T> extends Object
案例:
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args){
OptionalTest optionalTest = new OptionalTest();
Integer value1 = null;
Integer value2 = new Integer(10);
Optional<Integer> a = Optional.ofNullable(value1);
Optional<Integer> b = Optional.of(value2);
System.out.println(optionalTest.sum(a,b));
}
private Integer sum(Optional<Integer> a, Optional<Integer> b) {
System.out.println("First parameter is present: " + a.isPresent());
System.out.println("Second parameter is present: " + b.isPresent());
Integer value1 = a.orElse(new Integer(0));
Integer value2 = b.get();
return value1 + value2;
}
}
out:
First parameter is present: false
Second parameter is present: true
10
新的 Date/Time API
原有时间API
- 线程不安全
java.util.Date
线程不安全,新的时间API是不可变的,且没有setter方法 - 设计糟糕
原有时间API起始1900,月份从1开始,日期从0开始,没有一致性。新API中提供了很多实用的方法。 - 时区处理复杂
java.time
- Local 简化的 date-time API
- Zoned 专门处理多时区的 date-time API
LocalDateTime
、LocalDate
、LocalTime
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(localDateTime.getDayOfMonth());
LocalDate localDate = localDateTime.toLocalDate().withMonth(2);
System.out.println(localDate);
LocalDate date = LocalDate.of(1978, Month.SEPTEMBER, 21);
System.out.println(date);
LocalTime time = LocalTime.of(22, 15);
System.out.println(time);
LocalTime time2 = LocalTime.parse("20:15:30");
更多内容详见 [更多内容详见 Java8 Tutorial 。
网友评论