汇报人:…星辰
日期:2022-11-16

本周重点

1.任务进展

学习了java8的基础知识,更深层次的了解了Java的新特性

2.相关数据

lambda

lambda表达式的重要特征:
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

Lambda 表达式实例:
1
2
3
4
5
// 1. 不需要参数,返回值为 5   () -> 5    
// 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)

Lambda 表达式需要注意以下两点:
  • Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
变量作用域:

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

方法引用

概念:

方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 ::

四种不同的方法引用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@FunctionalInterface
public interface Supplier<T> {
T get();
}

class Car {
//Supplier是jdk1.8的接口,这里和lambda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}

public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}

public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}

public void repair() {
System.out.println("Repaired " + this.toString());
}
}

构造器引用:

它的语法是Class::new,或者更一般的Class< T >::new

1
2
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

静态方法引用:
1
cars.forEach( Car::collide );

特定类的任意对象的方法引用:
1
cars.forEach( Car::repair );

特定对象的方法引用
1
finalCarpolice = Car.create(Car::new); cars.forEach(police::follow);

Stream

概念:

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

1
2
3
4
5
6
7
8
9
+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();

是什么

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
生成流两个方法
  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。
stream api八种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;

public class Java8Tester {
public static void main(String args[]){
System.out.println("使用 Java 7: ");

// 计算空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("列表: " +strings);
long count = getCountEmptyStringUsingJava7(strings);

System.out.println("空字符数量为: " + count);
count = getCountLength3UsingJava7(strings);

System.out.println("字符串长度为 3 的数量为: " + count);

// 删除空字符串
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("筛选后的列表: " + filtered);

// 删除空字符串,并使用逗号把它们合并起来
String mergedString = getMergedStringUsingJava7(strings,", ");
System.out.println("合并字符串: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

// 获取列表元素平方数
List<Integer> squaresList = getSquares(numbers);
System.out.println("平方数列表: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);

System.out.println("列表: " +integers);
System.out.println("列表中最大的数 : " + getMax(integers));
System.out.println("列表中最小的数 : " + getMin(integers));
System.out.println("所有数之和 : " + getSum(integers));
System.out.println("平均数 : " + getAverage(integers));
System.out.println("随机数: ");

// 输出10个随机数
Random random = new Random();

for(int i=0; i < 10; i++){
System.out.println(random.nextInt());
}

System.out.println("使用 Java 8: ");
System.out.println("列表: " +strings);

count = strings.stream().filter(string->string.isEmpty()).count();
System.out.println("空字符串数量为: " + count);

count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("字符串长度为 3 的数量为: " + count);

filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选后的列表: " + filtered);

mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);

squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("列表: " +integers);

IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();

System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
System.out.println("随机数: ");

random.ints().limit(10).sorted().forEach(System.out::println);

// 并行处理
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的数量为: " + count);
}

private static int getCountEmptyStringUsingJava7(List<String> strings){
int count = 0;

for(String string: strings){

if(string.isEmpty()){
count++;
}
}
return count;
}

private static int getCountLength3UsingJava7(List<String> strings){
int count = 0;

for(String string: strings){

if(string.length() == 3){
count++;
}
}
return count;
}

private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){
List<String> filteredList = new ArrayList<String>();

for(String string: strings){

if(!string.isEmpty()){
filteredList.add(string);
}
}
return filteredList;
}

private static String getMergedStringUsingJava7(List<String> strings, String separator){
StringBuilder stringBuilder = new StringBuilder();

for(String string: strings){

if(!string.isEmpty()){
stringBuilder.append(string);
stringBuilder.append(separator);
}
}
String mergedString = stringBuilder.toString();
return mergedString.substring(0, mergedString.length()-2);
}

private static List<Integer> getSquares(List<Integer> numbers){
List<Integer> squaresList = new ArrayList<Integer>();

for(Integer number: numbers){
Integer square = new Integer(number.intValue() * number.intValue());

if(!squaresList.contains(square)){
squaresList.add(square);
}
}
return squaresList;
}

private static int getMax(List<Integer> numbers){
int max = numbers.get(0);

for(int i=1;i < numbers.size();i++){

Integer number = numbers.get(i);

if(number.intValue() > max){
max = number.intValue();
}
}
return max;
}

private static int getMin(List<Integer> numbers){
int min = numbers.get(0);

for(int i=1;i < numbers.size();i++){
Integer number = numbers.get(i);

if(number.intValue() < min){
min = number.intValue();
}
}
return min;
}

private static int getSum(List numbers){
int sum = (int)(numbers.get(0));

for(int i=1;i < numbers.size();i++){
sum += (int)numbers.get(i);
}
return sum;
}

private static int getAverage(List<Integer> numbers){
return getSum(numbers) / numbers.size();
}
}

