JDK8~13新特性
接口新特性_JDK8
使用场景
接口里面定义公用的业务逻辑,抽取出来,每个子类都必须具备;静态方法可以充当工具类
默认方法 default
接⼝⾥⾯定义⼀个默认⽅法,这个接⼝的实现类实现了这个接⼝之后,不⽤管这个 default 修饰的⽅法就可以直接调⽤,即接⼝⽅法的默认实现。
Animal_interface
1public interface Animal {
2 void run();
3 void eat();
4
5 default void breath(){
6 System.out.println("使用氧气呼吸");
7 }
8
9 static void test(){
10 System.out.println("这个是静态方法");
11 }
12}
Dog
1public class Dog implements Animal {
2 @Override
3 public void run() {
4 System.out.println("dog 跑");
5 }
6
7 @Override
8 public void eat() {
9 System.out.println("dog 吃");
10 }
11}
静态方法
接⼝名.静态⽅法来访问接⼝中的静态⽅法
Main
1public class Main {
2
3 public static void main(String[] args) {
4
5 Dog dog = new Dog();
6 dog.eat();
7 dog.run();
8 dog.breath();
9
10 Animal.test();
11 }
12}
输出
1dog 吃
2dog 跑
3使用氧气呼吸
4这个是静态方法
Base64 加解密 API_JDK8
Base64 是⽹络上最常⻅的⽤于传输 8Bit 字节码的编码⽅式之⼀,Base64 就是 ⼀种基于 64 个可打印字符来表示⼆进制数据的⽅法 ;基于 64 个字符 A-Z,a-z,0-9,+,/的编码⽅式, 是⼀种能将任意⼆进制数据⽤ 64 种字元组合成字符串的⽅法,⽽这个⼆进制数据和字符串资料之 间是可以互相转换的,在实际应⽤上,Base64 除了能将⼆进制数据可视化之外,也常⽤来表示字 串加密过后的内容。
早期 Java 要使⽤ Base64 怎么做?
使⽤ JDK ⾥ sun.misc 套件下的 BASE64Encoder 和 BASE64Decoder 这两个类;编码和解码的效率比较差,公开信息说以后的版本会取消这个方法,如果使用 Apache Commons Codes 则需要引包。
1 BASE64Encoder encoder = new BASE64Encoder();
2 BASE64Decoder decoder = new BASE64Decoder();
3
4 String text = "麦富迪";
5
6 byte[] textByte = text.getBytes("UTF-8");
7
8 //编码
9 String encodedText = encoder.encode(textByte);
10 System.out.println(encodedText); //5bCP5ru06K++5xCC
11
12 //解码
13 System.out.println(new String(decoder.decodeBuffer(encodedText),"UTF-8")); //麦富迪
Jdk1.8 的 java.util 包中,新增了 Base64 的类
好处:不⽤引包,编解码效率远⼤于 sun.misc 和 Apache Commons Codec。
1 Base64.Encoder encoder = Base64.getEncoder();
2 Base64.Decoder decoder = Base64.getDecoder();
3 String text = "麦富迪";
4 byte[] textByte = text.getBytes("UTF-8");
5
6 //编码
7 String encodedText = encoder.encodeToString(textByte);
8 System.out.println(encodedText);
9
10 //解码
11 System.out.println(new String(decoder.decode(encodedText),"UTF-8"));
时间日期处理类_JDK8
时间处理再熟悉不过,SimpleDateFormat,Calendar 等类。旧版缺点: java.util.Date 是⾮线程安全 的 API 设计⽐较差,⽇期/时间对象⽐较,加减麻烦。
Java 8 通过发布新的 Date-Time API (JSR 310)来进⼀步加强对⽇期与时间的处理:
- 新增了很多常⻅的 API,如⽇期/时间的⽐较,加减,格式化等
- 包所在位置 java.time
- 核心类
1LocalDate:不包含具体时间。 只有日期。
2LocalTime:不含⽇期的时间。 只有时间。
3LocalDateTime:包含了⽇期及时间。 包含日期和时间。
示例
1 LocalDate today = LocalDate.now();
2
3 //LocalTime localTime = LocalTime.now();
4
5 //今天日期 2019-10-22
6 System.out.println("今天日期 "+ today);
7
8 //获取年月日,周几
9
10 //现在是哪年:2019
11 System.out.println("现在是哪年:" + today.getYear() );
12
13 //现在是哪月:OCTOBER
14 System.out.println("现在是哪月:" + today.getMonth() );
15
16 //现在是哪月(数字):10
17 System.out.println("现在是哪月(数字):" + today.getMonthValue() );
18
19 //现在是几号:22
20 System.out.println("现在是几号:" + today.getDayOfMonth() );
21
22 //现在是周几:TUESDAY
23 System.out.println("现在是周几:" + today.getDayOfWeek() );
24
25
26 //加减年份, 加后返回的对象才是修改的,旧的依然是旧的
27 LocalDate changeDate = today.plusYears(1);
28 //加后是哪年:2020
29 System.out.println("加后是哪年:"+changeDate.getYear());
30 //旧的是哪年:2019
31 System.out.println("旧的是哪年:"+today.getYear());
32
33 //日期比较:是否在指定时候之后 isAfter:true
34 System.out.println("isAfter:"+changeDate.isAfter(today));
LocalDate 常用 API
1//getYear() int 获取当前⽇期的年份
2//getMonth() Month 获取当前⽇期的⽉份对象
3//getMonthValue() int 获取当前⽇期是第⼏⽉
4//getDayOfWeek() DayOfWeek 表示该对象表示的⽇期是星期⼏
5//getDayOfMonth() int 表示该对象表示的⽇期是这个⽉第⼏天
6//getDayOfYear() int 表示该对象表示的⽇期是今年第⼏天
7//withYear(int year) LocalDate 修改当前对象的年份
8//withMonth(int month) LocalDate 修改当前对象的⽉份
9//withDayOfMonth(int dayOfMonth) LocalDate 修改当前对象在当⽉的⽇ 期
10//plusYears(long yearsToAdd) LocalDate 当前对象增加指定的年份数
11//plusMonths(long monthsToAdd) LocalDate 当前对象增加指定的⽉份数
12//plusWeeks(long weeksToAdd) LocalDate 当前对象增加指定的周数
13//plusDays(long daysToAdd) LocalDate 当前对象增加指定的天数
14//minusYears(long yearsToSubtract) LocalDate 当前对象减去指定的年 数
15//minusMonths(long monthsToSubtract) LocalDate 当前对象减去注定的 ⽉数
16//minusWeeks(long weeksToSubtract) LocalDate 当前对象减去指定的周 数
17//minusDays(long daysToSubtract) LocalDate 当前对象减去指定的天数
18//compareTo(ChronoLocalDate other) int ⽐较当前对象和other对象在时 间上的⼤⼩,返回值如果为正,则当前对象时间较晚, //isBefore(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在 other对象⽇期之前
19//isAfter(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在 other对象⽇期之后
20//isEqual(ChronoLocalDate other) boolean ⽐较两个⽇期对象是否相等
日期格式化
JDK8 之前:SimpleDateFormat 来进⾏格式化,但 SimpleDateFormat 并不是线程安全的。
JDK8 之后:引⼊线程安全的⽇期与时间 DateTimeFormatter
示例
1 /**
2 * 日期格式化:年-月-日 时:分:秒
3 */
4 LocalDateTime ldt = LocalDateTime.now();
5 System.out.println(ldt);
6 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH🇲🇲ss");
7 String ldtStr = dtf.format(ldt);
8 System.out.println(ldtStr);
9
10 /**
11 * 自定义 日期时间对象
12 */
13 LocalDateTime ldt = LocalDateTime.of(2020, 11, 11, 8, 20, 30);
14 System.out.println(ldt);
15
16 //当前日期时间对象
17 LocalDateTime today = LocalDateTime.now();
18 System.out.println(today);
19
20 //自定义日期时间对象
21 LocalDateTime changeDate = LocalDateTime.of(2020, 10, 28, 12, 32, 30);
22 System.out.println(changeDate);
23
24 //比较两个日期时间对象
25 Duration duration = Duration.between(today,changeDate);
26 System.out.println("相差的天数:" + duration.toDays()); //两个时间差的天数
27 System.out.println("相差的小时数:" + duration.toHours()); //两个时间差的小时数
28 System.out.println("相差的秒数:" + duration.toMillis() / 1000); //两个时间差的秒数
29 System.out.println("相差的分钟数:" + duration.toMinutes()); //两个时间差的分钟数
30 System.out.println("相差的毫秒数:" + duration.toMillis()); //两个时间差的毫秒数
31 System.out.println("相差的纳秒数:" + duration.toNanos()); //两个时间差的纳秒数
Optional 类_JDK8
主要解决的问题是空指针异常(NullPointer Exception)
本质是⼀个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 /**
6 * Optional包装类可以避免空指针:因为Optional类本身可以允许null存在
7 * Optional.of()
8 * opt.isPresent()
9 */
10 Student student = new Student();
11
12 //Optional.of() 接收的参数不能为null,如果为null则会抛出异常。
13 //Optional<Student> opt = Optional.of(student);
14
15 //Optional.ofNullable() 接收的参数可以为null
16 Optional<Student> opt = Optional.ofNullable(student);
17
18 //判断是否为null,不为null才调用get(),不为null opt.isPresent()返回true
19 if(opt.isPresent()){
20 System.out.println("optional不为空");
21 Student s = opt.get();
22 }else {
23 System.out.println("optional为空");
24 }
25
26 /**
27 * 兜底数据
28 * Optional.ofNullable(student1).orElse(student2);
29 */
30 //Student student1 = new Student(5);
31 Student student1 = null;
32 Student student2 = new Student(2);
33 //兜底数据:当没有数据的时候会使用兜底数据
34 Student result = Optional.ofNullable(student1).orElse(student2);
35 System.out.println(result.getAge());
36
37
38 //Student std = null;
39 //Student std = new Student(10);
40 int res = Optional.ofNullable(std).map(obj->obj.getAge()).orElse(7);
41 System.out.println(res); //7
42 }
43}
Lambda 表达式_JDK8
在 JDK8 之前,Java 是不⽀持函数式编程的,所谓的函数编程,即可理解是将⼀个函数(也称为“⾏ 为”)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的 POJO 类),⽽函数式编 程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递)。
使用场景:Lambda 表达式 使⽤场景(前提):⼀个接⼝中只包含⼀个⽅法,则可以使⽤ Lambda 表达式,这样 的接⼝称之为“函数接⼝”
Lambda 表达式的优点
- Lambda 表达式的实现⽅式在本质是以匿名内部类的⽅式进⾏实现。
- 重构现有臃肿代码,更⾼的开发效率,尤其是集合 Collection 操作的时候。
语法: (params) -> expression
1第⼀部分为括号内⽤逗号分隔的形式参数,参数是函数式接⼝⾥⾯⽅法的参数;
2第⼆部分为⼀个箭 头符号:->
3第三部分为⽅法体,可以是表达式和代码块
4
5参数列表
6 括号中参数列表的数据类型可以省略不写
7 Collections.sort(lists,( a, b)-> b.compareTo(a));
8 括号中的参数只有⼀个,那么参数类型和()都可以省略不写
9 Collections.sort(lists, b -> System.out.println(b));
10
11⽅法体
12 如果{}中的代码只有⼀⾏,⽆论有返回值,可以省略{},return,分号,要⼀起省略,其他 则需要加上。
13 new Thread(()-> System.out.println("test")).start();
14 如果有多行代码,则不能省略{},分号,如果有返回值需要加return
15 new Thread(() -> {
16 System.out.println("1");
17 System.out.println("2");
18 }).start();
示例代码
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 /**
5 * 创建线程对比
6 */
7 //匿名内部类
8 new Thread(new Runnable() {
9 @Override
10 public void run() {
11 System.out.println("test");
12
13 }
14 }).start();
15 //Lambda表达式
16 new Thread(()->System.out.println("test")).start();
17
18 /**
19 * 字符串排序:降序 ggg fff ccc aaa
20 */
21 List<String> list = Arrays.asList("aaa","ggg","fff","ccc");
22 //匿名内部类
23 Collections.sort(list, new Comparator<String>() {
24 @Override
25 public int compare(String a, String b) {
26 return b.compareTo(a);
27 }
28 });
29 for(String string : list){
30 System.out.println(string);
31 }
32
33 List<String> lists = Arrays.asList("aaa","ggg","fff","ccc");
34 //Lambda表达式
35 Collections.sort(lists,( a, b)-> b.compareTo(a));
36 for(String string : lists){
37 System.out.println(string);
38 }
39 }
40}
自定义函数式编程实战_JDK8
定义 lambda 接口流程
- 定义一个函数式接口:需要标注此接口 @FunctionalInterface,否则万一团队成员在接口上加了其他方法则容易出故障。
- 编写一个方法,输入需要操作的数据和接口
- 在调用方法时传入数据和 Lambda 表达式,用来操作数据。
示例:定义一个可以使用加减乘除的接口
定义函数式接口
1@FunctionalInterface
2public interface OperFunction<R,T> {
3 R operator(T t1, T t2);
4}
使用 Lambda 方式传入函数的行为
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 System.out.println(operator(20, 5, (Integer x, Integer y) -> {
6 return x * y;
7 }));
8
9 System.out.println(operator(20, 5, (x, y) -> x + y));
10 System.out.println(operator(20, 5, (x, y) -> x - y));
11 System.out.println(operator(20, 5, (x, y) -> x / y));
12
13 }
14
15 public static Integer operator(Integer x, Integer y, OperFunction<Integer, Integer> of) {
16 return of.operator(x, y);
17 }
18}
函数式编程 Function _JDK8
Lambda 表达式必须先定义接⼝,创建相关⽅法之后才可使⽤,这样做⼗分不便,其实 java8 已经 内置了许多接⼝, 例如下⾯四个功能型接⼝,所以⼀般很少会由⽤户去定义新的函数式接⼝。
1Java8 内置的四⼤核⼼函数式接⼝
2
3Consumer<T>: 消费型接⼝:有⼊参,⽆返回值
4 void accept(T t);
5
6Supplier<T>: 供给型接⼝:⽆⼊参,有返回值
7 T get();
8
9Function<T, R>:: 函数型接⼝:有⼊参,有返回值
10 R apply(T t);
11
12Predicate<T>: 断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean
13 boolean test(T t);
Function
1主要作用
2 传入一个值经过函数的计算返回另一个值
3
4函数型接⼝:有⼊参,有返回值
5 T:入参类型 R: 返回类型
6
7调用方法
8 R apply(T t)
Function 接口源码
1@FunctionalInterface
2public interface Function<T, R> {
3
4 /**
5 * Applies this function to the given argument.
6 *
7 * @param t the function argument
8 * @return the function result
9 */
10 R apply(T t);
11
12}
示例
1// Function<Integer,Integer> func = p->{
2// System.out.println("我是函数");
3// return p*100;
4// };
5
6 Function<Integer,Integer> func = p->p*100;
7 System.out.println(func.apply(10));
BiFunction_JDK8
Function 只能接收⼀个参数,如果要传递两个参数,则⽤ BiFunction
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 /**
6 * 传递函数行为
7 */
8 System.out.println(operator(10,21,(a,b)->a+b));
9 System.out.println(operator(10,2,(a,b)->a-b));
10 System.out.println(operator(8,4,(a,b)->a*b));
11 System.out.println(operator(10,2,(a,b)->a/b));
12
13 /**
14 * 常规使用
15 */
16 BiFunction<Integer,Integer,Integer> func = (a,b)-> a+b;
17 System.out.println(func.apply(1,2));
18
19 }
20
21 /**
22 * 自定义行为
23 * @param a
24 * @param b
25 * @param bf
26 * @return
27 */
28 public static Integer operator(Integer a, Integer b, BiFunction<Integer,Integer,Integer> bf){
29
30 return bf.apply(a,b);
31 }
32}
Consumer_JDK8
1主要作用
2 因为没有返回值,常用于打印、发短信等消费动作
3
4消费型接⼝:有⼊参,无返回值
5 T:入参类型 R: 返回类型
6
7调用方法
8 void accept(T t);
接口源码
1@FunctionalInterface
2public interface Consumer<T> {
3
4 /**
5 * Performs this operation on the given argument.
6 *
7 * @param t the input argument
8 */
9 void accept(T t);
10}
示例 Java
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 Consumer<String> consumer = obj->{
6 System.out.println(obj);
7 System.out.println("调用短信接口发送短信,或者打印日志");
8 };
9
10 /**
11 * 简写
12 */
13// sendMsg("8888888",obj->{
14// System.out.println(obj);
15// System.out.println("调用短信接口发送短信,或者打印日志");
16// });
17
18 sendMsg("8888888",consumer);
19
20 }
21
22 public static void sendMsg(String phone,Consumer<String> consumer){
23 consumer.accept(phone);
24 }
25}
典型应用 ForEach
1 List<String> list = Arrays.asList("aaa","bbb");
2 //只有入参,没有返回值
3 list.forEach(obj->{
4 System.out.println(obj);
5 });
6
7//查看forEach源码
8 default void forEach(Consumer<? super T> action) {
9 Objects.requireNonNull(action);
10 for (T t : this) {
11 action.accept(t);
12 }
13 }
Supplier_JDK8
1主要作用
2 泛型⼀定和⽅法的返回值类型是⼀种类型,如果需要获得⼀个数据,并且不需要传⼊参数,可 以使⽤Supplier接⼝,例如 ⽆参的⼯⼚⽅法,即⼯⼚设计模式创建对象,简单来说就是 提供者。
3
4供给型接口:无⼊参,无返回值
5 T:返回值类型
6
7调用方法
8 T get();
接口源码
1@FunctionalInterface
2public interface Supplier<T> {
3
4 /**
5 * Gets a result.
6 *
7 * @return a result
8 */
9 T get();
10}
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 //Student student = new Student();
6
7 Student student = newStudent();
8 System.out.println(student.getName());
9 }
10
11 public static Student newStudent(){
12 Supplier<Student> supplier = ()-> {
13 Student student = new Student();
14 student.setName("默认名称");
15 return student;
16 };
17 return supplier.get();
18 }
19}
20
21class Student{
22 private String name;
23
24 public String getName() {
25 return name;
26 }
27
28 public void setName(String name) {
29 this.name = name;
30 }
31}
Predicate_JDK8
1主要作用
2 接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据.
3
4断言型接口:有⼊参,有返回值
5 T:入参类型
6 Boolean:返回值类型
7
8调用方法
9 boolean test(T t);
接口源码
1@FunctionalInterface
2public interface Predicate<T> {
3
4 /**
5 * Evaluates this predicate on the given argument.
6 *
7 * @param t the input argument
8 * @return {@code true} if the input argument matches the predicate,
9 * otherwise {@code false}
10 */
11 boolean test(T t);
12}
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 List<String> list = Arrays.asList("asdfdsfdsfds","xcvsdfsdf","sdfsdf","awwa","ad4432","dsfsdf");
6
7 //filter是过滤器:过滤器可以接收一个
8 //添加过滤条件: obj->obj.startsWith("a")
9 List<String> results = filter(list,obj->obj.startsWith("a"));
10
11 System.out.println(list);
12 System.out.println(results);
13 }
14
15 public static List<String> filter(List<String> list, Predicate<String> predicate){
16 List<String> results = new ArrayList<>();
17 for(String str:list){
18 //使用predicate,对每个数据进行过滤: Predicate接口中的test()方法
19 if(predicate.test(str)){
20 results.add(str);
21 }
22 }
23 return results;
24 }
25}
方法与构造函数引用_JDK8
⽅法引⽤是⼀种更简洁易懂的 lambda 表达式,操作符是双冒号::,⽤来直接访问类或者实例已经存在的方法或构造⽅法。
通过⽅法引⽤,可以将⽅法的引⽤赋值给⼀个变量。
语法:
1 左边是容器(可以是类名,实例名)
2 中间是" :: "
3 右边是相应的⽅法名
4
5静态⽅法
6 则是ClassName::methodName。如 Object ::equals
7实例⽅法
8 则是Instance::methodName
9构造函数
10 则是 类名::new;
11
12单个参数
13 Function<⼊参1, 返回类型> func = ⽅法引⽤
14 应⽤ func.apply(⼊参);
15
162个参数
17 BiFunction<⼊参1,⼊参2, 返回类型> func = ⽅法引⽤
18 应⽤ func.apply(⼊参1,⼊参2
示例
1import java.util.function.BiFunction;
2import java.util.function.Function;
3
4public class Main {
5
6 public static void main(String[] args) throws Exception {
7
8 /**
9 * 使用双冒号:: 来构造静态函数的引用
10 */
11 //Integer.parseInt("1024");
12 Function<String, Integer> fun = Integer::parseInt;
13 Integer value = fun.apply("1024");
14 System.out.println(value);
15
16 /**
17 * 使用双冒号::来构造非静态函数引用
18 */
19 String content = "一二三四";
20 Function<Integer, String> func = content::substring;
21 String result = func.apply(1);
22 System.out.println(result); //二三四
23
24 /**
25 * 构造函数引用,单个参数 Function<T,R>
26 */
27 Function<String,User> function = User::new;
28 User user2 = function.apply("顶顶");
29 System.out.println(user2);
30
31 /**
32 * 构造函数引用,多个参数 BiFunction<T,T,R>
33 */
34 BiFunction<String,Integer,User> biFunction = User::new;
35 User user1 = biFunction.apply("秃秃",32);
36 System.out.println(user1);
37
38 /**
39 * 函数引用:可以将函数引用作为方法的参数: String::toUpperCase 作为sayHello() 的参数
40 * 小写转大写
41 * String s = "abc".toUpperCase();
42 */
43 sayHello(String::toUpperCase,"abc1024.pub");
44 }
45
46
47 private static void sayHello(Function<String,String> func,String param){
48 String result = func.apply(param);
49 System.out.println(result);
50 }
51
52}
53
54class User {
55 private String name;
56 private int age;
57
58 public User() {
59 }
60
61 public User( String name,int age) {
62 this.age = age;
63 this.name = name;
64 }
65
66 public User(String name) {
67 this.name = name;
68 }
69
70
71 public String getName() {
72 return name;
73 }
74
75 public void setName(String name) {
76 this.name = name;
77 }
78
79 public int getAge() {
80 return age;
81 }
82
83 public void setAge(int age) {
84 this.age = age;
85 }
86
87 @Override
88 public String toString() {
89 return "User{" +
90 "name='" + name + '\'' +
91 ", age=" + age +
92 '}';
93 }
94}
流 Stream_JDK8
Stream 中⽂称为 “流”,通过将集合转换为这么⼀种叫做 “流”的元素队列,通过声明性⽅式, 能够对集合中的每个元素进⾏⼀系列并⾏或串⾏的流⽔线操作。
元素是特定类型的对象,所以元素集合看作⼀种流, 流在管道中传输, 且可以在管道的节点 上进⾏处理, ⽐如 排序,聚合,过滤等操作。
操作详情
- 数据元素便是原始集合,如 List、Set、Map 等
- ⽣成流,可以是串⾏流 stream() 或者并⾏流 parallelStream()
- 中间操作,可以是 排序,聚合,过滤,转换等
- 终端操作,很多流操作本身就会返回⼀个流,所以多个操作可以直接连接起来,最后统⼀进 ⾏收集
示例
1import java.util.Arrays;
2import java.util.HashMap;
3import java.util.List;
4import java.util.function.BiFunction;
5import java.util.function.Function;
6import java.util.stream.Collectors;
7
8public class Main {
9
10 public static void main(String[] args) throws Exception {
11
12 List<String> list = Arrays.asList("springboot","微服务","并发编程","redis","消息队列");
13
14 List<String> resultList = list.stream().map(obj->"学习"+obj).collect(Collectors.toList());
15
16 System.out.println(resultList); //[学习springboot, 学习微服务, 学习并发编程, 学习redis, 学习消息队列]
17 }
18}
map 函数
场景:转换对象,如 javaweb 开发中集合⾥⾯的 DO 对象转换为 DTO 对。
DO
1public class User {
2
3 private int id;
4
5 private String name;
6
7 private String pwd;
8
9 public User(){
10
11 }
12
13 public User(int id, String name,String pwd){
14 this.id = id;
15 this.name = name;
16 this.pwd = pwd;
17 }
18
19
20 public int getId() {
21 return id;
22 }
23
24 public void setId(int id) {
25 this.id = id;
26 }
27
28 public String getName() {
29 return name;
30 }
31
32 public void setName(String name) {
33 this.name = name;
34 }
35
36 public String getPwd() {
37 return pwd;
38 }
39
40 public void setPwd(String pwd) {
41 this.pwd = pwd;
42 }
43}
44
DTO
1public class UserDTO {
2
3 private int userId;
4
5 private String username;
6
7
8 public UserDTO(){}
9
10 public UserDTO(int userId, String username){
11 this.userId = userId;
12 this.username = username;
13 }
14
15
16 public int getUserId() {
17 return userId;
18 }
19
20 public void setUserId(int userId) {
21 this.userId = userId;
22 }
23
24 public String getUsername() {
25 return username;
26 }
27
28 public void setUsername(String username) {
29 this.username = username;
30 }
31
32 @Override
33 public String toString() {
34 return "UserDTO{" +
35 "userId=" + userId +
36 ", username='" + username + '\'' +
37 '}';
38 }
39}
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 /**
5 * TO to TDO
6 */
7 List<User> list = Arrays.asList(new User(1, "小东", "123"),
8 new User(21, "jack", "rawer"),
9 new User(155, "tom", "sadfsdfsdfsd"),
10 new User(231, "marry", "234324"),
11 new User(100, "小D", "122223")
12 );
13 List<UserDTO> userDTOList = list.stream().map(obj -> {
14 UserDTO userDTO = new UserDTO(obj.getId(), obj.getName());
15 return userDTO;
16 }).collect(Collectors.toList());
17 System.out.println(userDTOList); //[UserDTO{userId=1, username='小东'}, UserDTO{userId=21, username='jack'}, UserDTO{userId=155, username='tom'}, UserDTO{userId=231, username='marry'}, UserDTO{userId=100, username='小D'}]
18
19 }
20}
filter 函数
⽤于通过设置的条件过滤出元素.
需求:过滤出字符串⻓度⼤于 5 的字符串
1 /**
2 * filter过滤
3 */
4 List<String> list2 = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
5 List<String> resultList = list2.stream().filter(obj -> obj.length() > 5).collect(Collectors.toList());
6 System.out.println(resultList); //[springboot, springcloud, docker]
sorted 与 limit 函数
对流进⾏⾃然排序, 其中的元素必须实现 Comparable 接⼝。
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 /**
6 * 对元素进行排序,排序的元素必须实现 Comparable 接⼝
7 */
8 List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
9 List<String> resultList1 = list.stream().sorted().collect(Collectors.toList());
10 System.out.println(resultList1);
11
12
13 //根据长度排序:升序
14 //List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());
15
16 //根据长度排序:降序
17 //List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());
18
19 //函数引用的方式:降序 链式调用
20 List<String> resultList2 = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());
21 System.out.println(resultList2);
22
23 //limit截取: 先降序,再截取前三个
24 List<String> resultList3 = list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());
25
26 System.out.println(resultList3);
27 }
28}
allMatch 和 anyMatch 函数
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4
5 List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
6
7 //全部符合
8 boolean flag1 = list.stream().allMatch(obj->obj.length()>5);
9 System.out.println(flag1);
10
11 //部分符合
12 boolean flag2 = list.stream().anyMatch(obj->obj.length()>5);
13 System.out.println(flag2);
14 }
15}
max 和 min 函数
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 List<Student> list = Arrays.asList(
5 new Student(32),
6 new Student(33),
7 new Student(21),
8 new Student(29),
9 new Student(18)
10 );
11
12 /**
13 * 寻找年龄最大的学生
14 */
15
16 //list.stream().max(Comparator.comparingInt(Student::getAge));
17
18// Optional<Student> optionalStudent = list.stream().max((s1, s2)->{
19// return Integer.compare(s1.getAge(),s2.getAge());
20// });
21
22 /**
23 * 寻找年龄最小的学生
24 */
25 Optional<Student> optionalStudent = list.stream().min((s1, s2) -> {
26 return Integer.compare(s1.getAge(), s2.getAge());
27 });
28
29 Student student = optionalStudent.get();
30 System.out.println(student.getAge());
31
32 }
33}
34
35
36class Student {
37 private int age;
38
39 public Student() {
40 }
41
42 public Student(int age) {
43 this.age = age;
44 }
45 public int getAge() {
46 return age;
47 }
48 public void setAge(int age) {
49 this.age = age;
50 }
51}
并行流 parallelStream_JDK8
集合做重复的操作,如果使⽤串⾏执⾏会相当耗时,因此⼀般会采⽤多线程来加快, Java8 的 paralleStream ⽤ fork/join 框架提供了并发执⾏能⼒。
底层原理
- 线程池(ForkJoinPool)维护⼀个线程队列
- 可以分割任务,将⽗任务拆分成⼦任务,完全贴合分治思想
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 /**
5 * 并行无法保证执行的有序性,执行过程是乱序随机的
6 * 适合对顺序无要求的场景
7 */
8 List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9);
9 numbers.stream().forEach(System.out::println);//有序
10 numbers.parallelStream().forEach(System.out::println);//乱序
11
12 /**
13 * 操作并行集合需要使用线程安全的集合:CopyOnWriteArrayList
14 */
15 for(int i=0;i<10;i++){
16 //List list = new ArrayList();
17 List list = new CopyOnWriteArrayList();//线程安全,每次add之前加锁
18 IntStream.range(0,100).parallel().forEach(list::add);
19 System.out.println(list.size());
20 }
21 }
22}
paralleStream 并⾏是否⼀定⽐ Stream 串⾏快?
数据量少的情况,可能串⾏更快,ForkJoin 会耗性能。
多数情况下并⾏⽐串⾏快,是否可以都⽤并⾏
不⾏,部分情况会有线程安全问题,parallelStream ⾥⾯使⽤的外部变量,⽐如集合⼀ 定要使⽤线程安全集合,不然就会引发多线程安全问题。
reduce_JDK8
聚合操作,中⽂意思是 “减少”;根据⼀定的规则将 Stream 中的元素进⾏计算后返回⼀个唯⼀的值。
接口源码
1//accumulator 计算的累加器
2Optional<T> reduce(BinaryOperator<T> accumulator);
示例
1 //lambda
2 int value1 = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();
3 System.out.println(value1);
4
5 //匿名内部类
6 int value2 = Stream.of(1, 2, 3, 4, 5).reduce(new BinaryOperator<Integer>() {
7 @Override
8 public Integer apply(Integer integer, Integer integer2) {
9 return integer + integer2;
10 }
11 }).get();
12 System.out.println(value2);
接口源码
1T reduce(T identity, BinaryOperator<T> accumulator);
示例
1 /**
2 * 例⼦: 100作为初始值,然后和第⼀个元素相加,结果在和第⼆个元素相加,直到全部相加完成
3 * identity ⽤户提供⼀个循环计算的初始值
4 * accumulator 计算的累加器
5 */
6 int value = Stream.of(1, 2, 3, 4, 5).reduce(100,(item1,item2)->item1+item2);
7 System.out.println(value);
练习:求最⼤值
1 int value3 = Stream.of(1645, 234345, 32, 44434, 564534, 435, 34343542, 212)
2 .reduce((item1, item2) -> item1 > item2 ? item1 : item2).get();
3 System.out.println(value);
forech_JDK8
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 /**
5 * forEach使用
6 */
7 List<String> list = Arrays.asList("sdfsdf", "xxxx", "bbb");
8 list.forEach(obj -> System.out.println(obj));
9 list.forEach(System.out::println);
10
11 List<Student> studentList = Arrays.asList(
12 new Student(2),
13 new Student(21),
14 new Student(32),
15 new Student(1)
16 );
17
18 int age = 0;
19
20 /**
21 * forEach注意点
22 * 不能修改包含外部的变量的值: 这里age
23 * 不能⽤break或者return或者continue等关键词结束或者跳过循环
24 */
25 studentList.forEach(student -> {
26 System.out.println(student.getAge());
27 if (student.getAge() == 21) {
28 System.out.println("进入if");
29 return;
30 //break;
31 }
32 });
33 }
34}
35
36class Student {
37
38 private int age;
39
40 public Student(int age) {
41 this.age = age;
42 }
43
44 public int getAge() {
45 return age;
46 }
47
48 public void setAge(int age) {
49 this.age = age;
50 }
51}
收集器_collector_JDK8
collect()⽅法的作⽤
- ⼀个终端操作, ⽤于对流中的数据进⾏归集操作,collect ⽅法接受的参数是⼀个 Collector。
- 有两个重载⽅法,在 Stream 接⼝⾥⾯。
1//重载方法一
2 <R> R collect(Supplier<R> supplier,
3 BiConsumer<R, ? super T> accumulator,
4 BiConsumer<R, R> combiner);
5
6//重载方法二 推荐(第一种方法需要自定义,第二个方法提供了很多收集器)
7<R, A> R collect(Collector<? super T, A, R> collector);
Collector 的作⽤
就是收集器,也是⼀个接⼝, 它的⼯具类 Collectors 提供了很多⼯⼚⽅法
- Collectors.toMap()
- Collectors.toSet()
- Collectors.toCollection() :
1Collectors.toCollection(LinkedList::new)
2Collectors.toCollection(CopyOnWriteArrayList::new)
3Collectors.toCollection(TreeSet::new)
Collectors.toList()源码
1/**
2 * ArrayList::new,创建⼀个ArrayList作为累加器
3 * List::add,对流中元素的操作就是直接添加到累加器中
4 * reduce操作, 对⼦任务归集结果addAll,后⼀个⼦任务的结果直接全部添加到 前⼀个⼦任务结果中
5*/ CH_ID 是⼀个unmodifiableSet集合
6 public static <T>
7 Collector<T, ?, List<T>> toList() {
8 return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
9 (left, right) -> { left.addAll(right); return left; },
10 CH_ID);
11 }
示例
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 List<String> list = Arrays.asList("sdfsdf","xxxx","bbb","bbb");
5
6 //List<String> results = list.stream().collect(Collectors.toList());
7 //list.stream().collect(Collectors.toSet());
8
9 //List<String> result = list.stream().collect(Collectors.toCollection(LinkedList::new));
10 List<String> result = list.stream().collect(Collectors.toCollection(CopyOnWriteArrayList::new));
11 Set<String> stringSet = list.stream().collect(Collectors.toCollection(TreeSet::new));
12 System.out.println(result);
13 System.out.println(stringSet);
14
15 }
16}
字符串拼接_joing 函数_JDK8
三种重载方法
1Collectors.joining()
2Collectors.joining("param")
3Collectors.joining("param1", "param2", "param3")
Collectors.joining()源码
1 public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
2 return joining(delimiter, "", "");
3 }
4
5 public static Collector<CharSequence, ?, String> joining() {
6 return new CollectorImpl<CharSequence, StringBuilder, String>(
7 StringBuilder::new, StringBuilder::append,
8 (r1, r2) -> { r1.append(r2); return r1; },
9 StringBuilder::toString, CH_NOID);
10 }
11
12 public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
13 CharSequence prefix,
14 CharSequence suffix) {
15 return new CollectorImpl<>(
16 () -> new StringJoiner(delimiter, prefix, suffix),
17 StringJoiner::add, StringJoiner::merge,
18 StringJoiner::toString, CH_NOID);
19 }
示例
1public class Main {
2 public static void main(String[] args) throws Exception {
3 List<String> list = Arrays.asList("springboot","springcloud","java","dubbo");
4
5 String result1 = list.stream().collect(Collectors.joining());
6 System.out.println(result1);
7
8 String result2 = list.stream().collect(Collectors.joining("||"));
9 System.out.println(result2);
10
11 String result3 = list.stream().collect(Collectors.joining("||","[","]"));
12 System.out.println(result3);
13
14 //该⽅法可以将Stream得到⼀个字符串, joining函数接受三个参数,分别表示 元素之间的连 接符、前缀和后缀。
15 String result = Stream.of("springboot","mysql","html5","css3").collect(Collectors.joining(",","[","]"));
16 System.out.println(result);
17 }
18}
收集器_partitioningBy_分组_JDK8
partitioningBy 源码
1 public static <T>
2 Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
3 return partitioningBy(predicate, toList());
4 }
练习: 根据 list ⾥⾯进⾏分组,字符串⻓度⼤于 4 的为⼀组,其他为另外⼀组
1import java.util.Arrays;
2import java.util.List;
3import java.util.Map;
4import static java.util.stream.Collectors.*;
5
6public class Main {
7
8 public static void main(String[] args) throws Exception {
9 List<String> list = Arrays.asList("java", "springboot", "HTML5", "CSS3");
10 Map<Boolean, List<String>> result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));
11 System.out.println(result); // {false=[java, CSS3], true=[springboot, HTML5]}
12 }
13}
收集器_groupby_进阶_JDK8
源码
1 public static <T, K> Collector<T, ?, Map<K, List<T>>>
2 groupingBy(Function<? super T, ? extends K> classifier) {
3 return groupingBy(classifier, toList());
4 }
示例
1import java.util.Arrays;
2import java.util.List;
3import java.util.Map;
4import java.util.stream.Collectors;
5
6public class Main {
7 public static void main(String[] args) throws Exception {
8 //数据
9 List<Student> students = Arrays.asList(
10 new Student("广东", 23),
11 new Student("广东", 24),
12 new Student("广东", 23),
13 new Student("北京", 22),
14 new Student("北京", 20),
15 new Student("北京", 20),
16 new Student("海南", 25)
17 );
18
19 //按照省份进行分组
20 Map<String,List<Student>> listMap = students.stream().collect(Collectors.groupingBy(obj->obj.getProvince()));
21 listMap.forEach((key,value)->{
22 System.out.println(key+"省:");
23 value.forEach(obj->{
24 System.out.println(obj.getAge());
25 });
26 });
27 }
28}
29
30class Student {
31 private int age;
32 private String province;
33
34 public Student(String province, int age) {
35 this.province = province;
36 this.age = age;
37 }
38
39 public Student() {
40 }
41
42 public int getAge() {
43 return age;
44 }
45
46 public void setAge(int age) {
47 this.age = age;
48 }
49
50 public String getProvince() {
51 return province;
52 }
53
54 public void setProvince(String province) {
55 this.province = province;
56 }
57}
Collectors.counting 统计元素个数_JDK8
源码
1 public static <T, K, A, D>
2 Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
3 Collector<? super T, A, D> downstream) {
4 return groupingBy(classifier, HashMap::new, downstream);
5 }
示例:统计各个省份的⼈数
1import java.util.Arrays;
2import java.util.List;
3import java.util.Map;
4import java.util.stream.Collectors;
5
6public class Main {
7
8 public static void main(String[] args) throws Exception {
9
10 List<Student> students = Arrays.asList(
11 new Student("广东", 23),
12 new Student("广东", 24),
13 new Student("广东", 23),
14 new Student("北京", 22),
15 new Student("北京", 20),
16 new Student("北京", 20),
17 new Student("海南", 25)
18 );
19
20 Map<String,Long> listMap = students.stream().collect(Collectors.groupingBy(obj->obj.getProvince(),Collectors.counting()));
21
22 listMap.forEach((key,value)->{
23 System.out.println(key+"人数有:"+value);
24 });
25 }
26}
27
28class Student {
29 private int age;
30 private String province;
31
32 public Student(String province, int age) {
33 this.province = province;
34 this.age = age;
35 }
36
37 public Student() {
38 }
39
40 public int getAge() {
41 return age;
42 }
43
44 public void setAge(int age) {
45 this.age = age;
46 }
47
48 public String getProvince() {
49 return province;
50 }
51
52 public void setProvince(String province) {
53 this.province = province;
54 }
55}
summarizing 集合统计_JDK8
作⽤:可以⼀个⽅法把统计相关的基本上都完成
源码
1 public static <T>
2 Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) {
3 return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(
4 IntSummaryStatistics::new,
5 (r, t) -> r.accept(mapper.applyAsInt(t)),
6 (l, r) -> { l.combine(r); return l; }, CH_ID);
7 }
示例:统计学⽣的各个年龄信息
1import java.util.Arrays;
2import java.util.IntSummaryStatistics;
3import java.util.List;
4import java.util.Map;
5import java.util.stream.Collectors;
6
7public class Main {
8
9 public static void main(String[] args) throws Exception {
10 List<Student> students = Arrays.asList(
11 new Student("广东", 23),
12 new Student("广东", 24),
13 new Student("广东", 23),
14 new Student("北京", 22),
15 new Student("北京", 20),
16 new Student("北京", 20),
17 new Student("海南", 25)
18 );
19
20 IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
21
22 System.out.println("平均值"+summaryStatistics.getAverage());//平均值22.428571428571427
23 System.out.println("人数"+summaryStatistics.getCount());//人数7
24 System.out.println("最大值"+summaryStatistics.getMax());//最大值25
25 System.out.println("最小值"+summaryStatistics.getMin());//最小值20
26 System.out.println("总和"+summaryStatistics.getSum());//总和157
27 }
28}
29
30class Student {
31 private int age;
32 private String province;
33
34 public Student(String province, int age) {
35 this.province = province;
36 this.age = age;
37 }
38
39 public Student() {
40 }
41
42 public int getAge() {
43 return age;
44 }
45 public void setAge(int age) {
46 this.age = age;
47 }
48 public String getProvince() {
49 return province;
50 }
51 public void setProvince(String province) {
52 this.province = province;
53 }
54}
Collection、Lambda 练习
准备数据
POJO
1package chapter8;
2
3
4public class VideoOrder {
5
6 private String tradeNo;
7
8 private int money;
9
10 private String title;
11
12 public VideoOrder(String tradeNo, String title, int money) {
13 this.tradeNo = tradeNo;
14 this.title = title;
15 this.money = money;
16 }
17
18 public String getTradeNo() {
19 return tradeNo;
20 }
21
22 public void setTradeNo(String tradeNo) {
23 this.tradeNo = tradeNo;
24 }
25
26 public int getMoney() {
27 return money;
28 }
29
30 public void setMoney(int money) {
31 this.money = money;
32 }
33
34 public String getTitle() {
35 return title;
36 }
37
38 public void setTitle(String title) {
39 this.title = title;
40 }
41
42 @Override
43 public boolean equals(Object obj) {
44 if(obj instanceof VideoOrder){
45 VideoOrder o1 = (VideoOrder)obj;
46 return title.equals(o1.title);
47 }
48 return super.equals(obj);
49 }
50
51
52 @Override
53 public int hashCode() {
54 return title.hashCode();
55 }
56
57
58 @Override
59 public String toString() {
60 return "VideoOrder{" +
61 "title='" + title + '\'' +
62 '}';
63 }
64}
示例
1import java.util.Arrays;
2import java.util.IntSummaryStatistics;
3import java.util.List;
4import java.util.stream.Collectors;
5
6public class Main {
7
8 public static void main(String[] args) throws Exception {
9 /**
10 * 原始订单数据
11 */
12 //订单1:总价 35
13 List<VideoOrder> videoOrders1 = Arrays.asList(
14 new VideoOrder("20190242812", "springboot", 3),
15 new VideoOrder("20194350812", "springCloud", 5),
16 new VideoOrder("20190814232", "Redis", 9),
17 new VideoOrder("20190523812", "html", 9),
18 new VideoOrder("201932324", "Netty", 9));
19 //订单2:总价 54
20 List<VideoOrder> videoOrders2 = Arrays.asList(
21 new VideoOrder("2019024285312", "springboot", 3),
22 new VideoOrder("2019081453232", "Redis", 9),
23 new VideoOrder("20190522338312", "html", 9),
24 new VideoOrder("2019435230812", "Jmeter", 5),
25 new VideoOrder("2019323542411", "Git+Jenkins", 7),
26 new VideoOrder("2019323542424", "Idea", 21));
27
28 /**
29 * 需求一:统计出同时被两个⼈购买的商品列表(交集)
30 * 需要重写List中的 equals() & hashCode()
31 */
32 List<VideoOrder> intersectionList = videoOrders1.stream().filter(videoOrders2::contains).collect(Collectors.toList());
33 System.out.println(intersectionList);//[VideoOrder{title='springboot'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}]
34
35 /**
36 * 需求二:统计出两个⼈购买商品的差集
37 * ! 取反
38 */
39 //方式二
40 List<VideoOrder> diffList1 = videoOrders1.stream().filter(obj->!videoOrders2.contains(obj)).collect(Collectors.toList());
41 System.out.println("差集1="+diffList1);//差集1=[VideoOrder{title='springCloud'}, VideoOrder{title='Netty'}]
42 //方式一
43 List<VideoOrder> diffList2 = videoOrders2.stream().filter(obj->!videoOrders1.contains(obj)).collect(Collectors.toList());
44 System.out.println("差集2="+diffList2);//差集2=[VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]
45
46
47 /**
48 * 需求三:统计出全部被购买商品的去重并集
49 * ! 取反
50 */
51 List<VideoOrder> allVideoOrder = videoOrders1.parallelStream().collect(Collectors.toList());//这里使用了并行流,因为对执行顺序没有特殊的要求
52 allVideoOrder.addAll(videoOrders2);
53 System.out.println("并集="+allVideoOrder);//并集=[VideoOrder{title='springboot'}, VideoOrder{title='springCloud'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Netty'}, VideoOrder{title='springboot'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]
54
55 List<VideoOrder> allVideoOrderDistinct = allVideoOrder.parallelStream().distinct().collect(Collectors.toList());
56 System.out.println("去重并集="+allVideoOrderDistinct);//去重并集=[VideoOrder{title='springboot'}, VideoOrder{title='springCloud'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Netty'}, VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]
57
58 /**
59 * 需求四:统计两个⼈的分别购买订单的平均价格
60 */
61 Double videoOrder1Price = videoOrders1.stream().collect(Collectors.averagingDouble(VideoOrder::getMoney));
62 System.out.println("订单一的平均价格:" + videoOrder1Price);//订单一的平均价格:7.0
63 Double videoOrder2Price = videoOrders2.stream().collect(Collectors.averagingDouble(VideoOrder::getMoney));
64 System.out.println("订单二的平均价格:" + videoOrder2Price);//订单二的平均价格:9.0
65
66 /**
67 * 需求五:统计两个⼈的分别购买订单的总价格
68 */
69 IntSummaryStatistics summaryStatisticsOrder1 = videoOrders1.stream().collect(Collectors.summarizingInt(VideoOrder::getMoney));
70 System.out.println("订单1的总价:" + summaryStatisticsOrder1.getSum());//订单1的总价:35
71
72 IntSummaryStatistics summaryStatisticsOrder2 = videoOrders2.stream().collect(Collectors.summarizingInt(VideoOrder::getMoney));
73 System.out.println("订单1的总价:" + summaryStatisticsOrder2.getSum());//订单1的总价:54
74 }
75}
新增内存空间_MetaSpace_JDK8
JVM 种类有很多,⽐如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM,Hotspot 才有 MetaSpace,JRockit 以及 J9 是没有这个区域。
- 作⽤:该块内存主要是被 JVM ⽤来存放 class 和 mate 信息的,当 class 被加载 loader 的时候就会 被存储到该内存区中,如⽅法的编译信息及字节码、常量池和符号解析、类的层级信息,字段,名 字等。
1JVM内存知识 在JDK8之前的HotSpot JVM,有个区域叫做“永久代(permanent generation),
2 通过 在命令⾏设置参数-XX:MaxPermSize来设定永久代最⼤可分配的内存空间
3 如果JDK8⾥⾯设置了PermSize 和 MaxPermSize 会被忽略并给出警告
4
5java.lang.OutOfMemoryError: PermGen space
6 原因是: 永久代空间不够,类太多导致
7
8修改JDK8 HotSpot JVM
9 使⽤本地内存来存储类元数据信息,叫做 元空间(Metaspace) 在默认情况下Metaspace的⼤⼩只与本地内存⼤⼩有关
10 常⽤的两个参数
11 -XX:MetaspaceSize=N //指Metaspace扩容时触发FullGC的初始化阈值
12 -XX:MaxMetaspaceSize=N //指⽤于限制Metaspace增⻓的上限,防⽌因为某些情况导致 Metaspace⽆限的使⽤本地内存
13 不管两个参数如何设置,都会从20.8M开始,然后随着类加载越来越多不断扩容调整直到最⼤
14
15查看大小: MC: current metaspace capacity MU: mateaspace utilization 单位是KB
16C:\Users\soulboy>jstat -gc 14772
17 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
184352.0 4352.0 451.0 0.0 34944.0 16996.8 391728.0 345809.7 379220.0 364943.1 49576.0 44274.5 1408 4.032 0 0.000 4.032
try-with-resources_JDK7
在 try( ...)⾥声 明的资源,会在 try-catch 代码块结束后⾃动关闭掉
- 实现了 AutoCloseable 接⼝的类,在 try()⾥声明该类实例的时候,try 结束后⾃动调⽤的 close ⽅法,这个动作会早于 finally ⾥调⽤的⽅法
- 不管是否出现异常,try()⾥的实例都会被调⽤ close ⽅法
- try ⾥⾯可以声明多个⾃动关闭的对象,越早声明的对象,会越晚被 close 掉
1public class Main {
2
3 public static void main(String[] args) throws Exception {
4 String path = "D:\\a.txt";
5 test(path);
6 }
7
8 /**
9 * try(资源初始化过程){业务逻辑}catch(Exception e){}
10 * OutputStream implements Closeable
11 * @param filepath
12 */
13 private static void test(String filepath) {
14 try(OutputStream out = new FileOutputStream(filepath);) {
15 out.write((filepath+" 你好!世界!").getBytes());
16 }catch (Exception e){
17 e.printStackTrace();
18 }finally {
19 // TO DO
20 }
21 }
22
23 /**
24 * try{} catch{} 过于繁琐
25 * @param filepath
26 * @throws FileNotFoundException
27 */
28 private static void testOld(String filepath) throws FileNotFoundException {
29 FileOutputStream out = new FileOutputStream(filepath);
30 try {
31 out.write((filepath+" 你好!世界!").getBytes());
32 }catch (Exception e){
33 e.printStackTrace();
34 }finally {
35 try {
36 out.close();
37 }catch (IOException e){
38 e.printStackTrace();
39 }
40 }
41 }
42}
新增测试工具 Jshell_JDK9
从 java9 开始,JDK 引⼊了交互式 REPL(Read-Eval-Print-Loop,读取-求值-输出-循环)
Java Shell 工具(JShell)是用于学习 Java 编程语言和 Java 代码原型的交互式工具。JShell 是一个 Read-Evaluate-Print Loop(REPL),它在输入声明,语句和表达式时对它们进行评估,并立即显示结果。该工具从命令行运行。
Jshell 官方文档
常用命令
1帮助命令
2 /help
3 /help intro
4列出输入的源(每个操作都是一个源)
5 /list
6编辑某个源
7 /edit [源id]
8删除
9 /drop [源id]
10退出jshell命令
11 /exit
12重置
13 /reset
14查看历史编辑
15 /history
16⾃动化补⻬功能
17 Tab键
接口的私有方法_JDK9
JDK8 新增了静态⽅法和默认⽅法,但是不⽀持私有方法
jdk9 中新增了私有⽅法
- 接⼝中的静态⽅法不能被实现类继承和⼦接⼝继承,但是接⼝中的⾮静态的默认⽅法可以被 实现类继承
1例如List.of() ⽅法,ArrayList虽然继承了List,但是不能⽤ArrayList.of()⽅法
- 类的静态⽅法才可以被继承
示例
接口
1public interface OrderPay {
2
3 void pay();
4
5 default void defaultPay(){
6 privateMethod();
7 }
8
9 //接口的私有方法可以在Jdk9中使用
10 private void privateMethod(){
11 System.out.println("调用接口的私有方法");
12 }
13}
1实现类
1public class OrderPayImpl implements OrderPay {
2 @Override
3 public void pay() {
4 System.out.println("我实现了接口");
5 }
6}
1测试
1public class Main {
2 public static void main(String[] args) {
3 OrderPay orderPay = new OrderPayImpl();
4 orderPay.defaultPay(); //调用接口的私有方法
5 orderPay.pay(); //我实现了接口
6 }
7}
增强 try-with-resource_JDK9
在 JDK7 中 try-with-resource
在 JDK7 中,新增了 try-with-resources 语句,可以在 try 后的括号中初始化资源,可以实现资 源⾃动关闭。
在 JDK9 中的增强 try-with-resource
- 在 JDK9 中,改进了 try-with-resources 语句,在 try 外进⾏初始化,在括号内引⽤,即可实现 资源⾃动关闭,多个变量则⽤分号进⾏分割
- 不需要声明资源 out 就可以使⽤它,并得到相同的结果
1public class Main {
2
3 public static void main(String[] args)throws Exception {
4 test("");
5 }
6
7 private static void test(String filepath) throws Exception {
8 OutputStream out = new FileOutputStream(filepath);
9 OutputStream out2 = new FileOutputStream(filepath);
10 try(out;out2) {
11 out.write((filepath+"可以学习java架构课程").getBytes());
12 }catch (Exception e){
13 e.printStackTrace();
14 }
15 }
16
17}
只读集合_JDK9
1 List<String> list = List.of("SpringBoot","dubbo","SpringCloud");
2 System.out.println(list);
3
4 Set<String> set = Set.of("Mysql","Linux","Git");
5 System.out.println(set);
6
7 Map<String, String> map = Map.of("key1","课程1","key2","课程2");
8 System.out.println(map);
新增 Stream API_JDK9
takeWhile
有序的集合:从 Stream 中获取⼀部分数据, 返回从头开始的尽可能多的元素, 直到遇到第⼀ 个 false 结果,如果第⼀个值不满⾜断⾔条件,将返回⼀个空的 Stream.
1 /**
2 * takeWhile
3 * 截取一直匹配的对象,直到不匹配终止
4 */
5 List<String> list2 = List.of("SpringBoot", "java", "html", "", "git").
6 stream().takeWhile(obj -> !obj.isEmpty()).collect(Collectors.toList());
7 System.out.println(list2); //[SpringBoot, java, html]
dropWhile
与 takeWhile 相反,返回剩余的元素,和 takeWhile ⽅法形成互补.
1 /**
2 * dropWhile
3 * 从第一个不匹配对象开始截取到结束
4 */
5 List<String> list = List.of("SpringBoot", "java", "html", "", "git").
6 stream().dropWhile(obj -> !obj.isEmpty()).collect(Collectors.toList());
7 System.out.println(list);
局部变量类型推断 var_JDK10
- 仅适⽤于局部变量,如 增强 for 循环的索引,传统 for 循环局部变量
- 不能使⽤于⽅法形参、构造函数形参、⽅法返回类型或任何其他类型的变量声明
- 标识符 var 不是关键字,⽽是⼀个保留类型名称,⽽且不⽀持类或接⼝叫 var,也不符合命 名规范
1public class Main {
2 public static void main(String[] args) throws Exception {
3
4 //根据推断为字符串
5 var strVar = "springboot";
6 System.out.println(strVar instanceof String);
7
8 //根据10L 推断long类型
9 var longVar = 10L;
10
11 //根据true推断 boolean类型
12 //var flag = true;
13 var flag = Boolean.valueOf("true");
14 System.out.println(flag instanceof Boolean);
15
16
17 //推断 ArrayList<String>
18 var listVar = new ArrayList<String>();
19 System.out.println(listVar instanceof ArrayList);
20
21 var streamVar = Stream.of("aa","bb","cc");
22 System.out.println(streamVar instanceof Stream);
23
24
25 if(flag){
26 System.out.println("这个是 flag 变量,值为true");
27 }
28
29 for(var i = 0; i<10;i++){
30 System.out.println(i);
31 }
32
33 try(var input = new FileInputStream("")){
34
35 }
36 }
37}
HttpClient 客户端_JDK11
这个功能在 JDK 9 中引⼊并在 JDK 10 中得到了更新
最终 JDK11 正式发布,⽀持 HTT/1.1, HTTP/2,(JDK10 相关课程⾥⾯未讲解该知识点)
官网
1HttpClient.Builder
2 HttpClient 构建⼯具类
3HttpRequest.Builder
4 HttpRequest 构建⼯具类
5HttpRequest.BodyPublisher
6 将java 对象转换为可发送的HTTP request body字节流, 如form表单提
7HttpResponse.BodyHandler
8 处理接收到的 Response Body
HttpClient 提交 Post 和异步请求
1 private static final void testPost() {
2
3
4 //设置建立连接的超时 connect timeout
5 var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000)).build();
6
7 var request = HttpRequest.newBuilder().uri(uri)
8
9 //json格式则下面数据
10 //.header("Content-Type","application/json")
11 //.POST(HttpRequest.BodyPublishers.ofString("{\"phone\":\"13113777338\",\"pwd\":\"1234567890\"}"));
12
13 //form表单则使用下面的配置
14 .header("Content-Type","application/x-www-form-urlencoded")
15 .POST(HttpRequest.BodyPublishers.ofString("phone=13113777338&pwd=1234567890")).build();
16
17 try {
18 var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
19
20 System.out.println(response.body());
21
22 } catch (Exception e) {
23 e.printStackTrace();
24 }
25 }
HttpClient 提交 Post 和异步请求
1 private static final void testAsyncGet() {
2
3 //var httpClient = HttpClient.newHttpClient();
4 var httpClient = HttpClient.newBuilder().build();
5
6 var request = HttpRequest.newBuilder().timeout(Duration.ofMillis(3000))
7 .header("key1", "v1")
8 .header("key2", "v2")
9 .uri(uri).build();
10
11
12 try {
13// var response = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
14// .thenApply(HttpResponse::body);
15
16 CompletableFuture response = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
17 .thenApply(HttpResponse::body);
18
19 System.out.println(response.get());
20
21 } catch (Exception e) {
22 e.printStackTrace();
23 }
24
25
26 }
27
28 private static final void testPost() {
29
30 //设置建立连接的超时 connect timeout
31 var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000)).build();
32
33 var request = HttpRequest.newBuilder().uri(uri)
34
35 //json格式则下面数据
36 //.header("Content-Type","application/json")
37 //.POST(HttpRequest.BodyPublishers.ofString("{\"phone\":\"13113777338\",\"pwd\":\"1234567890\"}"));
38
39 //form表单则使用下面的配置
40 .header("Content-Type","application/x-www-form-urlencoded")
41 .POST(HttpRequest.BodyPublishers.ofString("phone=13113777338&pwd=1234567890")).build();
42 try {
43 var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
44
45 System.out.println(response.body());
46
47 } catch (Exception e) {
48 e.printStackTrace();
49 }
50 }
HttpClient 提交 Http2 请求_ JDK11
- HTTP2 协议的强制要求 https,如果⽬标 URI 是 HTTP 的,则⽆法使⽤ HTTP 2 协议
- 如何判断⽹站是否是 http2 协议,浏览器,network ⾯板,选择 protocol
1public class Main {
2 public static void main(String[] args) throws Exception {
3 //testGet();
4 //testPost();
5 //testAsyncGet();
6 testHttp2();
7 }
8
9 private static final String targetUrl = "https://http2.akamai.com/demo";
10 private static final URI uri = URI.create(targetUrl);
11
12 private static final void testHttp2() {
13 //var httpClient = HttpClient.newHttpClient();
14 //设置建立连接的超时 connect timeout
15 var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000))
16 .version(HttpClient.Version.HTTP_2)
17 .build();
18
19 var request = HttpRequest.newBuilder().timeout(Duration.ofMillis(3000))
20 .header("key1", "v1")
21 .header("key2", "v2")
22 .uri(uri).build();
23
24 try {
25 var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
26
27 System.out.println(response.body());
28 System.out.println(response.version());
29
30 } catch (Exception e) {
31 e.printStackTrace();
32 }
33 }
34}
javac 和 Java 命令优化_JDK11
JDK11 之前
1javac xxx.java
2java xxx
JDK11 后运⾏ Java 程序(本地不会⽣成 class ⽂件
1java xxx.java
JDK 各个版本
OpenJDK 和 OracleJDK 版本区别
- OpenJDK 是 JDK 的开放源码版本,以 GPL 协议的形式发布(General Public License)
- Oracle JDK 采⽤了商业实现
LTS
- Long Term Support ⻓期⽀持的版本,如 JDK8、JDK11 都是属于 LTS
- JDK9 和 JDK10 这两个被称为“功能性的版本”不同, 两者均只提供半年的技术⽀持
-
- 甲⻣⽂释出 Java 的政策,每 6 个⽉会有⼀个版本的释出,⻓期⽀持版本每三年发布⼀次,根据 后续的发布计划,下⼀个⻓期⽀持版 Java 17 将于 2021 年发布
8u20、11u20
- 就是 Java 的补丁,⽐如 JDK8 的 8u20 版本、8u60 版本; java11 的 11u20、11u40 版本
JDK 要收费了?
- Oracle 宣布 Java8 在 2019 年 1 ⽉之后停⽌更新,另外 Java11 及以后版本将不再提供免 费的 long-term support (LTS) ⽀持,猜测未来将有越来越多 Java 开发者转向使⽤ OpenJDK
OpenJDK
- OpenJDK 是免费的,想要不断体验新特性的 developer 来说是不错的选择
- OracleJDK 不是免费的,对于企业⽤户来说,有钱的情况下就选择 OracleJDK。对应 oracleJDK ,我们可以⾃⼰⽤来写代码,调试,学习即可