1. JDK8之default关键字
在jdk1.8之前接口里面是只能有抽象方法,不能有任何方法的实现,在jdk1.8里面打破了这个定义,引入了default关键字,使用default修饰方法,可以在接口里面定义具体的方法实现,即接口的默认方法,这个接口的实现类实现了接口之后,不用管这个default修饰的方法就可以直接调用,即接口方法的默认实现
public interface Animal {
void run();
void eat();
default void breath() {
System.out.println("呼吸氧气");
}
}
public class Dog implements Animal {
@Override
public void run() {
System.out.println("跑起来");
}
@Override
public void eat() {
System.out.println("吃饭");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
dog.eat();
dog.breath();
}
}
- 使用场景:接口里面定义公用的业务逻辑,抽取出来,每个子类都必须具备
2. JDK8之新增base64加密
Base64是⽹络上最常⻅的⽤于传输8Bit字节码的编码⽅式之⼀,Base64就是
⼀种基于64个可打印字符来表示⼆进制数据的方法 基于64个字符A-Z,a-z,0-9,+,/的编码方式,是⼀种能将任意⼆进制数据用64种字元组合成字符串的方法,而这个⼆进制数据和字符串资料之间是可以互相转换的,在实际应用上,Base64除了能将⼆进制数据可视化之外,也常⽤来表示字符串加密过后的内容
2.1 jdk1.8之前实现方式
早起base64使用jdk里sun.misc包下的BASE64Encoder和BASE64Decoder这两个类
BASE64Encoder encoder = new BASE64Encoder();
BASE64Decoder decoder = new BASE64Decoder();
String text = "测试字符串";
byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
// 编码
String encode = encoder.encode(bytes);
System.out.println(encode);
// 解密
System.out.println(new String(decoder.decodeBuffer(encode), "UTF-8"));
2.2 jdk1.8之后实现方式
Base64.Encoder encoder = Base64.getEncoder();
Base64.Decoder decoder = Base64.getDecoder();
String text = "测试字符串";
byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
// 编码
String encode = encoder.encodeToString(bytes);
System.out.println(encode);
// 解码
System.out.println(new String(decoder.decode(encode), "UTF-8"));
区别:1.8之前base64编码和解码效率比较差,公开信息说以后的版本会取消这个方法。jdk1.8之后在java.util包中新增了base64的类,编解码效率远大于sun.msic
3. JDK8之时间日期处理类
时间处理再熟悉不过,SimpleDateFormat, Calendar等类,旧版日期API缺点很严重,java.util.Date是非线程安全的,API设计比较差,日期/时间转换,加减,比较等操作很麻烦。Java8通过发布新的Date-Time API来进一步加强日期与时间的处理。
- 新增了很多场景API,如日期/时间的比较,加减,格式化等
- 包所在位置java.time
核心类:
- LocalDate : 不包含具体时间的日期
- LocalTime : 不含日期的时间
- LocalDateTime :包含了日期及时间
LocalDate代码示例
public class Main {
public static void main(String[] args) {
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(10);
System.out.println("加后的日期" + changeDate);
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 和 LocalDateTime 可以自行查找相关API,都是类似语法。
4. JDK8之时间日期格式化与比较
JDK8之前使用SimpleDateFormat来进行格式化,但SimpleDateFormat并不是线程安全的。JDK8之后引入线程安全的DateTimeFormat
时间格式转换
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 of = LocalDateTime.of(2021, 11, 8, 22, 44, 36);
System.out.println(of);
计算日期时间差
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
LocalDateTime changeDate = LocalDateTime.of(2020, 11, 11, 8, 12, 15);
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. JDK8之Optional类
Optional类主要用来处理空指针异常问题(NullPointerException)。
这个类本质是一个包含有可选值的包装类,这意味着Optional类既可以含有对象也可以为空
of()方法
of()方法当null值作为参数传递进去时,会抛出异常
public class Main {
public static void main(String[] args) {
Student student = null;
Optional<Student> optional = Optional.of(student);
}
}
执行结果
ofNullable()方法
如果对象即可能是null也可能是非null,应该使用ofNullable()方法
public class Main {
public static void main(String[] args) {
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
}
}
访问Optinal对象的值
public class Main {
public static void main(String[] args) {
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
Student s = optional.get();
}
}
如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象一般使用get之前需要先验证是否有值,不然还会报错
public class Main {
public static void main(String[] args) {
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
if (optional.isPresent()){
System.out.println("value is not null");
Student s = optional.get();
System.out.println(s);
} else {
System.out.println("value is null");
}
}
}
orElse()方法
orElse()如果有值返回该值,否则返回传递给它的参数值
Student student1 = null;
Student student2 = new Student(3);
Student result = Optional.ofNullable(student1).orElse(student2);
System.out.println(result.getAge());
执行结果为3,将student2的值赋值给了student1
另一种Lambda表达式方式写法
Student student3 = null;
Integer rs = Optional.ofNullable(student3).map(obj -> obj.getAge()).orElse(4);
System.out.println(rs);
System.out.println(student3.getAge());
rs返回值为4,但是最后一行打印会报出空指针异常
6. JDK8之Lambda表达式
在JDK8之前,Java是不支持函数式编程的,所谓的函数式编程,即可理解是将一个函数(也称为“行为”)作为一个参数进行传递,面向对象编程是对数据的抽象(各种各类的POJO),而函数式编程则是对行为的抽象(将行为作为一个参数进行传递)
jdk8之前线程:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("执行线程任务");
}
}).start();
jdk8之后用lambda表达式创建线程只需要一句话
new Thread(()-> System.out.println("执行线程任务")).start();
jdk8之前对集合里的字符串排序
List<String> list = Arrays.asList("aaa", "fsdfsd", "eqwe", "asd", "tre");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
jdk8之后对使用lambda表达式对集合里的字符串进行排序
List<String> list = Arrays.asList("aaa", "fsdfsd", "eqwe", "asd", "tre");
Collections.sort(list, (a,b)->a.compareTo(b));
lambda表达式使用场景(前提): 一个接口中只包含一个方法,则可以使用Lambda表达式,这样的接口称之为"函数接口"语法:(params)->expression
第一部分为括号内逗号分隔的形式参数,参数是函数式接口里面方法的参数;第二部分为一个箭头符号:-> ; 第三部分为方法体,可以是表达式和代码块
参数列表:
- 括号中参数列表的数据类型可以省略不写
- 括号中的参数只有一个,那么参数类型和()都可以省略
方法体:
如果{}中的代码只有一行,无论有返回值,都可以省略{} , return, 分号, 要一起省略,其他则需要加上
好处:Lambda表达式的实现方式在本质是以匿名内部类的方式进行实现
7. JDK8之自定义函数式编程实战
自定义lambda接口流程
- 定义一个函数式接口,需要标注此接口@FunctionalInterface,否则万一团队成员在接口上加了其他方法则容易出故障
- 编写一个方法,输入需要操作的数据和接口
- 在调用方法时传入数据和lambda表达式,用来操作数据
比如我们定义一个加减乘除的接口,以前需要定义4个方法
@FunctionalInterface
public interface OperFunction<R,T> {
R operator(T t1, T t2);
}
public class Oper {
public static Integer operator(int x, int y, OperFunction<Integer, Integer> of) {
return of.operator(x, y);
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Oper.operator(20, 4, (x, y)-> x * y));
System.out.println(Oper.operator(20, 4, (a, b) -> a + b));
System.out.println(Oper.operator(20, 4, (a, b) -> a - b));
System.out.println(Oper.operator(20, 4, (a, b) -> a / b));
System.out.println(Oper.operator(20, 4, (x, y)-> x * y * x + y));
}
}
8. JDK8提供的函数式编程实战
Lambda表达式必须先定义接口,创建相关方法之后才可使用,这样做十分不便,java8已经内置了许多接口,例如下面四个功能型接口,所以一般很少会由用户定义新的函数式接口。java8最大特性就是函数式接口,所有标注了@FunctionalInterface注解的接口都是函数式接口
Java8 内置的四⼤核⼼函数式接⼝
Consumer<T> : 消费型接⼝:有⼊参,⽆返回值
void accept(T t);
Supplier<T> : 供给型接⼝:⽆⼊参,有返回值
T get();
Function<T, R> : 函数型接⼝:有⼊参,有返回值
R apply(T t);
Predicate<T> : 断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean
boolean test(T t);
8.1 Function
Function传入一个值经过函数的计算返回另一个值
T:入参类型 R:出参类型
调用方法:R apply(T t);
实现:
public class FunctionObj implements Function {
@Override
public Object apply(Object o) {
return o + "拼接哈哈哈";
}
}
public class Oper {
public static String operator(String s, FunctionObj obj) {
return obj.apply(s).toString();
}
}
public class Main {
public static void main(String[] args) {
String csdn = Oper.operator("CSDN", new FunctionObj());
System.out.println(csdn);
}
}
另一种常规用法
public class Main {
public static void main(String[] args) {
Function<Integer, Integer> function = p->{
return p*100;
};
Integer apply = function.apply(100);
System.out.println(apply);
}
}
8.2 BIFunction
Function只能传递一个参数,如果要传递两个参数,则用BIFunction
public class BOper {
public static Integer operator(Integer a, Integer b, BiFunction<Integer, Integer, Integer> of) {
return (Integer) of.apply(a, b);
}
}
public class Main {
public static void main(String[] args) {
System.out.println(BOper.operator(10 ,3, (a,b)->a*b));
System.out.println(BOper.operator(10 ,3, (a,b)->a+b));
System.out.println(BOper.operator(10 ,3, (a,b)->a-b));
System.out.println(BOper.operator(10 ,3, (a,b)->a/b));
}
}
8.3 Consumer
Consumer消费型接口,有入参,无返回值
将T作为输入,不返回任何内容
调用方法:void accept(T t);
由于没有出参,常用于打印、发送短信等消费动作
实现
public class Main {
public static void main(String[] args) {
Consumer<String> consumer = obj -> {
System.out.println(obj);
System.out.println("调用短信接口发送短信");
};
System.out.println("start");
consumer.accept("17712345678");
sendMsg("11234214", obj->{
System.out.println(obj);
System.out.println("调用短信接口发送短信");
});
}
public static void sendMsg(String phone, Consumer<String> consumer) {
consumer.accept(phone);
}
}
典型应用
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("asda", "rterter");
list.forEach(obj -> {
System.out.println(obj);
});
}
}
8.4 Supplier
Supplier供给型接口:无入参,有返回值
T:出参类型,没有入参
调用方法 :T get();
示例:
public class Main {
public static void main(String[] args) {
System.out.println(getStudent());
}
public static User getStudent() {
Supplier<User> supplier = ()->{
User user = new User();
user.setName("张三");
user.setAge(18);
return user;
};
return supplier.get();
}
}
class User{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
8.5 Predicate
Predicate : 断言行接口:有入参,有返回值,返回值类型确定是boolean
T : 入参类型; 出参类型必须是Boolean
调用方法 : boolean test(T t);
用途:用于接收一个参数,用于判断是否满足一定的条件,过滤数据
public class Main {
public static void main(String[] args) {
List<String> arr = Arrays.asList("afwrwerw", "rwerwer", "awtertert", "Bwrtert", "utyu");
List<String> results = filter(arr, obj -> obj.startsWith("a"));
System.out.println(results);
}
}
public static List<String> filter(List<String> list, Predicate<String> predicate) {
List<String> results = new ArrayList<>();
list.forEach(obj -> {
if (predicate.test(obj)) {
results.add(obj);
}
});
return results;
}
9. JDK8之流Stream的使用
Stream中文称为流,通过将集合转换为这么一种叫做流的元素队列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。
元素是特定类型的对象,所以元素集合看作一种流,流在管道中传输,且可以在管道的节点上进行处理,比如排序,聚合,过滤等操作。
操作详情
- 数据元素便是原始集合,如List、Set、Map等
- 生成流,可以是串行流stream()或者是并行流parallelStream(),建议使用串行流
- 中间操作,可以是排序,聚合,过滤,转换等
- 终端操作,很多流操作本身就返回一个流,所以多个操作可以直接连接起来,最后统一进行收集
示例
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("JAVA", "PHP", "Go", "C++", "Python");
List<String> result = list.stream().map(obj -> "今天学习" + obj).collect(Collectors.toList());
System.out.println(result);
}
}
10. JDK8之map和filter函数的使用
10.1 map函数
- 将流中每一个元素T映射为R (类似类型转换)
示例场景:转换对象
public class Main {
public static void main(String[] args) {
List<String> userList = Arrays.asList("eqeqwe", "sgdfgdf", "eryrete", "sfsdfsdfs", "wrwrw");
List<UserDto> list = userList.stream().map(obj -> {
return new UserDto(obj);
}).collect(Collectors.toList());
System.out.println(list);
}
}
public class UserDto {
private String username;
public UserDto(){
}
public UserDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "UserDto{" +
"username='" + username + '\'' +
'}';
}
}
10.2 filter函数
- 用于通过设置的条件过滤出元素
示例:
public class Main {
public static void main(String[] args) {
List<String> userList = Arrays.asList("eqeqwe", "sgdfgdf", "eryrete", "sfsdfsdfs", "wrwrw");
List<String> stringList = userList.stream().filter(obj -> {
if (obj.length() > 6) {
return true;
} else {
return false;
}
}).collect(Collectors.toList());
System.out.println(stringList);
}
}
11. JDK8之limit和sorted函数
11.1 sorted函数
sorted()对流进行自然排序,其中的元素必须实现Comparable接口
sorted(Comparator<? super T> comparator) ⽤来⾃定义升降序
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springCloud", "springBoot", "redis", "dubbo", "rocketmq", "spring", "mybatis");
// 根据字符串长度升序排序
List<String> result = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());
System.out.println(result);
// 根据字符串长度降序排序
List<String> result1 = list.stream().sorted(Comparator.comparing(obj -> obj.length(), Comparator.reverseOrder())).collect(Collectors.toList());
System.out.println(result1);
}
}
11.2 limit函数
截断流使其最多包含指定数量的元素
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springCloud", "springBoot", "redis", "dubbo", "rocketmq", "spring", "mybatis");
List<String> result2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).limit(3).collect(Collectors.toList());
System.out.println(result2);
}
}
12. JDK8之allMatch和anyMatch函数
12.1 allMatch函数
检查是否匹配所有元素,只有全部符合才返回true
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springboot", "springcloud", "redis",
"git", "netty", "java", "html", "docker");
boolean result = list.stream().allMatch(obj -> obj.length() > 5);
System.out.println(result);
}
}
12.2 anyMatch函数
检查是否⾄少匹配⼀个元素
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springboot", "springcloud", "redis",
"git", "netty", "java", "html", "docker");
boolean result2 = list.stream().anyMatch(obj -> obj.length() > 10);
System.out.println(result2);
}
}
13. JDK8之max和min函数
max函数和min函数取集合里的最大值和最小值
public class Main {
public static void main(String[] args) {
List<User> list = Arrays.asList(new User(10), new User(8), new User(25), new User(32), new User(7));
Optional<User> max = list.stream().max((a, b) -> Integer.compare(a.getAge(), b.getAge()));
System.out.println(max.get().getAge());
Optional<User> min = list.stream().min((a, b) -> Integer.compare(a.getAge(), b.getAge()));
System.out.println(min.get().getAge());
}
}
public class User {
private int age;
public User(){
}
public User(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
14. JDK8之foreach操作
集合便利的方式有for循环或者迭代器,JDK8里面新增了foreach接口
public class Main {
public static void main(String[] args) {
List<User> list = Arrays.asList(new User(10), new User(8), new User(25), new User(32), new User(7));
list.forEach(obj -> {
System.out.println(obj.getAge());
});
}
}
注意:
- 不能修改包含外部的变量的值
- 不能⽤break或者return或者continue等关键词结束或者跳过循环
15. JDK8之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);
常用第二个重载方法,JDK提供了很多常用方法,无需自己自定义之后使用第一种
常用的方法有
- Collectors.toList()
- Collectors.toMap()
- Collectors.toSet()
- Collectors.toCollection() :⽤⾃定义的实现Collection的数据结构收集
- Collectors.toCollection(LinkedList::new)
- Collectors.toCollection(CopyOnWriteArrayList::new)
- Collectors.toCollection(TreeSet::new)
toMap方法示例
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springCloud", "springBoot", "redis", "dubbo", "rocketmq", "spring", "mybatis");
Map<String, String> map = list.stream().collect(Collectors.toMap(a -> a+"asd", a -> a));
System.out.println(map);
}
}
toSet方法示例
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springCloud", "springBoot", "redis", "dubbo", "rocketmq", "spring", "mybatis");
Set<String> result = list.stream().collect(Collectors.toSet());
System.out.println(result);
}
}
16. JDK8之joining函数
joining函数用于拼接字符串
joining函数有三种重载方法
Collectors.joining()
Collectors.joining("分隔符")
Collectors.joining("分隔符", "前缀", "后缀")
示例
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("springCloud", "springBoot", "redis", "dubbo", "rocketmq", "spring", "mybatis");
String result1 = list.stream().collect(Collectors.joining());
System.out.println(result1);
String result2 = list.stream().collect(Collectors.joining(", "));
System.out.println(result2);
String result3 = list.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(result3);
String result4 = Stream.of("java", "php", "Go", "Python").collect(Collectors.joining(",", "[", "]"));
System.out.println(result4);
}
}