Optional 类

概念:

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。

类声明

public final class Optional extends Object

类方法
1 static Optional empty()
返回空的 Optional 实例。
2 boolean equals(Object obj)
判断其他对象是否等于 Optional。
3 Optional filter(Predicate<? super predicate)
如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
4 Optional flatMap(Function<? super T,Optional> mapper)
如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
5 T get()
如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
6 int hashCode()
返回存在值的哈希码,如果值不存在 返回 0。
7 void ifPresent(Consumer<? super T> consumer)
如果值存在则使用该值调用 consumer , 否则不做任何事情。
8 boolean isPresent()
如果值存在则方法会返回true,否则返回 false。
9 Optional map(Function<? super T,? extends U> mapper)
如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。
10 static Optional of(T value)
返回一个指定非null值的Optional。
11 static Optional ofNullable(T value)
如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
12 T orElse(T other)
如果存在该值,返回值, 否则返回 other。
13 T orElseGet(Supplier<? extends T> other)
如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
14 T orElseThrow(Supplier<? extends X> exceptionSupplier)
如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
15 String toString()
返回一个Optional的非空字符串,用来调试

Optional 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.Optional;

public class Java8Tester {
public static void main(String args[]){

Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);

// Optional.ofNullable - 允许传递为 null 参数
Optional<Integer> a = Optional.ofNullable(value1);

// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a,b));
}

public Integer sum(Optional<Integer> a, Optional<Integer> b){

// Optional.isPresent - 判断值是否存在

System.out.println("第一个参数值存在: " + a.isPresent());
System.out.println("第二个参数值存在: " + b.isPresent());

// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = a.orElse(new Integer(0));

//Optional.get - 获取值,值需要存在
Integer value2 = b.get();
return value1 + value2;
}
}

日期时间 API

旧版的日期时间api的缺点和不足
非线程安全
:::info
java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
:::
设计很差
:::info
Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
:::
时区处理麻烦
:::info
日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
:::
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。
  • Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;

public class Java8Tester {
public static void main(String args[]){
Java8Tester java8tester = new Java8Tester();
java8tester.testLocalDateTime();
}

public void testLocalDateTime(){

// 获取当前的日期时间
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("当前时间: " + currentTime);

LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: " + date1);

Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();

System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds);

LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2);

// 12 december 2014
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3: " + date3);

// 22 小时 15 分钟
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4);

// 解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.time.ZonedDateTime;
import java.time.ZoneId;

public class Java8Tester {
public static void main(String args[]){
Java8Tester java8tester = new Java8Tester();
java8tester.testZonedDateTime();
}

public void testZonedDateTime(){

// 获取当前时间日期
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
System.out.println("date1: " + date1);

ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);

ZoneId currentZone = ZoneId.systemDefault();
System.out.println("当期时区: " + currentZone);
}
}

思考

java迭代的很快,但是很多的知识都是在使用中逐渐走向完善,大家可以在Java的学习中使用java8,如果后期需要学习Java的新特性再去看官方文档就可以很快的入手,没必要一开始就使用最新版的,有可能新版不甚稳定.