Java8新特性 (jdk1.8)

news2024/11/15 17:38:50

目录

一、Lamdba表达式?

二、函数式接口

三、方法引用和构造引用 

四、Stream API流

五、接口中的新增 默认方法和静态方法 

六、新时间日期API 

七、Optional 

八、其他特性  

一、Lamdba表达式?

为什么使用Lambda表达式?

  • Lambda 是一个 匿名函数 ,我们可以把Lambda表达式理解为是 一段可以传递的代码 (将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda表达式 在 Java 8 语言中引入 的 一 种 新的语法元素和操作符。

这个操作符为 “ -> ” 该操作符被称为 Lambda操作符 或 箭头操作符 。

它将 Lambda 分为两个部分:

  • 左侧:

         指定了 Lambda 表达式需要的 参数列表

  • 右侧:

        指定了 Lambda体 是抽象方法的实现逻辑,也 即Lambda 表达式要执行 的功能 。

package com.cs.java8;

import org.junit.Test;

import java.util.Comparator;

public class LambdaTest {
    @Test
    public void test1(){
        //普通写法:
        Runnable r1 = new Runnable() {
            @Override
            public void run(){
                System.out.println("我爱北京");
            }
        };
        r1.run();
        System.out.println("============================");
        //Lambda表达式写法:
        Runnable r2 = () -> System.out.println("华东师范");
        r2.run();
    }

    @Test
    public void test2(){
        //1.普通写法
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        System.out.println(com1.compare(67, 8));
        System.out.println("-----------------------------------------------");
        //2.Lambda表达式写法:
        Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
        System.out.println(com2.compare(4 , 7));
        System.out.println("-----------------------------------------------");
        //3.方法引用的写法:
        Comparator<Integer> com3 = Integer :: compare;
        System.out.println(com3.compare(55 ,55));
     }
}

 ② Lambda表达式的6种语法格式

 2.1 语法格式一: 无参、无返回值

//普通写法:
Runnable r1 = new Runnable() {
    @Override
    public void run(){
        System.out.println("我爱北京");
    }
};
-------------------------------------------
//Lambda表达式写法:
Runnable r2 = () -> {System.out.println("华东师范");};

 2.2 语法格式二: Lambda 需要一个参数, 但是没有返回值。

//普通写法:
Consumer<String> consumer1 = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
};
-----------------------------------------
//Lambda表达式写法:
Consumer<String> consumer2 = (String s) -> {System.out.println(s);};

 2.3 语法格式三: 数据类型可以省略

因为可由编译器推断得出,称为“类型推断“

//普通写法:
Consumer<String> consumer1 = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
};
-----------------------------------------
//Lambda表达式写法:
Consumer<String> consumer2 = (String s) -> {System.out.println(s);};
//数据类型省略写法:
Consumer<String> consumer2 = (s) -> {System.out.println(s);};

2.4 语法格式四: Lambda 若只需要一个参数时, 参数的小括号可以省略 

//普通写法:
Consumer<String> consumer1 = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
};
-----------------------------------------
//Lambda表达式写法:
Consumer<String> consumer2 = (String s) -> {System.out.println(s);};
//数据类型省略写法:
Consumer<String> consumer3 = (s) -> {System.out.println(s);};
//参数小括号省略写法:
Consumer<String> consumer3 = s -> {System.out.println(s);};

 2.5 语法格式五: Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值

//普通写法
Comparator<Integer> comparator1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        System.out.println("实现函数式接口方法");
        return Integer.compare(o1, o2);
    }
};
----------------------
//Lambda表达式写法:
Comparator<Integer> comparator2 = (o1, o2) -> {
    System.out.println("实现函数式接口方法");
    return Integer.compare(o1, o2);
};

 2.6 语法格式六: 当 Lambda 体只有 一条 语句时, return 与大括号 若有,都可以省略

//普通写法
Comparator<Integer> comparator1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1, o2);
    }
};
----------------------
//Lambda表达式写法:
Comparator<Integer> comparator2 = (o1, o2) -> Integer.compare(o1, o2);

二、函数式接口

  • 函数式接口的由来

        Lambda 表达式的 前提是需要有函数式接口 ,而 Lambda 表达式使用时不关心接口名, 抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用 Lambda 表达式更加的方便 ,在 JDK 中提供了大量常用的函数式接口,这样就 可以省去自己去定义函数式接口 。

