文章目录

  • Lambda表达式
  • 为什么使用Lambda表达式
  • Lambda表达式语法
  • 语法格式一:无参数,无返回值
  • 语法格式二:有一个参数,并且无返回值
  • 语法格式三:若只有一个参数,小括号可以省略不写
  • 语法格式四:有两个以上的参数,有返回值,并且 Lambda体中有多条语句
  • 语法格式五:若 Lambda体中只有一条语句, return和大括号都可以省略不写
  • 语法格式六:参数列表的数据类型可以省略不写
  • 函数式接口
  • 什么是函数式接口
  • Java内置函数式接口
  • 四大核心接口
  • 其他接口
  • 方法引用、构造器引用、数组引用
  • 方法引用
  • 构造器引用
  • 数组引用
  • 强大的Stream API
  • 什么是Stream
  • Stream操作的三个步骤
  • 创建Stream
  • 中间操作
  • 筛选与切片
  • 映射
  • 排序
  • 终止操作(终端操作)
  • 查找与匹配
  • 内部迭代
  • 最值、数量
  • 归约
  • 收集
  • 接口中的默认方法与静态方法
  • 默认方法
  • 静态方法
  • 新时间日期API
  • 简介
  • Instant时间戳
  • Duration和Period
  • 时间校正器
  • 解析和格式化日期或时间
  • 带时区的时间或日期
  • 与传统日期处理的转换
  • Optional类
  • JVM的新特性
  • 优化HashMap
  • 重复注解
  • Lambda表达式

    为什么使用Lambda表达式

  • Lambda是一个匿名函数,相当于匿名内部类
  • 只有函数式接口(可以用@FunctionalInterface注解修饰) 可以使用Lambda表达式
  • 使用 Lambda省略了写实现类的繁琐步骤,可以写出更简洁、更灵活的代码,使Java的语言表达能力得到了提升。
  • Lambda表达式语法

    Lambda 表达式在Java 语言中引入了一个新的操作符, “->”, 它将 Lambda 分为两个部分:
    左侧:指定了 Lambda表达式的参数列表
    右侧:指定了 Lambda体,即 Lambda表达式要执行的功能。

    语法格式一:无参数,无返回值

    () -> System.out.println("Hello Lambda!");
    

    语法格式二:有一个参数,并且无返回值

    (x) -> System.out.println(x);
    

    语法格式三:若只有一个参数,小括号可以省略不写

    x -> System.out.println(x)
    

    语法格式四:有两个以上的参数,有返回值,并且 Lambda体中有多条语句

    Comparator<Integer> com = (x, y) -> {
    	System.out.println("函数式接口");
    	return Integer.compare(x, y);
    };
    

    语法格式五:若 Lambda体中只有一条语句, return和大括号都可以省略不写

    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    

    语法格式六:参数列表的数据类型可以省略不写

    JVM编译器可以通过上下文推断出参数列表对应的数据类型,Lambda 表达式中无需指定类型,程序依然可以编译,即**“类型推断”**。

    // Integer推荐省略
    (Integer x, Integer y) -> Integer.compare(x, y);
    

    语法速记口诀:
    上联:左右遇一括号省
    下联:左侧推断类型省
    横批:能省则省

    函数式接口

    什么是函数式接口

  • 只包含一个抽象方法的接口,称为函数式接口。
  • 可以通过 Lambda 表达式来创建该接口的实现类。
  • 函数式接口只会有一个抽象方法,default方法不属于抽象方法,接口重写了Object的公共方法也不算入内,所以Comparator是函数式接口。

    Java内置函数式接口

    四大核心接口

    接口名 参数类型 返回类型 用途
    Consumer<T> 消费型接口 T void void accept(T t);
    适合“传递参数没有返回值”的场景
    Supplier<T> 供给型接口 T T get();
    适合“没有参数有返回值”的场景
    Function<T,R> 函数型接口 T R R apply(T t);
    适合“传递参数有返回值”的场景
    Predicate<T> 断定型接口 T boolean boolean test(T t);
    适合“传递参数返回boolean值”的场景

    其他接口

    接口名 参数类型 返回类型 用途
    BiFunction<T, U, R> T, U R R apply(T t, U u);
    适合“传递两个参数有返回值”的场景
    UnaryOperator<T> (Function子接口) T T T apply(T t);
    进行一元运算,适合“传递一个参数有返回值”的场景
    BinaryOperator<T>(BiFunction 子接口) T, T T T apply(T t1, T t2);
    进行二元运算,适合“传递两个参数有返回值”的场景
    BiConsumer<T, U> T void void accept(T t, U u);
    适合“传递两个参数没有返回值”的场景

    方法引用、构造器引用、数组引用

    方法引用

    若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。
    可以将方法引用理解 Lambda 表达式的另外一种表现形式
    使用操作符 “::” 将方法名和对象或类的名字分隔开来。
    主要有下面三种使用情况:

  • 对象::实例方法
  • // 示例1
    PrintStream ps = System.out;
    Consumer<String> con = (str) -> ps.println(str);
    con.accept("Hello World!");
    
    Consumer<String> con2 = ps::println;
    con2.accept("Hello Java8!");
    // 示例2
    Employee emp = new Employee(101, "张三", 18, 9999.99);
    Supplier<String> sup = () -> emp.getName();
    System.out.println(sup.get());
    
    Supplier<String> sup2 = emp::getName;
    System.out.println(sup2.get());
    
  • 类::静态方法
  • // 示例1
    BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
    System.out.println(fun.apply(1.5, 22.2));
    BiFunction<Double, Double, Double> fun2 = Math::max;
    System.out.println(fun2.apply(1.2, 1.5));
    // 示例2
    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    Comparator<Integer> com2 = Integer::compare;
    
  • 类::实例方法
  • // 示例1
    BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    System.out.println(bp.test("abcde", "abcde"));
    BiPredicate<String, String> bp2 = String::equals;
    System.out.println(bp2.test("abc", "abc"));
    // 示例2
    Function<Employee, String> fun = (e) -> e.show();
    System.out.println(fun.apply(new Employee()));
    Function<Employee, String> fun2 = Employee::show;
    System.out.println(fun2.apply(new Employee()));
    

    注意:

  • 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致。
  • 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
  • 构造器引用

    构造器的参数列表,需要与函数式接口中参数列表保持一致。
    语法格式:
    类名 :: new
    示例:

    Function<String, Employee> fun = Employee::new;
    

    数组引用

    语法格式:
    类型[] :: new
    示例:

    Function<Integer, String[]> fun = (length) -> new String[length];
    String[] strs = fun.apply(10);
    System.out.println(strs.length);
    Function<Integer, String[]> fun2 = String[]::new;
    String[] strs2 = fun2.apply(15);
    System.out.println(strs2.length);
    

    强大的Stream API

    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

    什么是Stream

    Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
    集合讲的是数据,流讲的是计算!

    注意:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
  • Stream操作的三个步骤


    Stream操作的三个步骤如下:

  • 创建 Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果
  • 示例如下:

    //所有的中间操作不会做任何的处理
    Stream<Employee> stream = emps.stream()
    	.filter((e) -> {
    		System.out.println("测试中间操作");
    		return e.getAge() <= 35;
    	});
    //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
    stream.forEach(System.out::println);
    

    创建Stream

    一、Java8 中的Collection接口被扩展,提供了两个获取流的方法:

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流
  • 示例代码如下:

    //1、 Collection提供了两个方法  stream() 与 parallelStream()
    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream(); //获取一个顺序流
    Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
    

    二、由数组创建流
    Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
    static Stream stream(T[] array): 返回一个流。

    示例代码如下:

    //2、 通过 Arrays 中的 stream() 获取一个数组流
    Integer[] nums = new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(nums);
    

    三、用静态方法Stream.of()
    可以使用静态方法 Stream.of(), 创建一个流。它可以接收任意数量的参数。
    public static Stream of(T… values) : 返回一个流。
    示例代码如下:

    //3、通过 Stream 类中静态方法 of()
    Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
    

    四、创建无限流
    示例代码:

    //迭代
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
    stream3.forEach(System.out::println);
    //生成
    Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
    stream4.forEach(System.out::println);
    

    中间操作

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为**“惰性求值”**。

    筛选与切片

    常用方法如下:

    方法 描述
    filter(Predicate p) 过滤: 接收 Lambda , 从流中排除某些元素。
    limit(long maxSize) 截断流: 使其元素不超过给定数量。
    skip(long n) 跳过元素: 返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。
    limit(n) 互补
    distinct() 去重: 通过流所生成元素的hashCode()和equals()去除重复元素

    示例代码如下:

    // 1、filter过滤
    //所有的中间操作不会做任何的处理
    Stream<Employee> stream = emps.stream()
    	.filter((e) -> {
    		System.out.println("测试中间操作");
    		return e.getAge() <= 35;
    	});
    //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
    stream.forEach(System.out::println);
    // 2、limit截断流
    emps.stream()
    	.filter((e) -> {
    		System.out.println("短路!"); // &&  ||
    		return e.getSalary() >= 5000;
    	})
    	.limit(3)
    	.forEach(System.out::println);
    // 3、skip跳过元素
    emps.parallelStream()
    	.filter((e) -> e.getSalary() >= 5000)
    	.skip(2)
    	.forEach(System.out::println);
    // 4、distinct去重
    emps.stream()
    	.distinct()
    	.forEach(System.out::println);
    

    映射

    常用方法如下:

    方法 描述
    map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。
    mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
    mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。
    flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

    示例代码如下:

    public static void main(String[] args) {
         List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
         // 1、map简单使用
         Stream<String> stream = strList.stream()
                 .map(String::toUpperCase);
         stream.forEach(System.out::println);
    
         Stream<Stream<Character>> stream2 = strList.stream()
                 .map(Test::filterCharacter);
         // 输出aaabbbcccdddeeeaaabbbcccdddeee
         stream2.forEach((sm) -> {
             sm.forEach(System.out::print);
         });
    
         // 2、flatMap使用:相当于List的addAll()
         Stream<Character> stream3 = strList.stream()
                 .flatMap(Test::filterCharacter);
         // 输出aaabbbcccdddeeeaaabbbcccdddeee
         stream3.forEach(System.out::print);
     }
    
    // 把字符串中的每一个字符加入到list,返回流
     public static Stream<Character> filterCharacter(String str){
         List<Character> list = new ArrayList<>();
         for (Character ch : str.toCharArray()) {
             list.add(ch);
         }
         return list.stream();
     }
    

    排序

    常用方法如下:

    方法 描述
    sorted() 产生一个新流,按自然顺序排序。
    sorted(Comparator comp) 产生一个新流,按比较器顺序排序。

    示例代码如下:

    // 自然排序
    emps.stream()
    	.map(Employee::getName)
    	.sorted()
    	.forEach(System.out::println);
    // 定制排序
    emps.stream()
    	.sorted((x, y) -> {
    		if(x.getAge() == y.getAge()){
    			return x.getName().compareTo(y.getName());
    		}else{
    			return Integer.compare(x.getAge(), y.getAge());
    		}
    	}).forEach(System.out::println);
    

    终止操作(终端操作)

    注意:
    流进行了终止操作后,不能再次使用。

    查找与匹配

    常用方法如下:

    方法 描述
    allMatch(Predicate p) 检查是否匹配所有元素
    anyMatch(Predicate p) 检查是否至少匹配一个元素
    noneMatch(Predicate p) 检查是否没有匹配所有元素
    findFirst() 返回第一个元素
    findAny() 返回当前流中的任意元素

    示例代码如下:

    boolean bl = emps.stream()
    	.allMatch((e) -> e.getStatus().equals(Status.BUSY));
    
    boolean bl1 = emps.stream()
    	.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
    
    boolean bl2 = emps.stream()
    	.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
    
    Optional<Employee> op = emps.stream()
    	.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
    	.findFirst();
    System.out.println(op.get());
    
    Optional<Employee> op2 = emps.parallelStream()
    	.filter((e) -> e.getStatus().equals(Status.FREE))
    	.findAny();
    System.out.println(op2.get());
    

    内部迭代

    方法 描述
    forEach(Consumer c) 内部迭代

    最值、数量

    方法 描述
    count() 返回流中元素总数
    max(Comparator c) 返回流中最大值
    min(Comparator c) 返回流中最小值

    示例代码如下:

    long count = emps.stream()
    				 .filter((e) -> e.getStatus().equals(Status.FREE))
    				 .count();
    
    Optional<Double> op = emps.stream()
    	.map(Employee::getSalary)
    	.max(Double::compare);
    
    Optional<Employee> op2 = emps.stream()
    	.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    

    归约

    方法 描述
    reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。
    返回 T。
    reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。
    返回Optional

    示例代码如下:

    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Integer sum = list.stream()
    	.reduce(0, (x, y) -> x + y);
    System.out.println(sum);
    
    Optional<Double> op = emps.stream()
    	.map(Employee::getSalary)
    	.reduce(Double::sum);
    System.out.println(op.get());
    
    // 搜索名字中 “六” 出现的次数
    Optional<Integer> sum = emps.stream()
    	.map(Employee::getName)
    	.flatMap(Test::filterCharacter)
    	.map((ch) -> {
    		if(ch.equals('六'))
    			return 1;
    		else 
    			return 0;
    	}).reduce(Integer::sum);
    System.out.println(sum.get());
    

    收集

    方法 描述
    collect(Collector c) 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

    Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

    示例代码如下:

    List<DeptInfoVo> deptInfoVoList = getDeptInfoVoData();
    Set<String> set = deptInfoVoList.stream()
            .map(DeptInfoVo::getDeptName)
            .collect(Collectors.toSet());
    
    HashSet<String> hashSet = deptInfoVoList.stream()
            .map(DeptInfoVo::getDeptName)
            .collect(Collectors.toCollection(HashSet::new));
    
    // 分组
    Map<Status, List<Employee>> map = emps.stream()
    .collect(Collectors.groupingBy(Employee::getStatus));
    
    // 多级分组
    Map<Status, Map<String, List<Employee>>> map = emps.stream()
    			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
    				if(e.getAge() >= 60)
    					return "老年";
    				else if(e.getAge() >= 35)
    					return "中年";
    				else
    					return "成年";
    			})));
    

    接口中的默认方法与静态方法

    默认方法

    Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default关键字修饰。

    接口默认方法的类优先原则:
    一个子类继承了一个父类,同时实现了一个接口。这个父类和接口有同名(默认)的方法,则字类调用方法会有些调用父类的方法。
    示例代码如下:

    public class ParentClass {
        public String getName(){
            return "嘿嘿嘿";
        }
    }
    
    public interface MyInterface {
        default String getName(){
            return "哈哈哈哈";
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
            // 输出:嘿嘿嘿
            System.out.println(subClass.getName());
        }
     }
    

    当然如果子类再重写方法的话,以子类的为准。

    接口默认方法冲突的解决方法:
    如果一个父接口提供一个方法,而另一个父接口接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须重写该方法来解决冲突
    示例代码如下:

    public interface MyInterface {
        default String getName(){
            return "嘿嘿嘿嘿";
        }
    }
    public interface MyInterfaceTwo {
        default String getName(){
            return "哈哈哈哈";
        }
    }
    public class SubClass implements MyInterface,MyInterfaceTwo {
        // 必须重写该方法
        @Override
        public String getName() {
            // return MyInterface.super.getName();
            return "我是子类重写的方法";
        }
    }
    public class Test {
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
            // 输出:我是子类重写的方法
            System.out.println(subClass.getName());
        }
     }
    

    Java中Super的用法

    静态方法

    Java8接口中允许添加静态方法。
    示例代码如下:

    public interface MyInterface {
        static String getName(){
            return "我是静态方法";
        }
    }
    public class Test {
        public static void main(String[] args) {
        	//输出:我是静态方法
            System.out.println(MyInterface.getName());
        }
      }
    

    新时间日期API

    简介

    SimpleDateFormat有线程安全问题,且Date类不好用。
    Java8提供了LocalDate、LocalTime和LocalDateTime 类,它们的实例是不可变的对象做任何操作都会新生成一个对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

    注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
    相关方法如下:

    示例代码如下:

    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);
    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());
    
    LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
    System.out.println(ld2);
    
    LocalDateTime ldt3 = ld2.plusYears(20);
    System.out.println(ldt3);
    
    LocalDateTime ldt4 = ld2.minusMonths(2);
    System.out.println(ldt4);
    

    Instant时间戳

    Instant用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的毫秒数进行运算。
    示例代码如下:

    // 默认使用 UTC 时区
    Instant ins = Instant.now();
    System.out.println(ins);
    
    // 使用东8区
    OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
    System.out.println(odt);
    // 获取毫秒数
    System.out.println(ins.toEpochMilli());
    
    // 1970元年之后5秒
    Instant ins2 = Instant.ofEpochSecond(5);
    System.out.println(ins2);
    

    Duration和Period

    1、Duration: 用于计算两个“时间”间隔
    2、Period:用于计算两个“日期”间隔

    示例代码如下:

    Instant ins1 = Instant.now();
    try {
    	Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
    Instant ins2 = Instant.now();
    System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
    
    LocalDate ld1 = LocalDate.now();
    LocalDate ld2 = LocalDate.of(2011, 1, 1);
    Period pe = Period.between(ld2, ld1);
    System.out.println(pe.getYears());
    System.out.println(pe.getMonths());
    System.out.println(pe.getDays());
    

    时间校正器

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

    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);
    // 获取下一个星期天
    LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(ldt3);
    
    //自定义:下一个工作日
    LocalDateTime ldt5 = ldt.with((l) -> {
    	LocalDateTime ldt4 = (LocalDateTime) l;
    	DayOfWeek dow = ldt4.getDayOfWeek();
    	if(dow.equals(DayOfWeek.FRIDAY)){
    		return ldt4.plusDays(3);
    	}else if(dow.equals(DayOfWeek.SATURDAY)){
    		return ldt4.plusDays(2);
    	}else{
    		return ldt4.plusDays(1);
    	}
    });
    System.out.println(ldt5);
    

    解析和格式化日期或时间

    示例代码如下:

    //DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
    LocalDateTime ldt = LocalDateTime.now();
    String strDate = ldt.format(dtf);
    System.out.println(strDate);
    LocalDateTime newLdt = ldt.parse(strDate, dtf);
    System.out.println(newLdt);
    

    带时区的时间或日期

    Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime。其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式,
    例如 :Asia/Shanghai。

    示例代码如下:

    LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(ldt);
    
    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
    System.out.println(zdt);
    

    与传统日期处理的转换

    相关方法如下:

    Optional类

    Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
    常用方法如下:

    方法 描述
    Optional.of(T t) 创建一个 Optional实例
    Optional.empty() 创建一个空的 Optional实例
    Optional.ofNullable(T t) 若 t 不为 null,创建 Optional 实例,否则创建空实例。相当于Optional.of(T t)+Optional.empty()
    isPresent() 判断是否包含值
    orElse(T t) 如果调用对象包含值,返回该值,否则返回t
    orElseGet(Supplier s) 如果调用对象包含值,返回该值,否则返回 s 获取的值
    map(Function f) 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
    flatMap(Function mapper) 与 map 类似,要求返回值必须是Optional

    JVM的新特性

    使用元空间Metaspace代替持久代PermGen space。
    在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。

    优化HashMap

    “数组+链表” 数据结构改为了 “数组+(链表/红黑树)”
    当链表长度大于8,且HashMap总容量大于64,会将链表自动转为红黑树。
    添加元素比链表慢,其他的都比链表更快速。

    重复注解

    Java8允许在同一申明类型(类,属性,或方法)的多次使用同一个注解,这就是重复注解。Java8开始注解可以应用在任何地方。
    Java8之前对重复注解的支持
    示例代码:

    public @interface Authority {
         String role();
    }
    public @interface Authorities {
        Authority[] value();
    }
    
    public class RepeatAnnotationUseOldVersion {
    
        @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
        public void doSomeThing(){
        }
    }
    

    Java8之前,重复注解实现是由另一个注解来存储重复注解
    在使用时候,用存储注解Authorities来扩展重复注解。
    很明显,这种方式可读性不是很好

    Java8对重复注解的支持
    示例代码:

    @Repeatable(Authorities.class)
    public @interface Authority {
        String role();
    }
    public @interface Authorities {
       Authority[] value();
    }
    
    public class RepeatAnnotationUseNewVersion {
       @Authority(role="Admin")
       @Authority(role="Manager")
       public void doSomeThing(){ }
    }
    

    不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities
    在使用时候,直接可以重复使用Authority注解。
    从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点

    说明:本笔记整理自尚硅谷Java8新特性课程以及互联网,仅供学习使用。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 学习Java8的最新特性

    发表回复