Lambda 表达式

是一个匿名函数
可以被当作一段可以传递的代码
//匿名内部类
Runnable r1=new Runnable(){
    @Override
    public void run(){
        System.out.println("Hello World");
    }
}


//Lambda表达式
Runnable r1=() -> System.out.println("Hello Lambda");
//使用匿名内部类作为参数传递
TreeSet<String> ts=new TreeSet<>(
    new Comparator<String>(){
        @Override
        public int compare(String o1,String o2){
            return Integer.compare(o1.length(),o2.length());
        }
    }
);


//Lambda表达式作为参数传递
TreeSet<String> ts2=new TreeSet<>(
    (o1,o2) -> Integer.compare(o1.length(),o2.length()) 
);

语法格式:

无参,无返回值,Lambda体只需一条语句:
Runnable r1=()->System.out.println("Hello Lambda");

一个参数:(参数小括号可以省略)
Consumer<String> fun = (args) -> System.out.println(args);

两个参数,并且有返回值:
BinaryOperator<Long> bo=(x,y)->{
    System.out.println("实现函数接口");
    return x+y;
};

如果Lambda体只有一条语句时,return与大括号可以省略
参数数据类型也可以省略,可以由编译器推断得出“类型推断”


函数式接口

只包含一个抽象方法的接口。
通过Lambda表达式来创建该接口的对象,若抛出一个受检异常,那么该异常需要在目标接口方法上进行声明。
函数式接口上使用@FunctionalInterface注解,可以检查它是否是一个函数式接口。

自定义函数式接口

@FunctionalInterface
public interface MyNumber{
    public double getValue();
}

//函数式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T>{
    public T getValue(T t);
}

作为参数传递Lambda表达式

public String toUpperString(MyFunc<String> mf,String str){
    return mf.getValue(str);
}

//作为参数传递Lambda表达式:接收Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口类型
String newStr=toUpperString(
    (str)->str.toUpperCase(),"abcdef"
);
System.out.println(newStr);




方法引用和构造器

对象::实例方法
类::静态方法
类::实例方法
(x)->System.out.println(x);
//等同于
System.out::println
BinaryOperator<Double> bo=(x,y)->Math.pow(x,y);
//等同于
BinaryOperator<Double> bo=Math::pow;
当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName

构造器引用:

与函数式接口相结合,自动与函数式接口中的方法兼容,可以把构造器引用赋值给定义的方法与构造器参数列表要与接口中抽象方法的参数列表一致。
Function<Integer,MyClass> fun = (n) -> new MyClass(n);
//等同于
Function<Integer,MyClass> fun = MyClass::new;
数组引用:
格式   type[] :: new 
Function<Integer,Integer[]> fun = (n) -> new Intener[n];
//等同于
Function<Integer,Integer[]> fun = Integer[]::new;



新的日期时间 API

LocalDate 、LocalTime 、LocalDateTime类的实例是不可变的对象。
分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。


Instant用于时间戳的运算
Duration:用于计算两个时间的间隔
Period:用于计算两个日期的间隔

日期的操纵:
TemporalAdjuster:时间校正器。比如将时间调整到下个周日的操作。
TemporalAdjusters:该类的静态方法提供了大量的常用TemporalAdjuster的实现。
LocalDate nextSunday = LocalDate.now().with(
    TemporalAdjusters.next(DayOfWeek.SUNDAY)
);

解析与格式化

java.time.format.DateTimeFormatter 类  三种格式化方法
  • 预定义标准格式
  • 语言环境相关格式
  • 自定义格式
时区的处理
ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式 例如:Asia/Shanghai 
ZoneId:该类中包含了所有的时区信息
getAvailableZoneIds():可以获取所有时区信息
of(id):用指定的时区信息获取ZoneId对象

Optional

Optional<T>类是一个容器类,代表一个值存在或不存在。比原来的null更好表达的概念,避免空指针异常。

重复注解与类型注解

@Target({TYPE,FIELD,METHOD,PARAMENER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaaations{
    MyAnnotation[] value();
}

@Repeatable(MyAnnotations.class)
@Target({TYPE,FIELD,METHOD,PARAMENTER,CONSTRUCTOR,LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
    String value();
}

@MyAnnotation("Hello")
@MyAnnotation("World")
public void show(@MyAnnotation("abc") String str){
    
}


HashMap的改进

接口的默认方法和静态方法

Java8中允许接口中包含具有具体实现的方法,该方法称之为“默认方法”,使用default关键字修饰
interface MyFunc<T>{
    T func(int a);
    
    default String getName(){  //接口中具体的方法,使用default修饰
        return "Hello java8";
    }
}
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名方法时
  • 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供了一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管是否为默认方法),那么必须覆盖该方法解决冲突。
interface MyFunc{
    default String getName(){
        return "Hello java8";
    }
}

interface Named{
    default String getName(){
        return "Hello atguigu!";
    }
}

class MyClass implements MyFunc,Named{
    public String getName(){
        return Named.super.getName();
    }
}

接口中的静态方法:

interface Named{
    public Integer myFun();
    default String getName(){
        return "Hello atguigu!";
    }
    static void show(){   //接口中定义静态方法
        System.out.println("Hello Lambda");
    }
}



Stream API

处理集合(查找、过滤、映射数据)
流(Stream)用于操作数据源(集合、数组等)所生成的元素序列等

注意:
  • Stream自己不会存储元素
  • Stream不会改变源对象,会返回一个持有结果的新Stream
  • Stream操作是延迟执行的,等到需要结果的时候才去执行。

Steam操作的三个步骤:

  1. 创建:一个数据源(集合、数组)获取一个流
  2. 中间操作:一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果

创建流:

Collection接口  default Stream<E> stream()  返回一个顺序流。default Stream<E> paralleStream() 返回一个并行流。
Arrays静态方法 static <T> Stream<T> stream(T[] array):返回一个流
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)

值创建流Stream.of()可以接收任意数量的参数 public static<T> Stream of(T...values): 返回一个流
函数创建流:Stream.iterate()和Stream.generate()创建无限流。

Stream的中间操作:

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



Stream的终止操作:

终端操作会从流的流水线生成结果,其结果可以是任何不是流的值,例如List 、Integer、甚至是void

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


并行流与串行流

并行流就是把一个内容分成多个数据块,并且不同的线程分别处理每个数据块的流。
Java8中奖并行进行了优化,我们可以很容易的对数据进行操作。
Stream API可以声明性地通过parallel()sequential()在并行流与顺序流之间进行切换。

Fork/Join框架:

将一个大任务进行拆分(fork)成若干个小任务(拆到不能再拆时),再将一个个小任务运算的结果进行join 汇总。

以上框架与传统线程池的区别:
fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能