优雅编程 - 语法糖
2019-12-19 本文已影响0人
林昀熙
本篇采用示例的形式展示Java8的常见特性应用.
线程写法
Java8之前
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("自定义线程");
}
}).start();
Java8写法
new Thread(()-> {
System.out.println("自定义线程");
}).start();
比较器写法
Java8之前
List<String> list = Arrays.asList("bb", "a", "ccc");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// [a, bb, ccc]
System.out.println(list);
Java8写法
Collections.sort(list, (o1, o2) -> o1.length() - o2.length());
// [a, bb, ccc]
System.out.println(list);
遍历写法
Java8之前
List<String> list = Arrays.asList("bb", "a", "ccc");
for (String li : list) {
System.out.println(li);
}
Java8写法
List<String> list = Arrays.asList("bb", "a", "ccc");
list.forEach(li -> System.out.println(li));
list.forEach(System.out::println);
计算过滤
List<Integer> numbers = Arrays.asList(20,22,1,2,1,3,3,2,4,8,16);
// 过滤集合中的偶数,去重、排序
List<Integer> filters = numbers.stream().filter(i -> i % 2 == 0).distinct().sorted().collect(Collectors.toList());
// 248162022
filters.forEach(System.out::print);
对列表每个元素应用函数
List<String> list = Arrays.asList("USA", "Japan", "France", "Germany", "Italy","Canada");
// 将字符串换成大写并用点号链接起来
String str = list.stream().map(x -> x.toUpperCase()).collect(Collectors.joining("、"));
// USA、JAPAN、FRANCE、GERMANY、ITALY、CANADA
System.out.println(str);
lambda表达式中的Map Reduce
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400);
double bill = costBeforeTax.stream().map((cost) -> cost + .10*cost).reduce((sum, cost) -> sum + cost).get();
// 1100.0
System.out.println("集合中的元素每个增大10%后的结果总和: " + bill);
集合元素计算
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("集合中最大值 : " + stats.getMax());
System.out.println("集合中最小值 : " + stats.getMin());
System.out.println("集合元素总数 : " + stats.getSum());
System.out.println("集合元素均值 : " + stats.getAverage());
集合元素分组
public static void main(String[] args) {
List<Person> list = Arrays.asList(new Person(10, "Elon"), new Person(12, "Dennisit"), new Person(10, "Alone"));
// [{"age":10,"name":"Elon"}, {"age":12,"name":"Dennisit"}, {"age":10,"name":"Alone"}]
System.out.println(list);
Map<Integer, List<Person>> groupByAge = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.toList()));
// {10=[{"age":10,"name":"Elon"}, {"age":10,"name":"Alone"}], 12=[{"age":12,"name":"Dennisit"}]}
System.out.println(groupByAge);
List<String> names = list.stream().map(e->e.getName()).collect(Collectors.toList());
// [Elon, Dennisit, Alone]
System.out.println(names);
List<Integer> ages = list.stream().map(e->e.getAge()).distinct().collect(Collectors.toList());
// [10, 12]
System.out.println(ages);
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class Person{
private int age;
private String name;
public String toString(){
return JSON.toJSONString(this);
}
}
集合对象排序
public static void main(String[] args) {
List<Person> list = Arrays.asList(new Person(10, "Elon"), new Person(12, "Dennisit"), new Person(10, "Alone"));
// 按照年龄降序
List<Person> sort = list.stream()
.sorted(Comparator.comparing(Person::getAge).reversed())
.collect(Collectors.toList());
// [{"age":12,"name":"Dennisit"}, {"age":10,"name":"Elon"}, {"age":10,"name":"Alone"}]
System.out.println(sort);
}
函数式接口
// 函数接口定义
Predicate<String> startWithJ = (n) -> n.startsWith("J");
Predicate<String> containedA = (n) -> n.contains("a");
List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
// 使用逻辑函数合并Predicate 执行结果: Java,Scala,Haskell,
languages.stream().filter(startWithJ.or(containedA)).forEach(li -> System.out.print(li + ","));
函数接口应用
我们在用全文索引的时候需要将数据先存储到索引然后进行搜索,索引数据一般操作有全量索引、增量索引、实施索引, 索引创建方式可能有databus、基于消息机制、或者基于定时任务等等.
通常我们把DB数据转换为搜索引擎的索引的时候,分两步:
- 筛选出待索引的数据
- 将目标数据进行索引
针对这个行为我们来定义行为规约, 函数接口定义时使用@FunctionalInterface
筛选数据行为
/**
* 筛选数据行为规约
*/
@FunctionalInterface
public interface DataCriteria<T> {
/**
* 分页加载数据
* @param page 页码
* @param size 页量
* @return 加载的数据集合
*/
public List<T> loader(int page, int size);
}
索引数据行为规约
/**
* 索引数据行为规约
*/
@FunctionalInterface
public interface IndexCriteria {
/**
* 数据索引行为
* @param list 待索引的数据
* @return
*/
public void index(List<?> list);
}
全量索引行为抽象
/**
* 每次批量处理的数据一次最大500个
*/
public static final Integer DEFAULT_BATCH_SIZE = 500;
/**
* 私有化构造
*/
private IndexAction(){
}
/**
* 执行全量索引
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
*/
public static void fullyIndex(DataCriteria dataCriteria, IndexCriteria indexCriteria) {
fullyIndex(DEFAULT_BATCH_SIZE, dataCriteria, indexCriteria);
}
/**
* 执行全量索引
* @param size 每个批次索引的数据大小
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
*/
public static void fullyIndex(int size, DataCriteria dataCriteria, IndexCriteria indexCriteria) {
// 游标页从第一页开始
int current = 1;
// 约束批量处理的大小
size = NumberUtils.restrainNum(size, 0, DEFAULT_BATCH_SIZE);
while(true){
int cursor = cursor(current, size, dataCriteria, indexCriteria);
if (cursor == current) {
LOG.info("[创建索引] 创建完成, {}*{}数据", size, cursor);
break;
}
current = cursor;
}
}
/**
* 游标扫描处理
* @param page 游标页
* @param size 每个批次索引的数据大小
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
* @return
*/
private static int cursor(int page, int size, DataCriteria dataCriteria, IndexCriteria indexCriteria){
Assert.notNull(dataCriteria, "数据操作不能为空");
Assert.notNull(indexCriteria, "操作操作不能为空");
List list = dataCriteria.loader(page, size);
if(CollectionUtils.isNotEmpty(list)){
indexCriteria.index(list);
++ page;
}
return page;
}
上面我们做了简单的全量索引行为抽象, 从第一页开始分页加载数据进行索引,直到我们加载到的数据为空,本次索引行为结束
特定索引行为抽象
/**
* 指定数据索引
* @param list 待索引数据
* @param indexCriteria 索引行为
*/
public static void pointIndex(List<?> list, IndexCriteria indexCriteria){
if(CollectionUtils.isEmpty(list)){
return;
}
Assert.notNull(indexCriteria, "操作操作不能为空");
indexCriteria.index(list);
}
该抽象比较简单,直接进行指定的数据索引即可.
规约使用
上面我们定义了规约, 接下来直接展示如何优雅的基于规约装载数据
/**
* 全量索引
*/
@Override
public void fullyIndex() {
IndexAction.fullyIndex(
// 分页加载数据
(p, s) -> {
return merchantService.selectList(p, s);
},
// 数据进行索引
(x)-> merchantEsRepository.indexList((List<MerchantIndex>)x)
);
}
/**
* 特定索引
* @param ids 主键编号集合
* @throws Exception
*/
@Override
public void pointIndex(List<Integer> ids) throws Exception{
List<MerchantIndex> list = merchantService.selectList(ids);
IndexAction.pointIndex(list, (x) -> merchantEsRepository.indexList((List<MerchantIndex>) x));
}
示例中分别展示了全量索引和特定数据索引的规约应用,通过函数接口功能打包.我们把全量和增量的行为就隐藏在了规约行为中,使业务代码更简洁优雅.
接口方法
public static void main(String[] args) {
MathOperation addition = (int a, int b) -> a + b;
// 默认方法
addition.print();
// 9
System.out.println(addition.operation(5, 4));
}
interface MathOperation {
// 接口方法
int operation(int a, int b);
default void print(){
System.out.println("默认方法");
}
}
Java8日期操作
/创建日期
LocalDate date = LocalDate.of(2017,1,21); //2017-01-21
int year = date.getYear() //2017
Month month = date.getMonth(); //JANUARY
int day = date.getDayOfMonth(); //21
DayOfWeek dow = date.getDayOfWeek(); //SATURDAY
int len = date.lengthOfMonth(); //31(days in January)
boolean leap = date.isLeapYear(); //false(not a leap year)
//时间的解析和格式化
LocalDate date = LocalDate.parse("2017-01-21");
LocalTime time = LocalTime.parse("13:45:20");
LocalDateTime now = LocalDateTime.now();
now.format(DateTimeFormatter.BASIC_ISO_DATE);
//合并日期和时间
LocalDateTime dt1 = LocalDateTime.of(2017, Month.JANUARY, 21, 18, 7);
LocalDateTime dt2 = LocalDateTime.of(localDate, time);
LocalDateTime dt3 = localDate.atTime(13,45,20);
LocalDateTime dt4 = localDate.atTime(time);
LocalDateTime dt5 = time.atDate(localDate);
//操作日期
LocalDate date1 = LocalDate.of(2014,3,18); //2014-3-18
LocalDate date2 = date1.plusWeeks(1); //2014-3-25
LocalDate date3 = date2.minusYears(3); //2011-3-25
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); //2011-09-25
日期格式化
java.util.date和java.time.LocalDateTime格式化
应用示例
/**
* 格式化日期
* @param date 待格式化的日期
* @param pattern 格式化正则
* @return 格式化结果串
*/
public static String format(Date date, String pattern){
return new SimpleDateFormat(pattern).format(date);
}
/**
* 格式化日期
* @param localDateTime 待格式化的日期
* @param pattern 格式化正式
* @return 格式化结果串
*/
public static String format(LocalDateTime localDateTime, String pattern){
return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
}
/**
* 格式化日期
* @param localDate 待格式化的日期
* @param pattern 格式化正则, 这里使用的类型 {@link LocalDate}, 所以正则只能设定到天
* @return 格式化结果串
*/
public static String format(LocalDate localDate, String pattern){
return localDate.format(DateTimeFormatter.ofPattern(pattern));
}
示例测试
// 2017-08-28 15:45:02
System.out.println(format(new Date(), "yyyy-MM-dd HH:mm:ss"));
// 2017-08-28 15:45:02
System.out.println(format((LocalDateTime.now()), "yyyy-MM-dd HH:mm:ss"));
// 2017-08-28
System.out.println(format((LocalDateTime.now().toLocalDate()), "yyyy-MM-dd"));
日期转换
java.util.date和java.time.LocalDateTime互相转换
应用示例
/**
* 将 {@link LocalDateTime} 转换成 {@link Date}
* @param localDateTime {@link LocalDateTime} 待转换的日期
* @return 转换成Date结果
*/
public static Date from(LocalDateTime localDateTime){
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
/**
* 将 {@link Date} 转换成 {@link LocalDateTime}
* @param date {@link Date} 待转换的日期
* @return 转换成 {@link LocalDateTime} 结果
*/
public static LocalDateTime from(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
示例测试
String patternTime = "yyyy-MM-dd HH:mm:ss";
Date now = new Date();
// 2017-08-28 14:47:10
System.out.println(format(from(now), patternTime));
// 2017-08-28 14:47:10
System.out.println(format(from(LocalDateTime.now()), patternTime));
日期区间集合
计算两端日期之间内的日期天数集合
示例代码
/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(Date start, Date end){
return dateZones(from(start), from(end));
}
/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<Date> dateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
.collect(Collectors.toList());
}
/**
* 获取{@link LocalDateTime} 在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
// 由于最后要的是字符串,所以map转换一下
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
// 把流收集为List
.collect(Collectors.toList());
}
/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link LocalDate}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(Date start, Date end){
return localDateZones(from(start), from(end));
}
/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.collect(Collectors.toList());
}
/**
* 获取 {@link LocalDateTime} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> e.atStartOfDay().toLocalDate())
// 把流收集为List
.collect(Collectors.toList());
}
示例测试
String patternDate = "yyyy-MM-dd";
List<Date> dateList = Arrays.asList(new Date(2017-1900, 11, 30), new Date(2018-1900, 0, 3));
// 2017-12-30
System.out.println("开始时间:" + format(dateList.get(0), patternDate) + ", 结束时间:" + format(dateList.get(1), patternDate));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(dateZones(dateList.get(0), dateList.get(1)).stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(dateList.get(0), dateList.get(1)).stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);
// 2017-12-30
System.out.println(format(now, patternDate));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(dateZones(now, now.plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(now, now.plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(now.toLocalDate(), now.toLocalDate().plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
日期加减
示例代码
String patternDate = "yyyy-MM-dd HH:mm:ss";
LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);
// 当前时间: 2017-12-30 00:00:00
System.out.println("当前时间: " + format(now, patternDate));
// 30秒前: 2017-12-29 23:59:30
System.out.println("30秒前: " + format(now.plus(-30, ChronoUnit.SECONDS), patternDate));
// 5分钟后: 2017-12-30 00:05:00
System.out.println("5分钟后: " + format(now.plus(5, ChronoUnit.MINUTES), patternDate));
// 2天前: 2017-12-28 00:00:00
System.out.println("2天前: " + format(now.plus(-2, ChronoUnit.DAYS), patternDate));
// 2天后: 2018-01-01 00:00:00
System.out.println("2天后: " + format(now.plus(2, ChronoUnit.DAYS), patternDate));
// 1周后: 2018-01-06 00:00:00
System.out.println("1周后: " + format(now.plusWeeks(1), patternDate));
// 1月前: 2017-11-30 00:00:00
System.out.println("1月前: " + format(now.plus(-1, ChronoUnit.MONTHS), patternDate));
// 1月后: 2018-01-30 00:00:00
System.out.println("1月后: " + format(now.plus(1, ChronoUnit.MONTHS), patternDate));
// 1年后: 2018-12-30 00:00:00
System.out.println("1年后: " + format(now.plus(1, ChronoUnit.YEARS), patternDate));
日期推算
示例代码
String patternDate = "yyyy-MM-dd";
LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);
// 当前时间: 2017-12-30
System.out.println("当前时间: " + format(now, patternDate) + " ,是否闰年: " + now.toLocalDate().isLeapYear());
// 当前月份: 十二月
System.out.println("当前月份: " + Month.from(now).getDisplayName(TextStyle.FULL, Locale.CHINA));
// 当前星期: 星期六
System.out.println("当前星期: " + DayOfWeek.from(now).getDisplayName(TextStyle.FULL, Locale.CHINA));
// 需要注意:java8提供的获取的本周第一天和本周最后一天是西方的界定方式, 第一天是周末, 最后一天是周六, 和中国的不太一样
// 本周初第一天:2017-12-24
System.out.println("本周初第一天: " + format(now.with(WeekFields.of(Locale.CHINA).dayOfWeek(),1L), patternDate));
// 本周最后一天:2017-12-30
System.out.println("本周最后一天: " + format(now.with(WeekFields.of(Locale.CHINA).dayOfWeek(),7L), patternDate));
// 本月初第一天:2017-12-01
System.out.println("本月初第一天: " + format(now.with(TemporalAdjusters.firstDayOfMonth()), patternDate));
// 本月最后一天:2017-12-31
System.out.println("本月最后一天: " + format(now.with(TemporalAdjusters.lastDayOfMonth()), patternDate));
// 本年最后一天:2017-01-01
System.out.println("本年最后一天: " + format(now.with(TemporalAdjusters.firstDayOfYear()), patternDate));
// 本年最后一天:2017-12-31
System.out.println("本年最后一天: " + format(now.with(TemporalAdjusters.lastDayOfYear()), patternDate));
Optional类说明
Optional类的Javadoc描述如下:
这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional基础对象处理
示例代码
// 有则返回, 无则由函数产生
System.out.println(Optional.ofNullable(null).orElseGet(() -> Arrays.asList(1,2,3)));
// 元素存在输出true,反之输出false
System.out.println(Optional.ofNullable("Elon").isPresent());
// 不为空时输出元素,反之输出“默认值”
System.out.println(Optional.ofNullable("Elon").orElse("默认值"));
// 元素存在输出元素,反之抛出异常
System.out.println(Optional.ofNullable("Elon").orElseThrow(IllegalArgumentException::new));
// 元素存在输出true,反之输出false
System.out.println(Optional.ofNullable(null).isPresent());
// 不为空时输出元素,反之输出“默认值”
System.out.println(Optional.ofNullable(null).orElse("默认值"));
// 元素存在输出元素,反之抛出异常
System.out.println(Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new));
示例输出
[1, 2, 3]
true
Elon
Elon
false
默认值
java.lang.IllegalArgumentException
at java.util.Optional.orElseThrow(Optional.java:290)
...
Optional基础集合处理
示例代码
List<Integer> list = Arrays.asList(1, 2, 3, 2, 5, 3);
// 集合不为空的时候进行遍历去重,反之输出空集合
System.out.println(Optional.ofNullable(list).orElse(Lists.newArrayList()).stream().distinct().collect(Collectors.toList()));
// 集合不为空的时候进行遍历去重,反之抛出异常
System.out.println(Optional.ofNullable(list).orElseThrow(IllegalArgumentException::new).stream().distinct().collect(Collectors.toList()));
list = null;
// 集合不为空的时候进行遍历去重,反之输出空集合
System.out.println(Optional.ofNullable(list).orElse(Lists.newArrayList()).stream().distinct().collect(Collectors.toList()));
// 集合不为空的时候进行遍历去重,反之抛出异常 IllegalArgumentException
System.out.println(Optional.ofNullable(list).orElseThrow(IllegalArgumentException::new).stream().distinct().collect(Collectors.toList()));
示例输出
[1, 2, 3, 5]
[1, 2, 3, 5]
[]
java.lang.IllegalArgumentException
at java.util.Optional.orElseThrow(Optional.java:290)
...
Optional复杂集合处理
示例代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Person{
private String name;
private int age;
public String toString(){
return JSON.toJSONString(this);
}
}
@Test
public void list(){
Person person = null;
System.out.println(Optional.ofNullable(person).map(x -> x.getName()).orElse("默认姓名"));
System.out.println(Optional.ofNullable(person).map(x -> x.getAge()).orElse(18));
List<Person> list = Arrays.asList(new Person("p1", 10), new Person("p2", 15));
System.out.println("最大年龄用户:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge))).get());
System.out.println("最大年龄数值:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge))).get().getAge());
System.out.println("年龄均值数值:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.averagingInt(Person::getAge)));
}
示例输出
默认姓名
18
最大年龄用户:{"age":15,"name":"p2"}
最大年龄数值:15
年龄均值数值:12.5
Nashorn JavaScript引擎
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// jdk.nashorn.api.scripting.NashornScriptEngine
System.out.println( engine.getClass().getName() );
// Result:2.0
System.out.println("Result:" + engine.eval("function f() { return 1; }; f() + 1;"));
Base64支持
final String text = "Base64 finally in Java 8!";
final String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
final String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8);
System.out.println(decoded);
Java8新特性示例图
[图片上传失败...(image-672dc3-1576724390193)]