玩转jdk8新特性

本文概述了Java 8的重要更新,包括接口默认方法的使用、简化Base64编码/解码、时间日期API增强和Optional类的应用,以及Lambda表达式和Stream流操作的基础知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.youkuaiyun.com/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<T> : 消费型接口:有入参,无返回值 
	void accept(T t);
	
Supplier<T> : 供给型接口:无入参,有返回值
	T get();
	
Function<T, R> : 函数型接口:有入参,有返回值
	R apply(T t);
	
Predicate<T> : 断言型接口:有入参,有返回值,返回值类型确定是boolean
	boolean test(T t);

Function的应用
传入一个值经过函数的计算返回另一个值

// 输出入参的10倍
Function<Integer, Integer> func = p -> p * 10; 

BiFunction 是继承Function

#Function只能接收一个参数,如果要传递两个参数,则用 BiFunction
@FunctionalInterfacepublic interface BiFunction<T, U, R> { 
	R apply(T t, U u);
}

Consumer的应用

//典型应用,集合的foreach
List<String> list = Arrays.asList("aaa","bbb");
list.forEach(obj->{ 
	//TODO
});

Supplier的应用

#泛型一定和方法的返回值类型是一种类型,如果需要获得一个数据,并且不需要传入参数,可以使用Supplier接口,例如 无参的工厂方法,即工厂设计模式创建对象,简单来说就是 提供者
public static Student newStudent(){ 
    Supplier<Student> supplier = ()-> { 
    		Student student = new Student();
    		student.setName("默认名称"); 
    		return student;     
    	};
    	return supplier.get();
    }
    

Predicate的应用

#接收一个参数,用于判断是否满足一定的条件,过滤数据
List<String> list = Arrays.asList("awewrwe","vdssdsd","aoooo","psdddsd");
List<String> results = filter(list,obj->obj.startsWith("a"));

8、JDK8之方法法与构造函数引用

说明:方法引用是一种更简洁易懂的lambda表达式,操作符是双冒号::,用来来直接访问类或者实例已经存在的方法或构造方法
语法:左边是容器(可以是类名,实例名),中间是" :: ",右边是相应的方法名
静态方法,则是ClassName::methodName。如 Object ::equals
实例方法,则是Instance::methodName
构造函数,则是 类名::new;

public static void main(String[] args) {
// 使用双冒号::来构造静态函数引用
 Function<String, Integer> fun = Integer::parseInt; 
 Integer value = fun.apply("1024"); 
 System.out.println(value);
 // 使用双冒号::来构造非静态函数引用
 String content = "你好世界!";
  Function<Integer, String> func = content::substring; 
  String result = func.apply(1); 
  System.out.println(result);
 // 构造函数引用,多个参数
 BiFunction<String, Integer, User> biFunction = User::new;
 User user1 = biFunction.apply("李白", 28);
 System.out.println(user1.toString());

//构造函数引用,单个参数
Function<String, User> function = User::new;
User user2 = function.apply("小D");
System.out.println(user2.toString());

// 函数引用也是一种函数式接口,可以将函数引用作为方法的参数
sayHello(String::toUpperCase, "www.baidu.com");


/** *
 *@param func 函数引用
 * @param param 对应的参数 
 */
private static void sayHello(Function<String, String> func, String param) { 	     
     String result = func.apply(param); 
     System.out.println(result); 
  }
}

9、Java新特性玩转JDK8之Stream流操作
在这里插入图片描述
在这里插入图片描述
map函数

将流中的每一个元素 T 映射为 R(类似类型转换)
类似遍历集合,对集合的每个对象做处理

//转换对象,集合里面的DO对象转换为DTO对象
List<User> list = 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<UserDTO> userDTOList = list.stream().map(obj->{ 
		UserDTO userDTO = new UserDTO(obj.getId(),obj.getName()); 			
		return userDTO;
	 }).collect(Collectors.toList()); System.out.println(userDTOList);
	

filter函数

用于通过设置的条件过滤出元素

List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
//需求:过滤出字符串长度大于5的字符串
List<String> resultList = list.stream().filter(obj -> obj.length() > 5).collect(Collectors.toList());

System.out.println(resultList);

sorted函数

orted() 对流进行自然排序, 其中的元素必须实现Comparable 接口

List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");

List<String> resultList = list.stream().sorted().collect(Collectors.toList());

sorted(Comparator<? super T> comparator) 用来自定义升降序

List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
//根据长度进行自然排序
List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());

//倒序
List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());

//倒序
List<String> resultList = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());

limit函数

截断流使其最多只包含指定数量的元素

List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");

//limit截取
List<String> resultList = list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());System.out.println(resultList);

allMatch和anyMatch函数


List<String> list = 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函数

List<Student> list = 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<Student> optional = list.stream().max((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));
//最小
Optional<Student> optional = list.stream().min((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));

reduce操作

聚合操作,中文意思是 “减少”
根据一定的规则将Stream中的元素进行计算后返回一个唯一的值

// accumulator 计算的累加器
Optional<T> reduce(BinaryOperator<T> accumulator);
//例子: 第一个元素相加和第二个元素相加,结果再和第三个元素相加,直到全部相加完成
int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();
//identity 用户提供一个循环计算的初始值
//accumulator 计算的累加器
//例子: 100作为初始值,然后和第一个元素相加,结果在和第二个元素相加,直到全部相加完成
T reduce(T identity, BinaryOperator<T> accumulator);
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<Student> 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的为一组,其他为另外一组
List<String> list = Arrays.asList("java", "springboot", "HTML5","nodejs","CSS3");
Map<Boolean, List<String>> result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));

Collectors.groupingBy()分组,key是用户可指定的类型

//根据学生所在的省份,进行分组
Map<String, List<Student>> 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

//统计学生的各个年龄信息
 List<Student> students = 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());

自定义distinctByKey 指定属性集合去重

  public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
    
  ArrayList<UserDataRecord> list = new ArrayList<>();
  List<UserDataRecord> collect = list.stream().filter(distinctByKey(UserDataRecord::getShopTreeId)).collect(Collectors.toList());   

10、Java新特性玩转JDK8之并行流parallelStream

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

两个区别:

//顺序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.stream().forEach(System.out::println);

//并行乱序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);numbers.parallelStream().forEach(System.out::println);

问题

paralleStream并行是否一定比Stream串行快?
错误,数据量少的情况,可能串⾏更快,ForkJoin会耗性能
多数情况下并行比串行快,是否可以都用并行
不行,部分情况会有线程安全问题,parallelStream里面使用的外部变量,比如集合一定要使用线程安全集合,不然就会引发多线程安全问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值