public class demo {
    public static void main(String[] args) {
        fun1((arr)->{
            int sum = 0 ;
            for (int i : arr) {
                sum += i;
            }
            return sum;
        });
    }
    public static void fun1(Operator operator){
        int[] arr = {1,2,3,4};
        int sum = operator.getSum(arr);
        System.out.println("sum = " + sum);
    }
}
/**
 * 函数式接口
 */
@FunctionalInterface
interface Operator{
    int getSum(int[] arr);
}
  • 常用函数式接口

            在JDK中帮我们提供的有函数式接口,主要是在 java.util.function 包中。

             Supplier(供给型或生产型接口):无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。

             Consumer(消费型接口):该接口中的方法可以接收一个参数,接收的参数类型由泛型指定,对参数的操作 方式根据该接口的实现类决定,不需要返回值。

             扩展方法 --> andThen 。如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,先通过调用者对象处理参数,将处理的结果再通过f对象处理,将两个处理的结果进行返回。

             Function(函数型接口):有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

            扩展方法:同样具有andThen,另外compose方法的作用顺序和andThen方法刚好相反而静态方法identity则是,输入什么参数就返回什么参数(参考源码)

             Predicate(断言型接口):有参且返回值为Boolean的接口。

              扩展方法:

                        and(Predicate p):先将参数通过调用者判断真假,再将参数通过p判断真假,全真为真,否则为假

                        or(Predicate p):全假为假,否则为真

                        negate():取反

三、方法引用和构造引用

符号表示: ::
        符号说明:双冒号为方法引用运算符,而它所在的表达式被称为 方法引用
        应用场景:如果Lambda 表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方 法引用。
        常见的引用方式:
                方法引用在JDK8中使用是相当灵活的,有以下几种形式:
                        1. instanceName :: methodName 对象 :: 方法名
                        2. ClassName::staticMethodName 类名 :: 静态方法
                        3. ClassName :: methodName 类名 :: 普通方法
                        4. ClassName :: new 类名 :: new 调用的构造器
                        5. TypeName[] :: new String[] :: new 调用数组的构造器

四、Stream API流 

        java8提供的stream api可以让程序员像操作数据库一样操作集合,通过各种条件对集合进行一次性的过滤,省去对集合的反复过滤存储塞选的过程。

1.Stream的特点:

  • 元素是特定类型的对象,形成一个队列。
  • java中的Stream并不会存储元素,而是按需计算。
  • 数据来源可以是集合,数组,I/O channel,产生器generator和IntStream等
  • 聚合操作类似sql语句一样的操作,比如filter、map、reduce、find、match、sorted等。

2.Stream 流的获取方式:  

  • 根据Collection获取
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.stream();
            Set<String> set = new HashSet<>();
            set.stream();
            Vector vector = new Vector();
            vector.stream();
        }
  • 通过Stream的of方法
        public static void main(String[] args) {
            Stream<String> a1 = Stream.of("a1", "a2", "a3");
            String[] arr1 = {"aa","bb","cc"};
            Stream<String> arr11 = Stream.of(arr1);
            Integer[] arr2 = {1,2,3,4};
            Stream<Integer> arr21 = Stream.of(arr2);
            arr21.forEach(System.out::println);
            // 注意:基本数据类型的数组是不行的
            int[] arr3 = {1,2,3,4};
            Stream.of(arr3).forEach(System.out::println);
        }

3.Stream 常用方法:
        终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。
        非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。

4.Stream注意事项:

  • Stream只能操作一次
  • Stream方法返回的是新的流
  • Stream不调用终结方法,中间的操作不会执行

5.parallelStream 并行流:
        通过默认的ForkJoinPool,提高多线程任务的速度。
 获取方式:

  • 通过List接口中的parallelStream方法来获取
  • 通过已有的串行流转换为并行流(parallel)
    public void test02(){
        List<Integer> list = new ArrayList<>();
        // 通过List 接口 直接获取并行流
        Stream<Integer> integerStream = list.parallelStream();
        // 将已有的串行流转换为并行流
        Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
    }
    /**
     * 并行流操作
     */
    @Test
    public void test03(){
        Stream.of(1,4,2,6,1,5,9)
                .parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
                .filter(s->{
                    System.out.println(Thread.currentThread() + " s=" +s);
                    return s > 2;
                }).count();
    }

