JDK8的新特性总结
作者:快盘下载 人气:29JDK安装
- win64位-JDK1.8下载||https://pan.baidu.com/s/1fMNaZ0JgySo2MzBT90T5Tw ||jjw3
- Linux64位-JDK1.8||https://pan.baidu.com/s/1CDpW-UNYyje-p0BxaNtncQ ||nwyd
- Mac-JDK1.8下载||https://pan.baidu.com/s/1liT9kSLicpXEAd7AdA0nOg ||5bpk
1.接口默认方法实现
- 在jdk1.8以前接⼝⾥⾯是只能有抽象⽅法;不能有任何⽅法的实现的
- jdk1.8⾥⾯打破了这个规定;引⼊了新的关键字default;使⽤default修饰⽅法;可以在接⼝⾥⾯定义具体的⽅法实现
- 默认⽅法; 接⼝⾥⾯定义⼀个默认⽅法;这个接⼝的实现类实现了这个接⼝之后;不⽤管这个default修饰的⽅法就可以直接调⽤;即接⼝⽅法的默认实现
;1;编写接口
public interface Animal {
void run(); void eat(); /** * 接口的默认方法 */ default void sleep(){ System.out.println(;睡觉;); } /** * 静态方法 */ static void st(){ System.out.println(;静态方法;); } }
;2;编写实现类
public class Dog implements Animal{
;Override public void run() { System.out.println(;小狗跑;); } ;Override public void eat() { System.out.println(;小狗吃;); } public static void main(String[] args) { Dog dog = new Dog(); dog.run(); dog.eat(); //直接能掉接口的默认实现 dog.sleep(); //直接调用静态方法 Animal.st(); } }
;3;运行结果

2.新增Base64加解密API
2.1.Base64编码简介
Base64是网络上常见的用于传输8Bit字节码的编码方式之一;Base64就是一种基于64个可打印字符来表示二进制数据的方法;基于64个字符A-Z、0-9、a-z、;、/的编码方式;是一种能将任意二级制数据用64种字节组合成字符串的方法;而这个二进制数据和字符串资料之间是可以相互转换的;实际应用上;Base64不但可以将二进制数据可视化;还可以用于数据传输的加密
2.2.JDK1.8之前Base64的API
- 早期使用JDK里sun.misc套件下的BASE64Encoder和BASE64Decoder这两个类
;1;编码实战
public static void main(String[] args) {
//目标字符串 String str = ;lixiang;; //加密 BASE64Encoder encoder = new BASE64Encoder(); String strEncode = encoder.encode(str.getBytes(;UTF-8;)); System.out.print(;加密后;;;strEncode); //解密 BASE64Decoder decoder = new BASE64Decoder(); String strDecode = new String(decoder.decodeBuffer(strEncode),;UTF-8;); System.out.print(;解密后;;;strDecode); }
;2;运行结果

- 缺点;编码和解码的效率比较差;公开信息说以后要取消这个方法
- Apache Commons Codec有提供Base64的编码与解码;但是需要引第三方依赖
2.3.JDK1.8之后Base64的API
- JDK1.8之后java.util中提供了Base64的类
;1;编码实战
public static void main(String[] args) {
//目标字符串 String str = ;lixiang;; //加密 Base64.Encoder encoder = Base64.getEncoder(); String strEncode = encoder.encodeToString(str); System.out.print(;加密后;;;strEncode); //解密 Base64.Decoder decoder = Base64.getDecoder(); String strDecoder = new String(decoder.decode(strEncode),;UTF-8;); System.out.print(;解密后;;;strDecoder); }
;2;运行结果

- 优点;不需要引包;效率远大于un.misc 和 Apache Commons Codec
3.时间日期处理类
- 时间处理JDK1.8之前用的是SimpleDateFormat;Calendar等类;缺点是java.util.Date是线程不安全的;而且日期/时间对象比较;加减麻烦
- JDK1.8新发布Date-Time API进行加强对日期时间的处理
- 包所在位置;java.time
- 核心类
LocalDate:不包含具体时间的日期
LocalTime:不包含日期的时间 LocalDateTime:包含了日期及时间
3.1.LocalDate常用的API
LocalDate today = LocalDate.now();
获取当前日期的年份;today.getYear() 获取当前日期的月份;today.getMonth() 获取当前日期的月份;数字;;today.getMonthValue() 获取当前日期是当月的多少号;today.getDayOfMonth() 获取当前日期是这一周的周几;today.getDayOfWeek(); 获取当前日期;1年,必须返回新的对象才会;1;today.plusYears(1) 获取当前日期;1月,必须返回新的对象才会;1;today.plusMonths(1) 获取当前日期;1周,必须返回新的对象才会;1;today.plusWeeks(1) 获取当前日期;1天,必须返回新的对象才会;1;today.plusDays(1) 获取当前日期-1天,必须返回新的对象才会-1;today.minusDays(1) 获取当前日期-1周,必须返回新的对象才会-1;today.minusWeeks(1) 获取当前日期-1年,必须返回新的对象才会-1;today.minusYears(1) 获取当前日期-1月,必须返回新的对象才会-1;today.minusMonths(1) 日期比较;当前日期与目标日期之后;date.isAfter(today) 日期比较;当前日期与目标日期之前;date.isBefore(today) 修改当前日期的年份;today.withYear(1999) 修改当前日期的月份;today.withMonth(3) 修改当前对象在当月的日期;today.withDayOfMonth(5) 比较两个日期是否相等;date.isEqual(today)
3.2.日期格式化
- JDK1.8之前;SimpleDateFormat来进行格式化;但SimpleDateFormat并不是线程安全的
- JDK1.8之后;引入线程安全的日期与时间DateTimeFormatter
//获取当前时间
LocalDateTime today = LocalDateTime.now(); System.out.println(;today:;;today); //日期格式化 LocalDateTime today = LocalDateTime.now(); DateTimeFormatter dtf = DateTimeFormatter.ofPattern(;yyyy-MM-dd HH:mm:ss;); String date = dtf.format(today);

//定制日期对象,参数分别对应;年、月、日、时、分、秒
LocalDateTime localDateTime = LocalDateTime.of(2021,11,12,8,10,2);

3.3.日期的比较
LocalDateTime date1 = LocalDateTime.of(2021,11,12,8,10,2);
LocalDateTime date2 = LocalDateTime.of(2020,11,12,8,10,2); Duration duration = Duration.between(date1,date2); //两个时间差的天数 System.out.println(duration.toDays()); //两个时间的小时差 System.out.println(duration.toHours()); //两个时间的分钟差 System.out.println(duration.toMinutes()); //两个时间的毫秒差 System.out.println(duration.toMillis()); //两个时间的纳秒差 System.out.println(duration.toNanos());

4.空指针处理Optional类
;1;Optional类的用处
主要解决的问题是空指针异常;NullPointerException;
;2;创建Optional类
- of
- null值作为参数传进去会报异常
- Optional optional = Optional.of(空对象);

- ofNullable
- null值作为参数传进去不会报异常
- Optional optional = Optional.ofNullable(空对象);

;3;访问Optional对象的值
- get
- 获取Optional对象中的值
- optional.get()
- isPresent
- 判断optional是否存在;如果不存在返回false;一般在获取Optional对象中的值之前调用判断
- optional.isPresent()
Student student = null;
//Optional<Student> optional = Optional.of(student); Optional<Student> optional = Optional.ofNullable(student); //拿值之前先判断optional是否为空 if(optional.isPresent()){ Student student1 = optional.get(); }else{ System.out.println(;optional中值为空;); }

;4;兜底orElse方法
- orElse()如果有只就返回该值;否则就传送一个默认值
Student student1 = null;
Student student2 = new Student(;李祥;,20); //当student1为空时;就返回student2对象;不为空就返回student1对象 Student orElse = Optional.ofNullable(student1).orElse(student2); System.out.println(orElse);

//判断对象年龄如果为空值就传送一个默认7
Optional.ofNullable(student1).map(obj->obj.getAge()).orElse(7); System.out.println(;age:;;integer);

5.Lambda表达式
;1;什么是Lambda表达式
- 在JDK8之前;java是不支持函数式编程的;所谓函数编程;可以理解成将一个函数;行为动作;作为一个方法的参数传递过去执行;面向对象编程是对数据的抽象;而函数式编程是对行为的抽象
;2;JDk8之前创建线程的方式与Lambda创建线程的方式
- JDK8之前创建线程
public static void main(String[] args) {
new Thread(new Runable(){ ;Override public void run(){ System.out.print(;测试;); } }).start(); }
- Lambda表达式创建线程
public static void main(String[] args) {
//new Thread(()->{System.out.print(;测试;);}).start(); new Thread(()->System.out.print(;测试;)).start(); }
;3;集合容器元素排序
- JDK8之前集合排序
public static void main(String[] args) {
Arrays.asList(;aaa;,;bbb;,;ccc;,;fff;,;ddd;); Collections.sort(list,new Comparator<String>(){ ;Override public int compare(String a,String b){ return a.compareTo(b); } }); }
- Lambda表达式方式排序
public static void main(String[] args) {
Arrays.asList(;aaa;,;bbb;,;ccc;,;fff;,;ddd;); Collections.sort(list,(x,y) -> x.comparaTo(y)); }
;4;Lambda表达式的使用场景
一个接口中只包含一个需要实现的方法;可以使用Lambda表达式;这样的接口称为函数式接口;语法;(params) -> expression
(x,y) -> {System.out.print(x;y);} (x,y):这一部分为参数;当只有一个参数时可以省略括号;x->,当没有参数时;直接写括号() ->:表示指向执行的方法体 {System.out.print(x;y);}:表示执行的方法体;可以是表法式;也可以是代码块;当是表达式的时候可以省略{};没有{}时;不要写;代码块时要加上{};就和平常的语句逻辑一样。 好处;Lambda表达式的实现方式时在本质以匿名内部类的方式进行实现;减少代码冗余;提高开发效率。
;5;自定义Lambda接口
-
定义一个函数接口;需要加上;FuncationInterface注解;只允许有一个默认的方法实现。
-
定义一个可以使用加减乘除的接口;之前需要定义四个方法;现在只需一个方法
-
编写接口;定义方法;加上FunctionInterface注解
;FunctionInterface
public interface OperFunction<R,T>{ R operator(T t1,T t2); }

- 定义方法;将接口作为方法入参
public static Integer operator(Integer x,Integer y,OperFunction<Integer,Integer> of){
return of.operator(x,y); }
- 测试
public static void main(String[] args) {
System.out.print(operator(x,y,(x,y)->x;y)); System.out.print(operator(x,y,(x,y)->x-y)); System.out.print(operator(x,y,(x,y)->x*y)); System.out.print(operator(x,y,(x,y)->x/y)); }

6.函数式编程
6.1.Java8内置的四大函数式接口
- Lambda表达式必须先定义接口;创建相关方法之后可使用;这样做十分不方便;java8已经内置了许多接口;例如下面四个功能性接口;所以一般很少会由用户去定义新的函数时接口。
- java8的最大特性就是函数式接口;所有标注了;FunctionalInterface注解的接口都是函数式接口。
Consumer<T>;消费型接口;有入参;无返回值。
void accept(T t); Supplier<T>;供给型接口;无入参;有返回值。 T get(); Function<T,R>;函数型接口;有入参;无返回值 R apply(T t); Predicate<T>;断言型接口;有入参;有返回值;返回值类型确定是boolean boolean test(T t);
6.2.函数式编程Function
- Function
- 传入一个值经过函数的计算返回另一个值
- T;入参类型;R;出参类型
;FunctionalInterface
public interface Function<T, R> { /** * Applies this function to the given argument. * ;param t the function argument * ;return the function result */ R apply(T t); }
- 作用;将转换后逻辑提取出来;解耦合
public class FunctionObj implements Function {
;Override public Object apply(Object o) { return o;;;apply处理;; } }
public static void main(String[] args) {
Object lixiang = apply(;lixiang;, new FunctionObj()); System.out.println(lixiang); } public static Object apply(String o,FunctionObj obj){ return obj.apply(o); }

6.3.函数式编程BiFunction
- BiFunction Function只能接受一个参数;如果要传递两个参数;则用BiFunction。
;FunctionalInterface
public interface BiFunction<T,U,R>{ R apply(T t,U u); }
public static void main(String[] args) {
System.out.println(operator(10,21,(a,b)->a;b)); System.out.println(operator(10,2,(a,b)->a-b)); System.out.println(operator(8,4,(a,b)->a*b)); System.out.println(operator(10,2,(a,b)->a/b)); } public static Integer operator(Integer a, Integer b, BiFunction<Integer,Integer, Integer> bf) { return bf.apply(a, b); }

6.4.函数式编程Consumer
- Consumer消费型接口;有入参;无返回值
- 将 T 作为输入;不反回任何内容
- 调用方法;void accept(T t);
;FunctionalInterface
public interface Consumer<T> { void accept(T t); }
- 用途;因为没有出参常用于打印;发送短信等消费动作
public static void main(String[] args) {
sendMessage(;11111111;,phone -> System.out.println(phone;;已经被发送;)); } public static void sendMessage(String phone, Consumer<String> consumer){ consumer.accept(phone); }

6.5.函数式编程Supplier
- Supplier;供给型接口;无入参;有返回值
- T;出参类型
- 调用方法;T get();
;FunctionalInterface
public interface Supplier<T> { T get(); }
- 用途;泛型一定和方法的返回值是一种类型;如果需要获得一个数据;并且不需要传入参数;可以使用Supplier接口;例如无参的工厂方法。
public static void main(String[] args) {
Student student = newStudent(); System.out.println(student); } public static Student newStudent(){ Supplier<Student> supplier=()-> new Student(;lixiang;,20); return supplier.get(); }

6.6.函数式编程Predicate
- Predicate;断言型接口;有入参;有出参;返回值类型是boolean
- T;入参类型;出餐类型是Boolean
- 调用方法;boolean test(T t)
;FunctionalInterface
public interface Predicate<T> { boolean test(T t); }
- 用途;接收一个参数;用于判断是否满足一定的条件过滤数据
public static void main(String[] args) {
List<String> list = Arrays.asList(;awewrwe;,;vdssdsd;,;aoooo;,;psdddsd;); List<String> results = filter(list, obj -> obj.startsWith(;a;)); System.out.println(results); } public static List<String> filter(List<String> list,Predicate<String> predicate){ List<String> results = new ArrayList<>(); for (String s : list) { if(predicate.test(s)){ results.add(s); } } return results; }

6.7.方法与构造方法引用
- 以前调用方法;对象.方法名();类名.方法名()
- JDK8提供了另外一种方式 :: (双冒号)
方法引用是一种更简洁易懂的lambda表达式;操作符是双冒号::,用来直接访问类或者实例已经存在的方法或者构造方法。
通过语法引用;可以将方法的引用赋值给一个变量 语法;左边是容器;可以是类名;实例名;;中间是;::;;右边是相应的方法名 静态方法;ClassName::methodName 实例方法;Instance::methodName 构造函数;类名::new 单个参数 Function<入参1;返回类型> func = 方法引用 应用 func.apply(݇入参); 两个参数 BiFunction<݇入参1, 入参2> func = 方法引用 应用 func.apply(入参1;入参2);
测试调用
public static void main(String[] args) {
//静态方法的调用 Function<String,Integer> function1 = Integer::parseInt; Integer num = function1.apply(;1024;); System.out.println(;转化后;;;num); //实例方法的调用 String context = ;lixiang;; Function<Integer,String> function2 = context::substring; String str = function2.apply(1); System.out.println(;截取后的字符串;;;str); //构造方法的调用,双参数 BiFunction<String,Integer,Student> function3 = Student::new; Student lixiang = function3.apply(;lixiang;, 20); System.out.println(lixiang); //构造方法的调用;单个参数 Function<String,Student> function4 = Student::new; Student lisi = function4.apply(;lisi;); System.out.println(lisi); //函数作为参数传递到另一个方法中 String sayHello = sayHello(String::toUpperCase, ;lixiang;); System.out.println(sayHello); } public static String sayHello(Function<String,String> function,String str){ String apply = function.apply(str); return apply; }

7.Stream流式操作集合框架
7.1.Stream简介
;1;什么是stream
Stream中文称为;流;;通过将集合转化为这么一种叫做流的元素队列;通过声明性方式;能够对集合中的每一个元素进行一些列并行或穿行的流水线操作。
元素是特定类型的对象;所以元素集合看作一种流;流在管道中传输;且可以在管道的节点上进行处理;比如 排序;聚合;过滤等

- 操作详情
- 数据元素就是原始的集合;List、Map、Set等
- 生成流;可以是串行流stream()或者并行流parallelStream()
- 中间操作;可以是排序;聚合;过滤;转换等
- 终端操作;很多流操作本身就会返回一个流;所以多个操作可以直接连接起来;最后统一收集
;2;案例
List<String> list = Arrays.asList(;SpringBoot;, ;SpringCloud;, ;redis;, ;RabbitMQ;);
List<String> collect = list.stream().map(obj -> obj ; ;-拼接;).collect(Collectors.toList()); for (String s : collect) { System.out.println(s); }

7.2.map和filter函数
;1;map函数
-
将流中的每一个元素T映射成R;类似类型转换;
-
应用场景;转换对象;类似DO对象转换成DTO对象
-
map函数源码

- 测试
public static void main(String[] args) {
List<User> users = Arrays.asList( new User(1, ;小东;, ;123;), new User(2, ;小李;, ;123;), new User(3, ;小王;, ;123;), new User(4, ;小张;, ;123;)); List<UserDTO> collect = users.stream().map(user -> { UserDTO userDTO = new UserDTO(); userDTO.setUserId(user.getId()); userDTO.setUsername(user.getName()); return userDTO; }).collect(Collectors.toList()); for (UserDTO userDTO : collect) { System.out.println(userDTO); } }

;2;filter函数
-
用于通过设置的条件过滤出元素
-
应用场景;过滤出符合条件的元素
-
filter函数源码

- 测试
List<String> list = Arrays.asList(;SpringBoot;, ;SpringCloud;, ;Redis;, ;RabbitMQ;,;JUC;);
//filter List<String> collect5 = list.stream().filter(str -> str.length() > 4).collect(Collectors.toList()); System.out.println(collect5);

7.3.sorted和limit函数
;1;sorted函数
- sorted()对流进行自然的排序;其中的元素必须实现Comparable接口
- 应用场景;需要对集合的元素进行定制化排序
- sorted源码

- 测试
//排序;sort();默认升序
List<String> collect1 = list.stream().sorted().collect(Collectors.toList()); System.out.println(;sort()按照英文字母升序;;;collect1); //根据长度排序;默认升序 List<String> collect2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList()); System.out.println(;sort()按照英文字母的长度升序;;;collect2); //根据长度排序,降序 List<String> collect3 = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList()); System.out.println(;sort()按照英文字母的长度降序;;;collect3);

;2;limit函数
- limit()截断流使其最多包含指定的数量的元素
- 应用场景;排行榜;截取前多少名
- limit源码

- 测试
//根据长度排序,降序,截取前三个
List<String> collect4 = list.stream() .sorted(Comparator.comparing(String::length,Comparator.reverseOrder())).limit(3) .collect(Collectors.toList()); System.out.println(collect4);


7.4.allMatch和anyMatch函数
;1;allMatch函数
- 检查是否匹配所有元素;主有全部符合才返回true
- allMatch源码

- 测试
List<String> list = Arrays.asList(;SpringBoot;, ;SpringCloud;, ;Redis;, ;RabbitMQ;,;Netty;,;JUC;,;docker;);
boolean allFlag = list.stream().allMatch(str -> str.length()>5); System.out.println(;allFlag全部满足返回true:;;allFlag);

;2;anyMatch函数
- 检查是否匹配所有元素;主有全部符合才返回true
- anyMatch源码

- 测试
List<String> list = Arrays.asList(;SpringBoot;, ;SpringCloud;, ;Redis;, ;RabbitMQ;,;Netty;,;JUC;,;Docker;);
boolean allFlag = list.stream().anyMatch(str -> str.length()>5); System.out.println(;allFlag全部满足返回true:;;allFlag);

7.4.max和min函数
;1;max函数
- 获取集合元素中的最大值
- max源码
- 测试
Optional<Student> max = list.stream().max(Comparator.comparingInt(Student::getAge));
if (max.isPresent()){ System.out.println(;最大年龄;;;max.get().getAge()); }

;2;min函数
- 获取集合中的最小值
- min源码

- 测试
Optional<Student> min = list.stream().min((student1, student2) -> {
return Integer.compare(student1.getAge(), student2.getAge()); }); if(min.isPresent()){ System.out.println(;最小年龄;;;min.get().getAge()); }

7.5.并行流parallelStream
- 为什么会有这个并行流
- 集合做重复的操作;如果使用串行执行会相当耗时;因此一般会采用多线程来加快;Java8的paralleStream用fork/join框架提供了并发执行能力
- 底层原理
- 线程池;ForkJoinPool;维护一个线程队列
- 可以分割任务;将父任务拆分成子任务;完全贴合分治思想
- fork/join框架

- 两个区别
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//串行流 System.out.print(;串行流;;); numbers.stream().forEach(num -> System.out.print(num;; ;)); System.out.println(); //并行流 System.out.print(;并行流;;); numbers.parallelStream().forEach(num -> System.out.print(num;; ;));

- 存在问题
- parallelStream并行是否一定比Sream串行快?
- 错误;数据量少的情况;可能串行更快;ForkJoin会耗性能
- 多数情况下并行比串行块;是否可以都用并行
- 不行;部分情况会线程安全问题;parallelStream里面使用的外部变量;比如集合一定要使用线程安全集合;不然就会引用多线程安全问题
for (int i = 0; i < 10; i;;) {
//List<Integer> list = new ArrayList<>(); CopyOnWriteArrayList list = new CopyOnWriteArrayList(); IntStream.range(1,100).parallel().forEach(list::add); System.out.println(list.size()); }

7.6.reduce函数
- 什么是reduce操作
- 聚合操作;中文意思是“减少”
- 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
- 源码分析

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
//将集合内的元素相加 Integer integer = list.stream().reduce((x, y) -> x ; y).get(); System.out.println(;集合内元素相加;;;integer); //初始值100;将集合内的元素相加 Integer integer1 = list.stream().reduce(100,(x,y) -> x;y); System.out.println(;100累加集合内的元素;;;integer1); //判断最大值返回 Integer integer2 = list.stream().reduce((x, y) -> x > y ? x : y).get(); System.out.println(;集合内最大元素;;;integer2);

7.7.集合foreach
- 集合遍历方式
- for循环
- 迭代器Iterator
- 源码分析

- JDK8里面的新增接口
List<Student> result = Arrays.asList(new Student(32),new Student(33),new Student(21),new Student(29),new Student(18));
result.forEach(obj -> System.out.println(obj.getAge()));

- 注意点;
- 不能修改包含外部的变量的值
- 不能用break或者return或者continue等关键词结束或者跳出循环
8.Collector收集器和集合统计
8.1.collector收集器
- collect()方法的作用
- 一个终端操作;用于对流中的数据进行归集操作;collect方法接收的参数是一个Collector
- 有两个方法重载;在Stream接口里面
//重载方法一
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R>combiner); //重载方法二 <R, A> R collect(Collector<? super T, A, R> collector)
- Collector的作用
- 就是收集器;也是一个接口;它的工具类Collectors提供了很多工厂方法
- Collectors的作用
- 提供了很多常见的集合的收集
- Collectors.toList()
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>( (Supplier<List<T>>) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID ); }
- ArrayList::new;创建一个ArrayList作为累加器
- List::add;对流中的元素直接加到累加器中
- reduce;对子任务归集结果直接全部添加到前一个子任务结果中
- CH_ID是一个unmodifableSet集合
常见的收集器
- Collector.toMaps();创建一个map收集器
- Collector.toSet();创建一个set收集器
- Collector.toCollection();自定义收集器
- Collector.toCollection(LinkedList::new)
- Collector.toCollection(TreeSet::new)
8.2.joining函数
- 拼接函数;将集合内的元素按照一定规则拼接起来;Collectors.joining()
//三种重载方法
Collectors.joining() //无参数默认按照空字符拼接 Collectors.joining(;-;) //一个参数;按照“-”进行分割 Collectors.joining(;-;,;{;,;};) //三个参数;第一个为分隔符;后两个为前缀后缀
- 源码



- 测试
List<String> list = Arrays.asList(;SpringBoot;,;Docker;,;Java;,;SpringCloud;,;Netty;);
String collect1 = list.stream().collect(Collectors.joining()); System.out.println(collect1); String collect2 = list.stream().collect(Collectors.joining(;-;)); System.out.println(collect2); String collect3 = list.stream().collect(Collectors.joining(;-;, ;{;, ;};)); System.out.println(collect3);
8.3.partitioningBy分组
-
Collectors.partitioningBy()会根据筛选条件进行分组;返回一个Map<Boolean,List>类型;boolean为true的表示符合条件的;false的为不符合筛选条件的。
-
源码

- 测试
List<String> list = Arrays.asList(;Java;,;SpringBoot;,;HTML5;,;CSS3;);
Map<Boolean, List<String>> collect = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 4)); System.out.println(collect);

8.4.groupingBy分组
- Collectors.groupingBy(),会根据实体Bean的某个属性进行分组;更实用;返回的是Map<String,List>,key为要分组的实体Bean的属性值
- 源码

- 测试
List<Student> students = Arrays.asList(
new Student(;lx;, 23, ;北京;), new Student(;ls;, 24, ;天津;), new Student(;zs;, 23, ;⼴东;), new Student(;ww;, 22, ;⼴东;), new Student(;ld;, 20, ;北京;), new Student(;xs;, 20, ;⼴东;), new Student(;ok;, 25, ;北京;)); Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince())); //第二种方式 //Map<String, List<Student>> collect1 = students.stream().collect(Collectors.groupingBy(Student::getProvince)); collect.forEach((key,value) -> { System.out.println(;----;;key); value.forEach(obj-> System.out.println(obj)); });

8.5.counting函数
- 用于统计groupingBy于分组之后;每组元素的个数
- 源码

- 测试
List<Student> students = Arrays.asList(
new Student(;lx;, 23, ;北京;), new Student(;ls;, 24, ;天津;), new Student(;zs;, 23, ;⼴东;), new Student(;ww;, 22, ;⼴东;), new Student(;ld;, 20, ;北京;), new Student(;xs;, 20, ;⼴东;), new Student(;ok;, 25, ;北京;)); Map<String, Long> collect1 = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting())); collect1.forEach((key,value) -> { System.out.println(;---;;key); System.out.println(;---;;value); });

