> 文章列表 > Java8新特性

Java8新特性

Java8新特性

1 lambda表达式

//匿名内部类Runnable r=new Runnable() {@Overridepublic void run() {System.out.println("hello");}};
//lambda表达式Runnable a=()->{String name=Thread.currentThread().getName();System.out.println(name+">>hello");};Thread t=new Thread(a);t.start()
//匿名内部类Comparator<String> com= new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {// TODO Auto-generated method stubreturn o2.length()-o1.length();}};List list=new ArrayList();list.add("aaa");list.add("a");list.add("bded");list.add("aa");Collections.sort(list,com);for (Object object : list) {System.out.println(object);}//lambda表达式//⽅式1/*Comparator<String> com=(o1,o2)->{return o1.length()-o2.length();};*///⽅式2//  Comparator<String> com=(o1, o2)->o1.length()-o2.length();

2 使⽤Stream API再优化lambda表达式

public static void main(String[] args) {List<Employee> employees = new ArrayList<>();employees.add(new Employee("xxx", 30, 10000));employees.add(new Employee("yyy", 29, 8000));employees.add(new Employee("zzz", 22, 12000));employees.add(new Employee("张三", 21, 20000));employees.add(new Employee("李四", 32, 22000));// 按照年龄过滤System.out.println("-------------按照年龄过滤--------------");//System.out::println 是对⼀个实例⽅法的引⽤employees.stream().filter((e)->e.getAge()>=25).forEach(System.out::println);// 按照⼯资过滤System.out.println("-------------按照⼯资过滤--------------");employees.stream().filter((e) -> e.getSalary() >= 10000).forEach(e1->System.out.println(e1));}

3 使⽤lambda表达式注意事项

Lambda引⼊了新的操作符: ->(箭头操作符),->将表达式分成两部分
左侧:(参数1,参数2…)表示参数列表;
右侧:{}内部是⽅法体
1、形参列表的数据类型会⾃动推断;
2、如果形参列表为空,只需保留();
3、如果形参只有1个,()可以省略,只需要参数的名称即可;
4、如果执⾏语句只有1句,且⽆返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略
return,且执⾏语句也保证只有1句;
5、lambda不会⽣成⼀个单独的内部类⽂件;
6、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系
统会⾃动添加,此后在修改该局部变量,会报错。
Lambda表达式的语法总结: () -> ();
Java8新特性

4 方法引用

使⽤“::”操作符将⽅法名和对象或类的名字分隔开来。以下是四种使⽤情况:

  • 对象::实例⽅法
  • 类::静态⽅法
  • 类::实例⽅法
  • 类::new
import java.util.Comparator;
import java.util.TreeSet;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
import com.yhp.Employee;
public class Test2 {public static void main(String[] args) {
// - 1对象::实例⽅法//lambda表达式中的⽅法体中只调⽤了⼀个⽅法,⽽且这个⽅法的参数和//返回值类型和lambda表达表示的抽象⽅法的参数和返回值类型相同//Consumer中的⽅法:void accept(T t)//Consumer<String> con=s->System.out.println(s);//等价于Consumer<String> con=System.out::println;con.accept("我爱编程");con.accept("我爱java");List<String> names=new ArrayList<>();names.add("xxxx");names.add("yyyy");names.add("zzzz");names.add("abc");//names.forEach(s->System.out.println(s));names.forEach(System.out::println);// - 2类::静态⽅法//lambda表达式中的⽅法体中只调⽤了⼀个⽅法,⽽且这个⽅法的参数和//返回值类型和lambda表达表示的抽象⽅法的参数和返回值类型相同//Comparator中的⽅法: int compare(T o1, T o2);Comparator<Integer> comparator=(o1,o2)->Integer.compare(o1, o2);//等价于Comparator<Integer> comparator2=Integer::compare;TreeSet<Integer> treeSet=new TreeSet<>(comparator);// - 3类::实例⽅法//1 lambda表达式中的⽅法体中只调⽤了⼀个⽅法//2 第⼀个参数作为⽅法的调⽤者,第⼆个参数作为⽅法的参数//BiPredicate中的⽅法: boolean test(T t, U u);BiPredicate<String, String> pre=(s1,s2)->s1.equals(s2);//等价于BiPredicate<String, String> pre2=String::equals;boolean b=pre2.test("haha","haha");System.out.println(b);// - 4类::new//lambda表达式的⽅法体内只有对象的创建,//并且类型和lambda表达式表示的抽象⽅法的返回值类型相同//Supplier中的⽅法:T get();Supplier<Employee> sup=()->new Employee();//等价于//思考问题?调⽤是什么构造⽅法(⽆参构造⽅法)Supplier<Employee> sup2=Employee::new;Employee employee = sup2.get();System.out.println(employee.toString());//4.1 调⽤指定的构造⽅法 (带参构造 String int)-需要⾃定义接⼝MySupplier<Employee> sup3=Employee::new;Employee employee2=sup3.get("李四", 30);System.out.println(employee2.toString());//5数组引⽤MyArray<String> myArray=(n)->new String[n];//等价于MyArray<String> myArray2=String[]::new;String[] strings = myArray2.get(10);System.out.println(strings.length);}
}public interface MyArray<T> {T[] get(int count);
}public interface MySupplier<T> {T get(String name,int age); //参数列表的类型要和构造⽅法的类型相同
}

5 Stream API

Stream是Java8中处理数组、集合的抽象概念,它可以指定你希望对集合进⾏的操作,可以执⾏⾮常复杂的查找、过滤和映射数据等操作。使⽤Stream API对集合数据进⾏操作,就类似于使⽤SQL执⾏的数据库查询。也可以使⽤Stream API来并⾏执⾏操作。
简单应⽤:统计⼀个字符串类型集合中,所有⻓度⼤于3的元素个数。

 //传统实现List<String> data=new ArrayList<>();data.add("hello");data.add("world");data.add("ni");data.add("apple");data.add("china");int count = 0;for (String s : data) {if (s.length() > 3)count++;}System.out.println(count);//Stream APIlong count2= data.stream().filter(s->s.length()>3).count();System.out.println(count2);}

上⾯代码中stream⽅法会为字符串列表⽣成⼀个Stream。filter⽅法会返回只包含字符串⻓度⼤于3的⼀个Stream,然后通过count⽅法计数。
Stream ⾃⼰不会存储元素。
Stream 不会改变源对象。相反,他们会返回⼀个持有结果的新Stream。
Stream 操作是延迟执⾏的。这意味着他们会等到需要结果的时候才执⾏。
Stream遵循“做什么,⽽不是怎么去做”的原则。只需要描述需要做什么,⽽不⽤考虑程序是怎样实现的。

6 如何使⽤Stream API

使⽤Stream,会有三个阶段(步骤):

  1. 创建⼀个Stream。 (创建)
  2. 在⼀个或多个步骤中,将初始Stream转化到另⼀个Stream的中间操作。 (中间操作)
  3. 使⽤⼀个终⽌操作来产⽣⼀个结果。该操作会强制他之前的延迟操作⽴即执⾏。在这之后,该Stream就不会在被使⽤了。(终⽌操作)

6.1 Stream的创建⽅法

 public static void main (String[]args){//1 Stream.of⽅法Stream<String> stream = Stream.of("xxx", "abc", "hello", "world", "beijing");stream.filter(s -> s.length() > 3).forEach(System.out::println);//2 Arrays⼯具类String[] country = {"中国", "美国", "韩国", "泰国"};Stream<String> stream2 = Arrays.stream(country);stream2.forEach(System.out::println);//3使⽤集合的⽅法stream⽅法//parallelStream提供了流的并⾏处理,它是Stream的另⼀重要特性,其底层使⽤Fork/Join框架实现。简单理解就是多线程异步任务的⼀种实现。List<String> list = new ArrayList<>();list.add("apple");list.add("orange");list.add("banana");list.add("xigua");list.parallelStream()//并⾏操作.filter(s -> s.startsWith("a")).forEach(System.out::println);//4创建⽆限流 Stream的⽅法//迭代Stream<Integer> iterate = Stream.iterate(0, n -> n + 2);iterate.limit(10).forEach(System.out::println);//⽣成Stream<Double> generate = Stream.generate(() -> new Random().nextDouble());generate.limit(5).forEach(System.out::println);}

6.2 Stream中间操作

中间操作包括:map (mapToInt, flatMap 等)、 filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered。

多个中间操作可以连接起来形成⼀个流⽔线,除⾮流⽔ 线上触发终⽌操作,否则中间操作不会执⾏任何的处理! ⽽在终⽌操作时⼀次性全部处理,称为“惰性求值”。

     public static void main (String[]args){List<Employee> employees = new ArrayList<>();employees.add(new Employee("xxx", 30, 10000));employees.add(new Employee("yyy", 29, 8000));employees.add(new Employee("zzz", 22, 12000));employees.add(new Employee("张三", 21, 20000));employees.add(new Employee("李四", 32, 22000));employees.add(new Employee("李四", 32, 22000));//1.过滤 filter()//employees.stream().filter((e)-
>e.getAge() > 25).forEach(System.out::println);//2.限定数据 limit()- 分⻚//employees.stream().limit(5).forEach(System.out::println);//3.跳过 skip()//employees.stream().skip(2).forEach(System.out::println);//4.去重 distinct()List list = new ArrayList();list.add("aaa1");list.add("aaa1");list.add("aaa2");//list.stream().distinct().forEach(System.out::println);//5.映射(获取指定部分的数据,也可以对这些数据进⾏操作) -map(Function -> Rapply(T))//employees.stream().map((e)->e.getName()).forEach(System.out::println);//employees.stream().map((e)-
>e.getName().toUpperCase()).forEach(System.out::println);//6.排序 sorted() 对字⺟和数字都可以排序List list2 = new ArrayList();list2.add(111);list2.add(4223);list2.add(2);list2.add(23);list2.add(3);//list2.stream().sorted().forEach(System.out::println);//⽐较对象employees.stream().sorted((o1, o2) -> o1.getAge() -o2.getAge()).forEach(System.out::println);}

6.3 Stream 的终⽌操作

终⽌操作包括:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。

遍历
forEach
查找和匹配
allMatch——检查是否匹配所有元素
anyMatch——检查是否⾄少匹配⼀个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第⼀个元素
findAny——返回当前流中的任意元素
max——返回流中最⼤值
min——返回流中最⼩值

public static void main(String[] args) {List<Employee> employees=new ArrayList<>();employees.add(new Employee("xxx", 30, 10000));employees.add(new Employee("yyy", 29, 8000));employees.add(new Employee("zzz", 22, 12000));employees.add(new Employee("张三", 21, 20000));employees.add(new Employee("李四", 30, 22000));employees.add(new Employee("李四", 33, 22000));//1.全部匹配//boolean b=employees.stream().allMatch((e)->e.getAge()==32);//2.⾄少匹配⼀个//boolean b=employees.stream().anyMatch((e)->e.getAge()==32);//3.检查都不匹配的//boolean b=employees.stream().noneMatch((e)->e.getAge()==32);//System.out.println(b);//4.返回第⼀个元素Optional<Employee> e1=employees.stream().findFirst();System.out.println(e1.get());//5.返回总条数long count=employees.stream().count();System.out.println(count);//6.最⼤值 最⾼的⼯资Optional<Employee> e4=employees.stream().max((e2,e3)->(int)(e2.getSalary()-e3.getSalary()));System.out.println(e4.get().getSalary());//7.最⼩值Optional<Employee> e5=employees.stream().min((e2,e3)->(int)(e2.getSalary()-e3.getSalary()));System.out.println(e5.get().getSalary());//8.终⽌操作//employees.forEach(System.out::println);//9.归约(统计操作)-计算总的⼯资Optional<Double> e6=employees.stream().map(Employee::getSalary).reduce((x1,y1)->x1+y1);System.out.println(e6.get());//10.collect() 收集List<String> result=employees.stream().filter((e)->e.getAge()>25).map(Employee::getName).distinct().collect(Collectors.toList());for (String string : result) {System.out.println(string);}}

6.4 并⾏操作

Stream有串⾏和并⾏两种,串⾏Stream上的操作是在⼀个线程中依次完成,⽽并⾏Stream则是在多个线程上同时执⾏。
计算⼀下排序这个Stream要耗时多久:

public static void main(String[] args) {int max = 1000000;List<String> values = new ArrayList<>(max);for (int i = 0; i < max; i++) {UUID uuid = UUID.randomUUID();values.add(uuid.toString());}System.out.println("----------串⾏------------");long t0 = System.currentTimeMillis();long count = values.stream().sorted().count();System.out.println(count);long t1 = System.currentTimeMillis();long millis = t1-t0;System.out.println(millis);// System.out.println("-------------并⾏----------------");
// long t0 = System.currentTimeMillis();
// parallelStream()是Collection接⼝的⽅法 
// long count = values.parallelStream().sorted().count();
// System.out.println(count);
// long t1 = System.currentTimeMillis();
// long millis = t1-t0;
// System.out.println(millis);}

7 新时间⽇期API

Java 8通过发布新的Date-Time API (JSR 310)来进⼀步加强对⽇期与时间的处理。
在旧版的 Java 中,⽇期时间 API 存在诸多问题,其中有:

  • ⾮线程安全 − java.util.Date 是⾮线程安全的,所有的⽇期类都是可变的,这是Java⽇期类最⼤的问题之⼀。
  • 设计很差 − Java的⽇期/时间类的定义并不⼀致,在java.util和java.sql的包中都有⽇期类,此外⽤于格式化和解析的类在java.text包中定义。java.util.Date同时包含⽇期和时间,⽽java.sql.Date仅包含⽇期,将其纳⼊java.sql包并不合理。另外这两个类都有相同的名字,这本身就是⼀个⾮常糟糕的设计。
  • 时区处理麻烦 − ⽇期类并不提供国际化,没有时区⽀持,因此Java引⼊了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
    Java 8 在 java.time 包下提供了很多新的 API。以下为两个⽐较重要的 API:
  • Local(本地) − 简化了⽇期时间的处理,没有时区的问题。
  • ZoneId (时区) − 通过定制的时区处理⽇期时间。

线程安全问题演示:

   public static void main(String[] args) throws Exception{SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");Callable<Date> task = new Callable<Date>() {@Overridepublic Date call() throws Exception {return sdf.parse("20201121");}};ExecutorService pool = Executors.newFixedThreadPool(10);List<Future<Date>> results = new ArrayList<>();for (int i = 0; i < 10; i++) {results.add(pool.submit(task));}for (Future<Date> future : results) {System.out.println(future.get());}pool.shutdown();}

Java8新特性
使⽤新时间⽇期API解决

   public static void main(String[] args) throws Exception{DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyyMMdd");Callable<LocalDate> task = new Callable<LocalDate>() {@Overridepublic LocalDate call() throws Exception {return LocalDate.parse("20201002", formatter);}};ExecutorService pool = Executors.newFixedThreadPool(10);List<Future<LocalDate>> results = new ArrayList<>();for (int i = 0; i < 10; i++) {results.add(pool.submit(task));}for (Future<LocalDate> future : results) {System.out.println(future.get());}pool.shutdown();}

8 本地化⽇期时间 API

LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使⽤ ISO-8601⽇ 历系统的⽇期、时间、⽇期和时间。它们提供了简单的⽇期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

 public static void main(String[] args) {//当前时间LocalDateTime ldt=LocalDateTime.now();System.out.println(ldt);//其他时间LocalDateTime ldt2=LocalDateTime.of(2020, 10, 1, 10, 10,10);System.out.println(ldt2);//加时间LocalDateTime ldt3=ldt2.plusDays(2);System.out.println(ldt3);//减时间LocalDateTime ldt4 = ldt3.minusHours(2);System.out.println(ldt4);//获取时间部分System.out.println(ldt.getYear());System.out.println(ldt.getMonthValue());System.out.println(ldt.getDayOfMonth());System.out.println(ldt.getHour());System.out.println(ldt.getMinute());System.out.println(ldt.getSecond());}

9 Instant、ZoneId

Instant 时间戳
它是以Unix元年(传统 的设定为UTC时区1970年1⽉1⽇午夜时分)开始 所经历的描述进⾏运算
ZoneId 时区

 public static void main(String[] args) throws Exception {//时间戳Instant instant=Instant.now();System.out.println(instant);Thread.sleep(1000);Instant instant2=Instant.now();long millis = Duration.between(instant, instant2).toMillis();System.out.println(millis);//时区System.out.println(ZoneId.getAvailableZoneIds());ZoneId zone1 = ZoneId.of("Europe/Berlin");ZoneId zone2 = ZoneId.of("Brazil/East");System.out.println(zone1.getRules());System.out.println(zone2.getRules());}

10 时间矫正器 TemporalAdjuster

TemporalAdjuster : 时间校正器。有时我们可能需要获 取例如:将⽇期调整到“下个周⽇”等操作。
TemporalAdjusters : 该类通过静态⽅法提供了⼤量的常 ⽤ TemporalAdjuster 的实现。
例如获取下个周⽇

LocalDate date=LocalDate.now();
//下周5
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
//下周2
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));
//下周⽇(周⽇为每周的第⼀天)
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)));

11 DateTimeFormatter

java.time.format.DateTimeFormatter 类:该类提供了三种 格式化⽅法:
预定义的标准格式
语⾔环境相关的格式
⾃定义的格式

public static void main(String[] args) throws InterruptedException,ExecutionException {//获得时间LocalDate date=LocalDate.now();System.out.println(date);LocalTime time=LocalTime.now();System.out.println(time);LocalDateTime datetime=LocalDateTime.now();System.out.println(datetime);//格式化DateTimeFormatter format=DateTimeFormatter.ofPattern("yyyy年MM⽉dd⽇HH:mm:ss E");//Date->StringString str=format.format(datetime);System.out.println(str);//String->DateString date2="2019年08⽉22⽇ 14:01:46 星期四";LocalDateTime da=LocalDateTime.parse(date2,format);System.out.println(da);}

12 接⼝中的默认⽅法和静态⽅法

在接⼝中可以使⽤default和static关键字来修饰接⼝中定义的普通⽅法

public interface Interface {default String getName(){return "zhangsan";}static String getName2(){return "zhangsan";}
}

在JDK1.8中很多接⼝会新增⽅法,为了保证1.8向下兼容,1.7版本中的接⼝实现类不⽤每个都重新实现新添加的接⼝⽅法,引⼊了default默认实现,static的⽤法是直接⽤接⼝名去调⽅法即可。当⼀个类继承⽗类⼜实现接⼝时,若后两者⽅法名相同,则优先继承⽗类中的同名⽅法,即“类优先”,如果实现两个同名⽅法的接⼝,则要求实现类必须⼿动声明默认实现哪个接⼝中的⽅法。

13 对HashMap的优化

在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化 。原来的hashMap采⽤的数据结构是哈希表(数组+链表),hashMap默认⼤⼩是16,⼀个0-15索引的数组,如何往⾥⾯存储元素,⾸先调⽤元素的hashcode ⽅法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么⽐较它们的equals⽅法⽐较内容 如果内容⼀样,后⼀个value会将前⼀个value的值覆盖,如果不⼀样,在1.7的时候,后加的放在前⾯,形成⼀个链表,形成了碰撞,在某些情况下如果链表⽆限下去,那么效率极低,碰撞是避免不了的

加载因⼦:0.75,数组扩容,达到总容量的75%,就进⾏扩容,但是⽆法避免碰撞的情况发⽣在1.8之后,在数组+链表+红⿊树来实现hashmap,当碰撞的元素个数⼤于8时 & 总容量⼤于64,会有红⿊树的引⼊ 。除了添加之后,效率都⽐链表⾼。JDK1.7及之前的 ConcurrentHashMap 使⽤ 锁分段
机制 实现,JDK1.8则使⽤ 数组+链表+红⿊树数据结构 和 CAS原⼦操作实现。

jdk 7 与 jdk 8 中关于HashMap的对⽐
8时红⿊树+链表+数组的形式,当桶内元素⼤于8时,便会树化
hash值的计算⽅式不同
1.7 table在创建hashmap时分配空间,⽽1.8在put的时候分配,如果table为空,则为table分配空间。
在发⽣冲突,插⼊链中时,7是头插法,8是尾插法。
在resize操作中,7需要重新进⾏index的计算,⽽8不需要,通过判断相应的位是0还是1,要么依旧是原index,要么是oldCap + 原index

14 Optional 类

Optional 类是⼀个可以为null的容器对象。如果值存在则isPresent()⽅法会返回true,调⽤get()⽅法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有⽤的⽅法,这样我们就不⽤显式进⾏空值检测。
Optional 类的引⼊很好的解决空指针异常。
类中的⽅法
Java8新特性
注意: 这些⽅法是从 java.lang.Object 类继承来的。
示例:

import java.util.Optional;public class Test {public static void main(String args[]){Test test = new Test();Integer value1 = null;Integer value2 = new Integer(10);// Optional.ofNullable - 允许传递为 null 参数Optional<Integer> a = Optional.ofNullable(value1);// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerExceptionOptional<Integer> b = Optional.of(value2);System.out.println(test.sum(a,b));}public Integer sum(Optional<Integer> a, Optional<Integer> b){// Optional.isPresent - 判断值是否存在System.out.println("第⼀个参数值存在: " + a.isPresent());System.out.println("第⼆个参数值存在: " + b.isPresent());// Optional.orElse - 如果值存在,返回它,否则返回默认值Integer value1 = a.orElse(new Integer(0));//Optional.get - 获取值,值需要存在Integer value2 = b.get();return value1 + value2;}}

运⾏结果:
第⼀个参数值存在: false
第⼆个参数值存在: true
10

15 Base64

在Java 8中,Base64编码已经成为Java类库的标准。
Java 8 内置了 Base64 编码的编码器和解码器。
Base64⼯具类提供了⼀套静态⽅法获取下⾯三种BASE64编解码器

基本:输出被映射到⼀组字符A-Za-z0-9+/,编码不添加任何⾏标,输出的解码仅⽀持A-Za-z0-9+/。
URL:输出映射到⼀组字符A-Za-z0-9+_,输出是URL和⽂件。
MIME:输出隐射到MIME友好格式。输出每⾏不超过76字符,并且使⽤’\\r’并跟随’\\n’作为分割。编码输出最后没有⾏分割。

内嵌类
Java8新特性
⽅法
Java8新特性
注意:Base64 类的很多⽅法从 java.lang.Object 类继承。
示例:

  import java.util.Base64;
import java.util.UUID;
import java.io.UnsupportedEncodingException;public class Test {public static void main(String args[]){try {// 使⽤基本编码String base64encodedString =Base64.getEncoder().encodeToString("yhp".getBytes("utf-8"));System.out.println("Base64 编码字符串 (基本) :" + base64encodedString);// 解码byte[] base64decodedBytes =Base64.getDecoder().decode(base64encodedString);System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));//url编码base64encodedString =Base64.getUrlEncoder().encodeToString("http://www.baidu.com".getBytes("utf-8"));System.out.println("Base64 编码字符串 (URL) :" + base64encodedString);StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < 10; ++i) {stringBuilder.append(UUID.randomUUID().toString());}//Mime编码byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");String mimeEncodedString =Base64.getMimeEncoder().encodeToString(mimeBytes);System.out.println("Base64 编码字符串 (MIME) :" + mimeEncodedString);}catch(UnsupportedEncodingException e){System.out.println("Error :" + e.getMessage());}}}