在多线程的处理下,数据安全问题处理:

  1. 加同步锁
  2. 使用线程安全的容器
  3.  通过Stream中的toArray/collect操作
 /**
     * 使用线程安全的容器
     */
    @Test
    public void test03(){
        Vector v = new Vector();
        Object obj = new Object();
        IntStream.rangeClosed(1,1000)
                .parallel()
                .forEach(i->{
                    synchronized (obj){
                        v.add(i);
                    }
                });
        System.out.println(v.size());
    }
    /**
     * 将线程不安全的容器转换为线程安全的容器
     */
    @Test
    public void test04(){
        List<Integer> listNew = new ArrayList<>();
        // 将线程不安全的容器包装为线程安全的容器
        List<Integer> synchronizedList = Collections.synchronizedList(listNew);
        Object obj = new Object();
        IntStream.rangeClosed(1,1000)
                .parallel()
                .forEach(i->{
                    synchronizedList.add(i);
                });
        System.out.println(synchronizedList.size());
    }
    /**
     * 我们还可以通过Stream中的 toArray方法或者 collect方法来操作
     * 就是满足线程安全的要求
     */
    @Test
    public void test05(){
        List<Integer> listNew = new ArrayList<>();
        Object obj = new Object();
        List<Integer> list = IntStream.rangeClosed(1, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(list.size());
    }

五、接口中的新增 默认方法和静态方法

  •  默认方法:

        当类A、B、C、D都实现了接口xxInterface,此时在接口xxInterface中新增一个方法,那么A、B、C、D都必须实现这个方法,但是想象一下,如果有数百个类实现了一个接口,那么几乎不可能更改所有这些类中的代码。这就是为什么在Java8中,我们有了一个新概念“默认方法”。这些方法可以添加到任何现有接口中,我们不需要强制在实现类中实现这些方法,因此我们可以在不破坏代码的情况下将这些默认方法添加到现有接口中。我们只需要在新增的方法中使用default即可。

  语法规则:

interface 接口名{
          修饰符 default 返回值类型 方法名{
                    方法体;
          }
  }

 使用方式:

        1. 实现类直接调用接口的默认方法
        2. 实现类重写接口的默认方法 

  • 静态方法

       作用也是为了接口的扩展。

        语法规则:

 interface 接口名{
          修饰符 static 返回值类型 方法名{
                    方法体;
          }
  }

使用方式:

                接口中的静态方法在实现类中是不能被重写的,调用的话只能通过接口类型来实现: 接口名.静态方法名();

默认方法和静态方法两者的区别介绍
                1. 默认方法通过实例调用,静态方法通过接口名调用
                2. 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
                3. 静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用

六、新时间日期API 

1. 旧版日期时间的问题

在旧版本中 JDK 对于日期和时间这块的时间是非常差的。

    /**
     * 旧版日期时间设计的问题
     */
    @Test
    public void test01() throws Exception{
        // 1.设计不合理
        Date date = new Date(2021,05,05);
        System.out.println(date);
        // 2.时间格式化和解析操作是线程不安全的
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                // System.out.println(sdf.format(date));
                try {
                    System.out.println(sdf.parse("2021-05-06"));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
  1. 设计不合理,在java.util和java.sql的包中都有日期类,java.util.Date同时包含日期和时间的,而 java.sql.Date仅仅包含日期,此外用于格式化和解析的类在java.text包下。
  2. 非线程安全,java.util.Date是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一。
  3. 时区处理麻烦,日期类并不提供国际化,没有时区支持。

2. 新日期时间 API 介绍
JDK 8 中增加了一套全新的日期时间 API ,这套 API 设计合理,是线程安全的。新的日期及时间 API 位于 java.time 包 中,下面是一些关键类。

  • LocalDate :表示日期,包含年月日,格式为 2019-10-16
  • LocalTime :表示时间,包含时分秒,格式为 16:38:54.158549300
  • LocalDateTime :表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
  • DateTimeFormatter :日期时间格式化类。
  • Instant:时间戳,表示一个特定的时间瞬间。
  • Duration:用于计算2个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZonedDateTime :包含时区的时间

Java 中使用的历法是 ISO 8601 日历系统,它是世界民用历法,也就是我们所说的公历。平年有 365 天, 闰年是366 天。此外Java 8 还提供了 4 套其他历法,分别是:

  • ThaiBuddhistDate:泰国佛教历
  • MinguoDate:中华民国历
  • JapaneseDate:日本历
  • HijrahDate:伊斯兰历

2.1 日期时间的常见操作

        LocalDate, LocalTime 以及 LocalDateTime 的操作。

/**
     * JDK8 日期时间操作
     */
    @Test
    public void test01(){
        // 1.创建指定的日期
        LocalDate date1 = LocalDate.of(2021, 05, 06);
        System.out.println("date1 = "+date1);
        // 2.得到当前的日期
        LocalDate now = LocalDate.now();
        System.out.println("now = "+now);
        // 3.根据LocalDate对象获取对应的日期信息
        System.out.println("年:" + now.getYear());
        System.out.println("月:" + now.getMonth().getValue());
        System.out.println("日:" + now.getDayOfMonth());
        System.out.println("星期:" + now.getDayOfWeek().getValue());
    }
    /**
     * 时间操作
     */
    @Test
    public void test02(){
        // 1.得到指定的时间
        LocalTime time = LocalTime.of(5,26,33,23145);
        System.out.println(time);
        // 2.获取当前的时间
        LocalTime now = LocalTime.now();
        System.out.println(now);
        // 3.获取时间信息
        System.out.println(now.getHour());
        System.out.println(now.getMinute());
        System.out.println(now.getSecond());
        System.out.println(now.getNano());
    }
    /**
     * 日期时间类型 LocalDateTime
     */
    @Test
    public void test03(){
        // 获取指定的日期时间
        LocalDateTime dateTime =
                LocalDateTime.of(2020
                        , 06
                        , 01
                        , 12
                        , 12
                        , 33, 213);
        System.out.println(dateTime);
        // 获取当前的日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        // 获取日期时间信息
        System.out.println(now.getYear());
        System.out.println(now.getMonth().getValue());
        System.out.println(now.getDayOfMonth());
        System.out.println(now.getDayOfWeek().getValue());
        System.out.println(now.getHour());
        System.out.println(now.getMinute());
        System.out.println(now.getSecond());
        System.out.println(now.getNano());
    }

2.2 日期时间的修改和比较

/**
     * 日期时间的修改
     */
    @Test
    public void test01(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = "+now);
        // 修改日期时间 对日期时间的修改,对已存在的LocalDate对象,创建了它模板
        // 并不会修改原来的信息
        LocalDateTime localDateTime = now.withYear(1998);
        System.out.println("now :"+now);
        System.out.println("修改后的:" + localDateTime);
        System.out.println("月份:" + now.withMonth(10));
        System.out.println("天:" + now.withDayOfMonth(6));
        System.out.println("小时:" + now.withHour(8));
        System.out.println("分钟:" + now.withMinute(15));
        // 在当前日期时间的基础上 加上或者减去指定的时间
        System.out.println("两天后:" + now.plusDays(2));
        System.out.println("10年后:"+now.plusYears(10));
        System.out.println("6个月后 = " + now.plusMonths(6));
        System.out.println("10年前 = " + now.minusYears(10));
        System.out.println("半年前 = " + now.minusMonths(6));
        System.out.println("一周前 = " + now.minusDays(7));
    }
    /**
     * 日期时间的比较
     */
    @Test
    public void test02(){
        LocalDate now = LocalDate.now();
        LocalDate date = LocalDate.of(2020, 1, 3);
        // 在JDK8中要实现 日期的比较 isAfter isBefore isEqual 通过这几个方法来直接比较
        System.out.println(now.isAfter(date)); // true
        System.out.println(now.isBefore(date)); // false
        System.out.println(now.isEqual(date)); // false
    }

        注意:在进行日期时间修改的时候,原来的LocalDate对象是不会被修改,每次操作都是返回了一个新的 LocalDate对象,所以在多线程场景下是数据安全的。
2.3 格式化和解析操作

在 JDK8 中我们可以通过 java.time.format.DateTimeFormatter 类可以进行日期的解析和格式化操作

    /**
     * 日期格式化
     */
    @Test
    public void test01(){
        LocalDateTime now = LocalDateTime.now();
        // 指定格式 使用系统默认的格式 2021-05-27T16:16:38.139
        DateTimeFormatter isoLocalDateTime =
                DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        // 将日期时间转换为字符串
        String format = now.format(isoLocalDateTime);
        System.out.println("format = " + format);
        // 通过 ofPattern 方法来指定特定的格式
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM-dd HH:mm:ss");
        String format1 = now.format(dateTimeFormatter);
        // 2021-05-27 16:16:38
        System.out.println("format1 = " + format1);
        // 将字符串解析为一个 日期时间类型
        LocalDateTime parse = LocalDateTime.parse("1997-05-06 22:45:16",
                dateTimeFormatter);
        // parse = 1997-05-06T22:45:16
        System.out.println("parse = " + parse);
    }

2.4 Instant 类
        在JDK8 中给我们新增一个 Instant 类 ( 时间戳 / 时间线 ) ,内部保存了从 1970 年 1 月 1 日 00:00:00 以来的秒和纳秒

    /**
     * Instant 时间戳
     * 可以用来统计时间消耗
     */
    @Test
    public void test01() throws Exception{
        Instant now = Instant.now();
        System.out.println("now = " + now);
        // 获取从1970年一月一日 00:00:00 到现在的 纳秒
        System.out.println(now.getNano());
        Thread.sleep(5);
        Instant now1 = Instant.now();
        System.out.println("耗时:" + (now1.getNano() - now.getNano()));
    }

2.5 计算日期时间差
JDK8 中提供了两个工具类 Duration/Period :计算日期时间差

  • Duration:用来计算两个时间差(LocalTime)
  • Period:用来计算两个日期差(LocalDate)
 /**
     * 计算日期时间差
     */
    @Test
    public void test01(){
        // 计算时间差
        LocalTime now = LocalTime.now();
        LocalTime time = LocalTime.of(22, 48, 59);
        System.out.println("now = " + now);
        // 通过Duration来计算时间差
        Duration duration = Duration.between(now, time);
        System.out.println(duration.toDays()); // 0
        System.out.println(duration.toHours()); // 6
        System.out.println(duration.toMinutes()); // 368
        System.out.println(duration.toMillis()); // 22124240
        // 计算日期差
        LocalDate nowDate = LocalDate.now();
        LocalDate date = LocalDate.of(1997, 12, 5);
        Period period = Period.between(date, nowDate);
        System.out.println(period.getYears()); // 23
        System.out.println(period.getMonths()); // 5
        System.out.println(period.getDays()); // 22
    }

2.6 时间校正器
        有时候我们可以需要如下调整:将日期调整到" 下个月的第一天 " 等操作。这时我们通过时间校正器效果可能会更好。

  • TemporalAdjuster:时间校正器
  • TemporalAdjusters:通过该类静态方法提供了大量的常用TemporalAdjuster的实现。
    /**
     * 时间校正器
     */
    @Test
    public void test02(){
        LocalDateTime now = LocalDateTime.now();
        // 将当前的日期调整到下个月的一号
        TemporalAdjuster adJuster = (temporal)->{
            LocalDateTime dateTime = (LocalDateTime) temporal;
            LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
            System.out.println("nextMonth = " + nextMonth);
            return nextMonth;
        };
        // 我们可以通过TemporalAdjusters 来实现
        // LocalDateTime nextMonth = now.with(adJuster);
        LocalDateTime nextMonth =
                now.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("nextMonth = " + nextMonth);
    }

2.7 日期时间的时区
Java8 中加入了对时区的支持, LocalDate 、 LocalTime 、 LocalDateTime 是不带时区的,带时区的日 期时间类分别为:ZonedDate 、 ZonedTime 、 ZonedDateTime 。
其中每个时区都对应着 ID , ID 的格式为 “ 区域 / 城市 ” 。例如 : Asia/Shanghai 等。
ZoneId :该类中包含了所有的时区信息

    /**
     * 时区操作
     */
    @Test
    public void test01() {
        // 1.获取所有的时区id
        // ZoneId.getAvailableZoneIds().forEach(System.out::println);
        // 获取当前时间 中国使用的 东八区的时区,比标准时间早8个小时
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = " + now); // 2021-05-27T17:17:06.951
        // 获取标准时间
        ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
        System.out.println("bz = " + bz); // 2021-05-27T09:17:06.952Z
        // 使用计算机默认的时区,创建日期时间
        ZonedDateTime now1 = ZonedDateTime.now();
        System.out.println("now1 = " + now1); //2021-05-
        27 T17:
        17:06.952 + 08:00[Asia / Shanghai]
        // 使用指定的时区创建日期时间
        ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
        System.out.println("now2 = " + now2);
    }

JDK新的日期和时间API的优势

  1. 新版日期时间API中,日期和时间对象是不可变,操作日期不会影响原来的值,而是生成一个新的 实例
  2. 提供不同的两种方式,有效的区分了人和机器的操作
  3. TemporalAdjuster可以更精确的操作日期,还可以自定义日期调整期
  4. 线程安全

七、Optional 

这个Optional 类注意是解决空指针的问题

        1. 以前对 null 的处理

    @Test
    public void test01(){
        //String userName = "张三";
        String userName = null;
        if(userName != null){
            System.out.println("字符串的长度:" + userName.length());
        }else{
            System.out.println("字符串为空");
        }
    }

 2. Optional类
        Optional是一个没有子类的工具类, Optional 是一个可以为 null 的容器对象,它的主要作用就是为了避 免Null 检查,防止 NullpointerException。
3. Optional 的基本使用
        Optional 对象的创建方式

    /**
     * Optional对象的创建方式
     */
    @Test
    public void test02(){
        // 第一种方式 通过of方法 of方法是不支持null的
        Optional<String> op1 = Optional.of("zhangsan");
        //Optional<Object> op2 = Optional.of(null);
        // 第二种方式通过 ofNullable方法 支持null
        Optional<String> op3 = Optional.ofNullable("lisi");
        Optional<Object> op4 = Optional.ofNullable(null);
        // 第三种方式 通过empty方法直接创建一个空的Optional对象
        Optional<Object> op5 = Optional.empty();
    }

 4. Optional的常用方法

 /**
     * Optional中的常用方法介绍
     * get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常
     * get()通常和isPresent方法一块使用
     * isPresent():判断是否包含值,包含值返回true,不包含值返回false
     * orElse(T t):如果调用对象包含值,就返回该值,否则返回t
     * orElseGet(Supplier s):如果调用对象包含值,就返回该值,否则返回 Lambda表达式的返
     回值
     */
    @Test
    public void test03(){
        Optional<String> op1 = Optional.of("zhangsan");
        Optional<String> op2 = Optional.empty();
        // 获取Optional中的值
        if(op1.isPresent()){
            String s1 = op1.get();
            System.out.println("用户名称:" +s1);
        }
        if(op2.isPresent()){
            System.out.println(op2.get());
        }else{
            System.out.println("op2是一个空Optional对象");
        }
        String s3 = op1.orElse("李四");
        System.out.println(s3);
        String s4 = op2.orElse("王五");
        System.out.println(s4);
        String s5 = op2.orElseGet(()->{
            return "Hello";
        });
        System.out.println(s5);
    }
/**
     * 自定义一个方法,将Person对象中的 name 转换为大写 并返回
     */
    @Test
    public void test05(){
        Person p = new Person("zhangsan",18);
        Optional<Person> op = Optional.of(p);
        String name = getNameForOptional(op);
        System.out.println("name="+name);
    }
    /**
     * 根据Person对象 将name转换为大写并返回
     * 通过Optional方式实现
     * @param op
     * @return
     */
    public String getNameForOptional(Optional<Person> op){
        if(op.isPresent()){
            String msg = //op.map(p -> p.getName())
                    op.map(Person::getName)
                            //.map(p -> p.toUpperCase())
                            .map(String::toUpperCase)
                            .orElse("空值");
            return msg;
        }
        return null;
    }
    /**
     * 根据Person对象 将name转换为大写并返回
     * @param person
     * @return
     */
    public String getName(Person person){
        if(person != null){
            String name = person.getName();
            if(name != null){
                return name.toUpperCase();
            }else{
                return null;
            }
        }else{
            return null;
        }
    }

 八、其他特性 

1. 重复注解
        自从Java 5 中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注 解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8 引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8 中使用 @Repeatable 注解定义重复注解。
1.1 定义一个重复注解的容器

    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotations {
        MyAnnotation[] value();
    }

1.2 定义一个可以重复的注解

    @Repeatable(MyAnnotations.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value();
    }

1.3 配置多个重复的注解

    @MyAnnotation("test1")
    @MyAnnotation("test2")
    @MyAnnotation("test3")
    public class AnnoTest01 {
        @MyAnnotation("fun1")
        @MyAnnotation("fun2")
        public void test01(){
        }
    }

1.4 解析得到指定的注解

    /**
     * 解析重复注解
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException {
        // 获取类中标注的重复注解
        MyAnnotation[] annotationsByType =
                AnnoTest01.class.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation myAnnotation : annotationsByType) {
            System.out.println(myAnnotation.value());
        }
        // 获取方法上标注的重复注解
        MyAnnotation[] test01s = AnnoTest01.class.getMethod("test01")
                .getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation test01 : test01s) {
            System.out.println(test01.value());
        }
    }

 2. 类型注解

JDK 8 为 @Target 元注解新增了两种类型: TYPE_PARAMETER , TYPE_USE 。

  • TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 类型参数声明如: 、
  • TYPE_USE :表示注解可以再任何用到类型的地方使用。

TYPE_PARAMETER

@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}

使用:

    public class TypeDemo01 <@TypeParam T> {
        public <@TypeParam K extends Object> K test01(){
            return null;
        }
    }

TYPE_USE 

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

使用 

    public class TypeUseDemo01 {
        public @NotNull Integer age = 10;
        public Integer sum(@NotNull Integer a,@NotNull Integer b){
            return a + b;
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1575336.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[挖坟]如何安装Shizuku和LSPatch并安装模块(不需要Root,非Magisk)

2023年12月13日&#xff0c;LSPatch 停止维护 2024年1月8日&#xff0c;LSPosed 停止维护 2024年1月8日&#xff0c;ZygiskNext 停止维护 2024年1月9日&#xff0c;KernelSU 停止维护 这里使用 ColorOS 14 演示&#xff0c;其他品牌手机类似 安装 Shizuku 官网: https://shiz…

JQuery(二)---【使用JQuery对HTML、CSS进行操作】

零.前言 JQuery(一)---【JQuery简介、安装、初步使用、各种事件】-CSDN博客 一.使用JQuery对HTML操作 1.1获取元素内容、属性 使用JQ可以操作元素的“内容” text()&#xff1a;设置或返回元素的文本内容html()&#xff1a;设置或返回元素的内容(包括HTML标记)val()&#…

每天一个注解之@DataSource、 @DS

在Java中&#xff0c;DataSource 注解通常用于标记数据源&#xff08;DataSource&#xff09;相关的信息。数据源是一个用于获取数据库连接的对象&#xff0c;它通常用于与数据库进行交互。DataSource 注解的详细说明可能会因不同的框架或库而有所不同&#xff0c;但通常用于配…

2024-04-07 作业

作业要求&#xff1a; 1> 思维导图 2> 自由发挥应用场景实现一个登录窗口界面。 【可以是QQ登录界面、也可以是自己发挥的登录界面】 要求&#xff1a;尽量每行代码都有注释 作业1&#xff1a; 作业2&#xff1a; 运行代码&#xff1a; #include "myqwidget.h&quo…

部署安装ElasticSearch、Kibana、IK

文章目录 1、部署单点es1.1、创建网络1.2、加载镜像1.3、运行 2、部署kibana2.1、部署2.2、DevTools 3、IK分词器3.1、在线安装3.2、离线安装1&#xff09;查看数据卷目录2&#xff09;解压缩分词器安装包3&#xff09;上传到es容器的插件数据卷中4&#xff09;重启容器5&#…

2024.4.7

1. 2列火车 #include<myhead.h>pthread_mutex_t m1; pthread_mutex_t m2;void* run(void* arg) {while(1){pthread_mutex_lock(&m1);printf("火车B进入\n");printf("A请等待\n");pthread_mutex_unlock(&m2);sleep(2);} }int main(in…

火山方舟大模型服务平台调用Demo测试(豆包)

豆包得后台大模型支持为字节得火山方舟&#xff0c;所以想使用豆包的API&#xff0c;直接从这里就可以。 一、首先注册账号&#xff1a; 火山引擎-云上增长新动力 注册完成之后&#xff0c;控制台-账户-API访问密钥 二、找到API测试用例&#xff1a; Skylark-chat API调用…

白盒测试-语句覆盖

​ 语句覆盖法是指设计适当数量的测试用例&#xff0c;使被测程序中的每条语句至少被执行一次。语句覆盖率的计算方法为&#xff1a; ​ 至少被执行一次的语句数量 / 程序中可执行的语句总数。 案例 ​ 为了清晰地比较几种逻辑覆盖法设计测试用例的异同&#xff0c;逻辑覆盖…

LeetCode热题100:哈希

1.两数之和 题目链接&#xff1a;两数之和 题目描述&#xff1a;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数…

11.2 浏览器调试常用技巧

目录 1、开发者工具介绍 2、查看节点事件 3、断点调试 4、观察调用栈 5、恢复 JavaScript 执行 6、Ajax 断点 7、改写 JavaScript 文件 1、开发者工具介绍 由于需要学习 JavaScript 逆向&#xff0c;所以此文主要介绍与 JavaScript 逆向有帮助的功能。 以下链接为例&a…

国内:深圳交通流量数据集

数据来源&#xff1a;深圳政府数据开放平台&#xff08;深圳市政府数据开放平台&#xff09;&#xff0c;这个官网上还有其他类数据集&#xff0c;值得收藏&#xff01;&#xff01;&#xff01; 数据集介绍&#xff1a;宝安区-G4高速西乡大道入口车流量统计 第一行每列的标题…

记一次Debug与Release版程序输出不一致的问题解决

问题叙述&#xff1a; 在x86平台下无论Debug还是Release都没问题&#xff0c;而在arm平台下Debug版本程序无问题&#xff0c;Release版本程序&#xff08;-O3编译&#xff09;发现输出值不正确&#xff0c;怀疑值被篡改&#xff0c;于是在调用前后分别使用printf打印出参数值&…

vitepress系列-04-规整sideBar左侧菜单导航

规整左侧菜单导航 新建navConfig.ts 文件用来管理左侧导航菜单&#xff1a; 将于其他的配置分开&#xff0c;避免config.mts太大 在config目录下&#xff0c;新建 sidebarModules文件目录用来左侧导航菜单 按模块进行分类&#xff1a; 在config下新建sidebarConfig.ts文件&…

3dmax经常染失败?优化方法提升染质量!

在三维建模和渲染的过程中&#xff0c;优化模型和场景的效率是至关重要的。以下是一些提升效率的方法&#xff1a; 模型简化&#xff1a;在创建模型时&#xff0c;应尽量减少使用的命令和修改器的数量。这是因为命令和修改器越多&#xff0c;消耗的内存和CPU资源也就越多&…

vitepress系列-05-其他优化设置

其他优化设置 设置底部上一页和下一页 设置&#xff1a; import { defineConfig } from vitepress// https://vitepress.dev/reference/site-config export default defineConfig({lang: en-US,title: "东东爱编码的技术博客",description: "记录日常学习点点…

Cute Background FX

Cute Background FX是环境背景粒子系统的集合。非常适合作为菜单的背景。 该包包括: -20个独特预制件+20个URP预制件 -5种独特的环境设计 -15种纹理 -2个自定义着色器+2个URP着色器 -共59项独特资产 -一个演示场景,您可以在其中概述所有内容。 所有纹理都是512x512分辨率的P…

布隆过滤器详解及java实现

什么是布隆过滤器&#xff1f; 布隆过滤器&#xff08;Bloom Filter&#xff09;是一种数据结构&#xff0c;用于判断一个元素是否属于一个集合。它的特点是高效地判断一个元素是否可能存在于集合中&#xff0c;但是存在一定的误判率。 布隆过滤器的基本原理是使用一个位数组…

原型变量、原子操作、原子性、内存序

一、原子变量、原子操作 锁竞争&#xff1a;互斥锁、条件变量、原子变量、信号量、读写锁、自旋锁。在高性能基础组件优化的时候&#xff0c;为了进一步提高并发性能&#xff0c;可以使用原子变量。性能&#xff1a;原子变量 > 自旋锁 > 互斥锁。 操作临界资源的时间较长…

第十讲 Query Execution Part 1

1 处理模型【Processing Model】 DBMS 的处理模型【Processing Model】定义了系统如何执行【execute】查询计划【Query Plan】。 针对不同的工作负载进行不同的权衡。 方法1&#xff1a;迭代器模型【Iterator Model】 方法2&#xff1a;物化模型【Materialization Model】 方…

linux虚拟机上安装,使用以及远程连接mysql

1. 安装mysql 5.7 1) 首先更新软件源 sudo apt-get update 2) 安装MySQL数据库软件 ​ sudo apt-get install mysql-server 3) 安装MySQL数据库管理软件​ sudo apt-get install mysql-client 4) 安装MySQL数据库客户端&#xff0c;用户访问数据库 sudo apt-get install…