8.6.summarizing集合统计
- 集合相关的统计都能实现;根据实体Bean的某个属性进行统计;求最大值;最小值;总和等等
- 分类
- summarizingInt
- summarizingLong
- summarizingDouble
- 测试;统计各个学生的年龄信息
IntSummaryStatistics collect = students.stream().collect(Collectors.summarizingInt(Student::getAge));
System.out.println(;平均值;;;collect.getAverage()); System.out.println(;总人数;;;collect.getCount()); System.out.println(;年龄总和;;;collect.getSum()); System.out.println(;最大值;;;collect.getMax()); System.out.println(;最小值;;;collect.getMin());

9.Collection和Lambda实战
;1;需求;求两个集合的交集、差集、去重并集;两集合的各自平均值;总和
;2;创建两个集合
//总价 35
List<VideoOrder> videoOrders1 = Arrays.asList( new VideoOrder(;20190242812;, ;springboot教程;, 3), new VideoOrder(;20194350812;, ;微服务SpringCloud;, 5), new VideoOrder(;20190814232;, ;Redis教程;, 9), new VideoOrder(;20190523812;, ;⽹⻚开发教程;, 9), new VideoOrder(;201932324;, ;百万并发实战Netty;, 9)); //总价 54 List<VideoOrder> videoOrders2 = Arrays.asList( new VideoOrder(;2019024285312;, ;springboot教程;, 3), new VideoOrder(;2019081453232;, ;Redis教程;, 9), new VideoOrder(;20190522338312;, ;⽹⻚开发教程;, 9), new VideoOrder(;2019435230812;, ;Jmeter压⼒测试;, 5), new VideoOrder(;2019323542411;, ;Git;Jenkins持续集成;, 7), new VideoOrder(;2019323542424;, ;Idea全套教程;, 21));
;3;注意一点要重写equals和hashcode方法
;Override
public boolean equals(Object o) { if(o instanceof VideoOrder){ VideoOrder o1 = (VideoOrder)o; return this.title.equals(o1.title); } return super.equals(o); } ;Override public int hashCode() { return title.hashCode(); }
;4;计算两个集合的交集
List<VideoOrder> clist = videoOrder1.stream().filter(videoOrder2::contains).collect(Collectors.toList());
;5;计算两个集合的差集
List<VideoOrder> nclist = videoOrder1.stream().filter(obj -> !videoOrder2.contains(obj)).collect(Collectors.toList());
;6;计算两个集合的去重并集
List<VideoOrder> allList = videoOrder1.paralleStream().distinct().collect(Collectors.toList());
allList.addAll(videoOrder2);
;7;计算订单价格的平均值
double avg = videoOrder1.stream().collect(Collectors.averagingInt(VideoOrder::getMoney)).doubleValue();
;8;计算订单价格的总和
Integer sum = videoOrders1.stream().collect(Collectors.summingInt(VideoOrder::getMoney));

