1、jdk8新特性default关键字
在jdk1.8以前接口里面是只能有抽象方法,不能有任何方法的实现的
jdk1.8里面打破了这个规定,引入了新的关键字default,使用default修饰方法,可以在接口里面定义具体的方法实现
默认方法: 接口里面定义一个默认方法,这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法就可以直接调用,即接接口方法的默认实现
public interface Animal {
void run();
void eat();
default void breath(){
System.out.println("使用氧气呼吸");
}
public interface Animal {
void run();
void eat();
default void breath(){
System.out.println("使用氧气呼吸");
}
static void test(){
System.out.println("这是静态方法");
}
}
2、JDK8之新增base64加解密API
什么是base64编码 base64是网络上最常见的用于传输8Bit字节码的编码方式之一,base64就是一种基于64个可打印字符来表示⼆进制数据的方法 基于64个字符A-Z,a-z,0-9,+,/的编码方式,是一种能将任意二进制数据用64种字元组合成字符串的方法,而这个二进制数据和字符串资料之间是可以互相转换的,在实际应用上,base64除了能将⼆进制数据可视化之外,也常用来表示字串加密过后的内容
推荐文章:https://blog.csdn.net/wo541075754/article/details/81734770
早期java要使用base64怎么做
//使用JDK里sun.misc套件下的base64Encoder和base64Decoder这两个类
base64Encoder encoder = new base64Encoder();
base64Decoder decoder = new base64Decoder();
String text = "hello world!";
byte[] textByte = text.getBytes("UTF-8");
//编码
String encodedText = encoder.encode(textByte);
System.out.println(encodedText);
//解码
System.out.println(new String(decoder.decodeBuffer(encodedText), "UTF-8"));
缺点:编码和解码的效率比较差,公开信息说以后的版本会取消这个方法
Apache Commons Codec有提供base64的编码与解码 缺点:是需要引用ApacheCommons Codec
jdk1.8之后怎么玩?(首选选推荐)
dk1.8的java.util包中,新增了base64的类好处:不用引包,编解码销量远大于 sun.misc 和 Apache Commons Codec
base64.Encoder encoder = base64.getEncoder();
base64.Decoder decoder = base64.getDecoder();
String text = "hello world!";
byte[] textByte = text.getBytes("UTF-8");
//编码
String encodedText = encoder.encodeToString(textByte);
System.out.println(encodedText);
//解码
System.out.println(new String(decoder.decode(encodedText), "UTF8"));
3、JDK8之时间日期处理类(上)
时间处理再熟悉不过,SimpleDateFormat,Calendar等类 旧版缺点: java.util.Date 是非线程安全的 API设计比较较差,日期/时间对象比较,加减麻烦
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理新增了很多常用的api,如日期/时间的比较,加减,格式化等
包所在位置 java.time
核心类:
LocalDate:不包含具体时间的日期。
LocalTime:不含日期的时间。
LocalDateTime:包含了日期及时间。
LocalDate 常用API:
LocalDate today = LocalDate.now();
System.out.println("今天日期:" + today);
//获取年 月 日 周几
System.out.println("现在是哪年:"+today.getYear());
System.out.println("现在是哪月:"+today.getMonth());
System.out.println("现在是哪月(数字):"+today.getMonthValue())
System.out.println("现在是几号:"+today.getDayOfMonth());
System.out.println("现在是周几:"+today.getDayOfWeek());
//加减年份, 加后返回的对象才是修改后的, 旧的依旧是旧的
LocalDate changeDate = today.plusYears(1);
System.out.println("加后是哪年:"+changeDate.getYear());
System.out.println("旧的是哪年:"+today.getYear());
//日期比较
System.out.println("isAfter: "+changeDate.isAfter(today));
//getYear() int 获取当前日期期的年份
//getMonth() Month 获取当前日期的月份对象
//getMonthValue() int 获取当前日期是第几月
//getDayOfWeek() DayOfWeek 表示该对象表示的日期是星期几
//getDayOfMonth() int 表示该对象表示的日期是这个月第几天
//getDayOfYear() int 表示该对象表示的日期是今年第几天
//withYear(int year) LocalDate 修改当前对象的年份
//withMonth(int month) LocalDate 修改当前对象的月份
//withDayOfMonth(int dayOfMonth) LocalDate 修改当前对象在当月的日期
//plusYears(long yearsToAdd) LocalDate 当前对象增加指定的年份数
//plusMonths(long monthsToAdd) LocalDate 当前对象增加指定的月份数
//plusWeeks(long weeksToAdd) LocalDate 当前对象增加指定的周数
//plusDays(long daysToAdd) LocalDate 当前对象增加指定的天数
//minusYears(long yearsToSubtract) LocalDate 当前对象减去指定的年数
//minusMonths(long monthsToSubtract) LocalDate 当前对象减去指定的月数
//minusWeeks(long weeksToSubtract) LocalDate 当前对象减去指定的周数
//minusDays(long daysToSubtract) LocalDate 当前对象减去指定的天数
//compareTo(ChronoLocalDate other) int 比较当前对象和other对象在时间上的大下,返回值如果为正,则当前对象时间较晚,
//isBefore(ChronoLocalDate other) boolean 比较当前对象日期是否在other对象日期之前
//isAfter(ChronoLocalDate other) boolean ⽐较当前对象日期是否在other对象日期之后
//isEqual(ChronoLocalDate other) boolean 比较两个日期对象是否相等
LocalTime 常用API 和 LocalDateTime 常用API 大同小异
4、JDK8之时间日期处理类(下)
日期时间格式化
JDK8之前:SimpleDateFormat来进行格式化,但SimpleDateFormat并不是线程安全的
JDK8之后:引入线程安全的日期与时间DateTimeFormatter
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String ldtStr = dtf.format(ldt);
System.out.println(ldtStr);
获取指定的日期时间对象
LocalDateTime ldt = LocalDateTime.of(2020, 11, 11, 8, 20, 30); System.out.println(ldt);
计算日期时间差 java.time.Duration
LocalDateTime today = LocalDateTime.now(); System.out.println(today); LocalDateTime changeDate = LocalDateTime.of(2020,10,1,10,40,30); System.out.println(changeDate); //第二个参数减第一个参数 Duration duration = Duration.between( today,changeDate); 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());//两个时间差的纳秒数
5、Java8之Optional类
Optional 类有啥用
主要解决的问题是空指针异常(NullPointerException)
怎么解决?
本质是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空
创建Optional类
of()
null 值作为参数传递进去,则会抛异常
Optional opt = Optional.of(user);
ofNullable()
如果对象即可能是 null 也可能是非null,应该使用ofNullable() 方法
Optional opt = Optional.ofNullable(user);
访问 Optional 对象的值
get() 方法
Optional opt = Optional.ofNullable(student);
Student s = opt.get();
如果值存在则isPresent()方法法会返回true,调用get()方法会返回该对象一般使用get之前需要先验证是否有值,不然还会报错
6、Java高级核心玩转JDK8 Lambda表达式
在JDK8之前,Java是不支持函数式编程的,所谓的函数编程,即可理解是将各个函数(也称为“行为”)作为一个参数进行传递, 面向对象编程是对数据的抽象(各种各样的POJO类),而函数式编程则是对行为的抽象(将行为作为一个参数进行传递)
lambda表达式 使用场景(前提):
一个接口中只包含一个方法,在java里面接口类名上有@FunctionalInterface,则可以使用Lambda表达式,这样的接口称之为“函数接口” 语法: (params) -> expression
@FunctionalInterface //添加此注解后,接口中只能有一个抽象方法。
public interface A {
void call();
}
第一部分为括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数;
第二部分为一个箭头符号:-> ;
第三部分为方法体,可以是表达式和代码块
参数列表 : 括号中参数列表的数据类型可以省略不写 括号中的参数只有一个,那么参数类型和()都可以省略不写
方法体: 如果{}中的代码只有一行,无论有无返回值,可以省略{}、return、分号,要一起省略,其他则需要加上
Lambda 表达式的实现方式在本质是以匿名内部类的方式进行实现
// 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)
7、Java新特性玩转JDK8之函数式编程
Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现.
Lambda表达式必须先定义接口,创建相关方法之后才可使用,这样做十分不便,其实java8已经内置了许多接口,所以一般般很少会由用户去定义新的函数式接口
Java8的最大特性就是函数式接口,所有标注了@FunctionalInterface注解的接口都是函数式接口,例如下面四个基本功能型接口,
Java8 内置的四大核心函数式接口 T:入参类型,R:出参类型 Consumer: 消费型接口:有入参,无返回值 void accept(T t); Supplier : 供给型接口:无入参,有返回值 T get(); Function : 函数型接口:有入参,有返回值 R apply(T t); Predicate : 断言型接口:有入参,有返回值,返回值类型确定是boolean boolean test(T t);
Function的应用
传入一个值经过函数的计算返回另一个值
// 输出入参的10倍 Functionfunc = p -> p * 10;
BiFunction 是继承Function
#Function只能接收一个参数,如果要传递两个参数,则用 BiFunction @FunctionalInterfacepublic interface BiFunction{ R apply(T t, U u); }
Consumer的应用
//典型应用,集合的foreach Listlist = Arrays.asList("aaa","bbb"); list.forEach(obj->{ //TODO });
Supplier的应用
#泛型一定和方法的返回值类型是一种类型,如果需要获得一个数据,并且不需要传入参数,可以使用Supplier接口,例如 无参的工厂方法,即工厂设计模式创建对象,简单来说就是 提供者
public static Student newStudent(){
Supplier supplier = ()-> {
Student student = new Student();
student.setName("默认名称");
return student;
};
return supplier.get();
}
Predicate的应用
#接收一个参数,用于判断是否满足一定的条件,过滤数据 Listlist = Arrays.asList("awewrwe","vdssdsd","aoooo","psdddsd"); List results = filter(list,obj->obj.startsWith("a"));
8、JDK8之方法法与构造函数引用
说明:方法引用是一种更简洁易懂的lambda表达式,操作符是双冒号::,用来来直接访问类或者实例已经存在的方法或构造方法
语法:左边是容器(可以是类名,实例名),中间是" :: ",右边是相应的方法名
静态方法,则是ClassName::methodName。如 Object ::equals
实例方法,则是Instance::methodName
构造函数,则是 类名::new;
public static void main(String[] args) {
// 使用双冒号::来构造静态函数引用
Function fun = Integer::parseInt;
Integer value = fun.apply("1024");
System.out.println(value);
// 使用双冒号::来构造非静态函数引用
String content = "你好世界!";
Function func = content::substring;
String result = func.apply(1);
System.out.println(result);
// 构造函数引用,多个参数
BiFunction biFunction = User::new;
User user1 = biFunction.apply("李白", 28);
System.out.println(user1.toString());
//构造函数引用,单个参数
Function function = User::new;
User user2 = function.apply("小D");
System.out.println(user2.toString());
// 函数引用也是一种函数式接口,可以将函数引用作为方法的参数
sayHello(String::toUpperCase, "www.baidu.com");
private static void sayHello(Function func, String param) {
String result = func.apply(param);
System.out.println(result);
}
}
9、Java新特性玩转JDK8之Stream流操作
map函数
将流中的每一个元素 T 映射为 R(类似类型转换)
类似遍历集合,对集合的每个对象做处理
//转换对象,集合里面的DO对象转换为DTO对象 Listlist = Arrays.asList(new User(1,"小东","123"),new User(21,"jack","rawer"), new User(155,"tom","sadfsdfsdfsd"), new User(231,"marry","234324"),new User(100,"小D","122223")); //DTO只需要id和name List userDTOList = list.stream().map(obj->{ UserDTO userDTO = new UserDTO(obj.getId(),obj.getName()); return userDTO; }).collect(Collectors.toList()); System.out.println(userDTOList);
filter函数
用于通过设置的条件过滤出元素
Listlist = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker"); //需求:过滤出字符串长度大于5的字符串 List resultList = list.stream().filter(obj -> obj.length() > 5).collect(Collectors.toList()); System.out.println(resultList);
sorted函数
orted() 对流进行自然排序, 其中的元素必须实现Comparable 接口
Listlist = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker"); List resultList = list.stream().sorted().collect(Collectors.toList());
sorted(Comparator super T> comparator) 用来自定义升降序
Listlist = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker"); //根据长度进行自然排序 List resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList()); //倒序 List resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList()); //倒序 List resultList = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());
limit函数
截断流使其最多只包含指定数量的元素
Listlist = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker"); //limit截取 List resultList = list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());System.out.println(resultList);
allMatch和anyMatch函数
Listlist = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker"); //检查是否匹配所有元素,只有全部符合才返回true boolean flag = list.stream().allMatch(obj->obj.length()>1); //检查是否至少匹配一个元素 boolean flag = list.stream().anyMatch(obj->obj.length()>18);
max和min函数
Listlist = Arrays.asList(new Student(32),new Student(33),new Student(21),new Student(29),new Student(18)); //最大 list.stream().max(Comparator.comparingInt(Student::getAge)); //最大 Optional optional = list.stream().max((s1, s2)->Integer.compare(s1.getAge(),s2.getAge())); //最小 Optional optional = list.stream().min((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));
reduce操作
聚合操作,中文意思是 “减少”
根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
// accumulator 计算的累加器 Optionalreduce(BinaryOperator accumulator); //例子: 第一个元素相加和第二个元素相加,结果再和第三个元素相加,直到全部相加完成 int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();
//identity 用户提供一个循环计算的初始值 //accumulator 计算的累加器 //例子: 100作为初始值,然后和第一个元素相加,结果在和第二个元素相加,直到全部相加完成 T reduce(T identity, BinaryOperatoraccumulator); int value = Stream.of(1, 2, 3, 4,5).reduce(100, (sum, item) -> sum + item);
//求最大值 int value = Stream.of(1645, 234345, 32, 44434,564534,435,34343542,212).reduce( (item1, item2) -> item1 > item2 ? item1 : item2 ).get();
集合的foreach
集合遍历的方式
for循环 迭代器 Iterator
注意点:
不能修改包含外部的变量的值
不能用break或者return或者continue等关键词结束或者跳过循环
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
List results = Arrays.asList(new Student(32),new Student(33),new Student(21),new Student(29),new Student(18));
results.forEach(obj->{ System.out.println(obj.toString());});
collect()方法
一个终端操作, 用于对流中的数据进行归集操作,collect方法接受的参数是一个Collector(收集器)
Collector的作用:
就是收集器,也是一个接口, 它的工具类Collectors提供了很多工厂方法
Collectors 的作用:
工具类,提供了很多常见的收集器实现
Collectors.toList() Collectors.toMap() Collectors.toSet() Collectors.toCollection()//用户自定义的实现Collection的数据结构收集 Collectors.toCollection(linkedList::new) Collectors.toCollection(CopyOnWriteArrayList::new) Collectors.toCollection(TreeSet::new)
joining函数
拼接函数 Collectors.joining
3种重载方法
Collectors.joining()
Collectors.joining(“param”)
Collectors.joining(“param1”, “param2”, “param3”)
//该方法可以将Stream得到一个字符串, joining函数接受三个参数,分别表示:元素之间的连接符、前缀、后缀。
String result = Stream.of("springboot", "mysql", "html5", "css3").collect(Collectors.joining(",", "[", "]"));
partitioningBy分组和group by分组
Collectors.partitioningBy 分组,key是boolean类型
//根据list进行分组,字符串长度大于4的为一组,其他为另外一组 Listlist = Arrays.asList("java", "springboot", "HTML5","nodejs","CSS3"); Map > result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));
Collectors.groupingBy()分组,key是用户可指定的类型
//根据学生所在的省份,进行分组 Map> listMap = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince())); listMap.forEach((key, value) -> { System.out.println("========"); System.out.println(key); value.forEach(obj -> { System.out.println(obj.getAge()); }); });
summarizing 集合统计
可以一个方法把统计相关的基本上都完成
分类:
summarizingInt
summarizingLong
summarizingDouble
//统计学生的各个年龄信息 Liststudents = Arrays.asList(new Student("广东", 23), new Student("广东", 24), new Student("广东", 23),new Student("北京", 22), new Student("北京", 20), new Student("北京", 20),new Student("海南", 25)); IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge)); System.out.println("平均值:" + summaryStatistics.getAverage()); System.out.println("人数:" + summaryStatistics.getCount()); System.out.println("最大值:" + summaryStatistics.getMax()); System.out.println("最小值:" + summaryStatistics.getMin()); System.out.println("总和:" + summaryStatistics.getSum());
10、Java新特性玩转JDK8之并行流parallelStream
为什么会有这个并行流?
集合做重复的操作,如果使用串行执行会相当耗时,因此一般会采用多线程来加快, Java8的paralleStream用fork/join框架提供了并发执行能力
底层原理:
线程池(ForkJoinPool)维护一个线程队列
可以分割任务,将父任务拆分成子任务,完全贴合分治思想
两个区别:
//顺序输出 Listnumbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.stream().forEach(System.out::println); //并行乱序输出 List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.parallelStream().forEach(System.out::println);
问题
paralleStream并行是否一定比Stream串行快?
错误,数据量少的情况,可能串⾏更快,ForkJoin会耗性能
多数情况下并行比串行快,是否可以都用并行
不行,部分情况会有线程安全问题,parallelStream里面使用的外部变量,比如集合一定要使用线程安全集合,不然就会引发多线程安全问题