10.内存空间和异常处理
10.1.新内存空间Matespace
- JVM 种类有很多;⽐如 Oralce-Sun HoTSPot, Oralce JRockit, IBM J9, Taobao JVM;我们讲的是Hotspot才有;JRockit以及J9是没有这个区域
- JVM内存JDK8之前的HotSpot JVM;有个区域叫做”永久代“;permanent generation;;通过在命令行设置参数;-XX:MaxPermSize来设定永久代最大可分配的内存空间
- 如果JDK8中设置了PermSize和Max PermSize会被忽略并且警告
- 作用;该块内存主要是被JVM用来存放class和mate信息的;当class被加载loader的时候会被存储到该内存区中;入方法的编译信息及字节码、常量池和符号解析、类的层级信息;字段、名字等
- java.lang.OutOfMemoryError: PermGen space
- 异常原因;永久代空间不够;可能类太多导致
- JDK8使用本地内存存储类元数据信息;叫做元空间;Metaspcae;
- 在默认情况下Metaspcae的大小至于本地内存大小有关系
- 常⽤的两个参数
- -XX:MetaspaceSize=N 指Metaspace扩容时触发FullGC的初始化阈值
- -XX:MaxMetaspaceSize=N 指⽤于限制Metaspace增⻓的上限;防⽌因为某些情况导致Metaspace⽆限的使⽤本地内存
- 不管两个参数如何设置;都会从20.8M开始;然后随着类加载越来越多不断扩容调整直到最⼤
- 查看MetaspaceSize的大小
- jstat -gc pid
10.2.try-with-resources(JDK7)
- 自动关流
- jdk7之前处理异常
public static void main(String[] args) throws FileNotFoundException {
String filePath = ;F:1.txt;; test(filePath); } private static void test(String filePath) throws FileNotFoundException { OutputStream outputStream = new FileOutputStream(filePath); try { outputStream.write((filePath;;学习;).getBytes()); } catch (IOException e) { e.printStackTrace(); }finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
- jdk7之后处理异常
public static void main(String[] args) throws FileNotFoundException {
String filePath = ;F:1.txt;; test(filePath); } private static void test(String filePath) throws FileNotFoundException { //自动关流;当有多个文件时;直接用;分割就可以 try (OutputStream outputStream = new FileOutputStream(filePath)){ outputStream.write((filePath ; ;学习;).getBytes()); } catch (IOException e) { e.printStackTrace(); } }
- 实现了AutoCloseable接口的类;在try()里面声明该类实例的时候;try结束后自动i盗用的close方法;这个动作会早于finally里面调用的方法。
- 不管是否出现异常;try()里面的实例都会被调用close方法。
- try里面可以声明多个自动关闭的对象;越早声明的对象;会越晚被关掉。
public static void main(String[] args) throws FileNotFoundException {
String filePath = ;F:1.txt;; test(filePath); } private static void test(String filePath) throws FileNotFoundException { OutputStream outputStream = new FileOutputStream(filePath); try { outputStream.write((filePath;;学习;).getBytes()); } catch (IOException e) { e.printStackTrace(); }finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
- jdk7之后处理异常
public static void main(String[] args) throws FileNotFoundException {
String filePath = ;F:1.txt;; test(filePath); } private static void test(String filePath) throws FileNotFoundException { //自动关流;当有多个文件时;直接用;分割就可以 try (OutputStream outputStream = new FileOutputStream(filePath)){ outputStream.write((filePath ; ;学习;).getBytes()); } catch (IOException e) { e.printStackTrace(); } }
- 实现了AutoCloseable接口的类;在try()里面声明该类实例的时候;try结束后自动i盗用的close方法;这个动作会早于finally里面调用的方法。
- 不管是否出现异常;try()里面的实例都会被调用close方法。
- try里面可以声明多个自动关闭的对象;越早声明的对象;会越晚被关掉。
加载全部内容