JDK8新特性

一、Lambda表达式
二、方法调用(双冒号)的用法
三、Stream函数式操作流元素集合
四 、接口新增:默认方法与静态方法
五、最新的Date/Time API

一、Lambda表达式

1、语法:完整的Lambda表达式由三部分组成:参数列表、箭头、声明语句

(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM;}

2、绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型,所以参数可以省略

(param1,param2, ..., paramN) -> { statment1; statment2; //............. return statmentM;}

3、当lambda表达式的参数个数只有一个,可以省略小括号

param1 -> { statment1; statment2; //............. return statmentM;}

4、当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号

param1 -> statment

展示例子:

// bean类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    //名字
    private String name;
    //性别
    private String sex;
    //薪水
    private int salary;
    //年龄
    private int age;
    //星座
    private String star;

    public static void printStuMethod(Student stu) {
        System.out.println("get student, name is: " + stu.getName());
    }
}

// 构造bean list对象
List list = Arrays.asList(
    new Student("张三","男",5000,18,"天秤座"),
    new Student("李四","男",4000,16,"双鱼座"),
    new Student("王五","男",3000,24,"水瓶座")
);
  • list的遍历
// 标准写法
list.stream().forEach((Student x) -> {System.out.println(x);});
// 简略写法
list.stream().forEach(x -> System.out.println(x));
// 双冒号简略写法
list.stream().forEach(System.out::println);
// 双冒号调用类内部静态方法
list.stream().forEach(Student::printStuMethod);
// 结合stream的其他方法
list.stream().filter(e -> e.getStar().equals("天秤座")).forEach(System.out::println);
  • map的遍历
// 构造map对象
Map map = Maps.uniqueIndex(list, new Function() {
    @Override
    public String apply(Student person) {
        return person.getName();
    }
});
// 输出方法
public static void printMap(String key, Student value){
    System.out.println(key + " " + value);
}

// 标准写法输出
map.forEach( (k,v) -> System.out.println(k+" "+v) );
// 调用内部方法输出
map.forEach( (k,v) -> printMap(k, v) );
  • set的遍历
Set sets = Sets.newHashSet("1", "2", "3");
// 输出方法
public static void printSet(String set){
    System.out.println(set);
}
//  写法一
sets.stream().forEach(System.out::println);
// 写法二,调用内部方法
sets.stream().forEach(x -> printSet(x));
  • 带有返回值的 lambda 用法
    标准实例
// 定义接口
public interface Calculator {
    //定义一个计算两个int整数和的方法并返回结果
    public abstract int calc(int a,int b);
}
// 调用
public class DemoCalculator {
    public static void main(String[] args) {
        //调用invokeCalc方法,方法的参数是一个接口可以使用匿名内部类
        invokeCalc(5, 10, new Calculator() {
            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        });
 
        //使用Lambda表达式简化匿名内部类的书写
        invokeCalc(10,20,(int a,int b)->{
            return a + b;
        });
 
        //优化省略Lambda表达式
        invokeCalc(10,20,(a,b)-> a + b);
    }
 
    public static void invokeCalc(int a,int b,Calculator c){
        int sum = c.calc(a,b);
        System.out.println(sum);
    }
}

二、方法调用(双冒号)的用法

方法调用实例

方法调用的五种形式

  • 对象实例::实例方法名
  • 对象实例名::实例方法名
  • 类名::静态方法名
  • 类名::实例方法名
  • 类名::new

JDK8中增加了一个新的包:java.util.function,它里面包含了常用的函数式接口,例如:

  • Predicate:接收 T 并返回 boolean
  • Consumer:接收 T,不返回值
  • Function:接收 T,返回 R
  • Supplier:提供 T 对象(例如工厂),不接收值
  • UnaryOperator:接收 T 对象,返回 T
  • BinaryOperator:接收两个 T,返回 T
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class MethodReference {

    public MethodReference(String s) {
        System.out.println("print ==> " + s);
    }

    public static void main(String[] args) {
        method1();
        method2();
        method3();
        method4();
        method5();
    }

    // 1、实例对象::实例方法名
    public static void method1(){
        Student student = new Student("ZhangSan",23);

        Supplier supplier1 = () -> student.getName();
        System.out.println("Lambda形式: "+supplier1.get());

        Supplier supplier2 = student::getName ;
        System.out.println("方法引用形式: "+supplier2.get());
    }

    // 2、实例对象名::实例方法名
    public static void method2(){
        //传统的lambda表达式
        Consumer consumer = (x) -> System.out.println(x);
        consumer.accept("Hi: 我是Lambda表达式实现的!"); //打印:Hi: 我是Lambda表达式实现的!

        //方法引用实现
        consumer = System.out::println;
        consumer.accept("Hello : ZhangSan,我是使用方法引用实现的 ");
    }

    // 3、类名::静态方法名
    public static void method3(){
        //传统的lambda表达式
        Consumer consumer = (str) -> MethodReference.sayName(str);
        consumer.accept("Hello : XiangYang");

        //方法引用实现
        consumer = MethodReference::sayName;
        consumer.accept("Hello : XiangYang");
    }

    public static void sayName(String name){
        System.out.println(name);
    }

    // 4、类名::实例方法名
    // 参数列表的第一个参数是实例方法的调用者, 第n>1个参数是实例方法的参数时进行使用
    public static void method4(){
        BiPredicate biPredicate = (x , y) -> x.equals(y);
        boolean test = biPredicate.test("hello","hi");
        System.out.println(test);

        biPredicate = String::equals;
        test = biPredicate.test("hello","hello");
        System.out.println(test);
    }

    // 5、类名::new
    public static void method5(){

        Consumer consumer = MethodReference::new;
        consumer.accept("hello");
    }
}

三、Stream函数式操作流元素集合

父类:BasicStream
子类:Stream、IntStream、LongStream、DoubleStream
包含两个类型,中间操作(intermediate operations)和结束操作(terminal operations)
下面是所有方法的属于那一端操作的方法:

stream方法汇总

下面测试一下map、flatmap、reduce三个方法,其它方法的用法请参考:
https://my.oschina.net/mdxlcj/blog/1622718

先准备一个list

public static List list = Arrays.asList(
            new Student("张三", 12),
            new Student("李四", 15),
            new Student("王五", 21)
);
  • map:将List 转换为List, collect是将结果转换为List
List names = list.stream().map(Student::getName).collect(Collectors.toList());
names.stream().forEach(System.out::println);
  • mapToInt:转换数值流,等同mapToLong、mapToDouble,如下这个是取最大值
IntStream intStream = list.stream().mapToInt(Student::getAge);
Stream integerStream = intStream.boxed();
Optional max = integerStream.max(Integer::compareTo);
System.out.println(max.get());
  • flatMap:将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
List list2 = new ArrayList<>();
list2.add("aaa bbb ccc");
list2.add("ddd eee fff");
list2.add("ggg hhh iii");
list2 = list2.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
System.out.println(list2);
  • reduce reduce(accumulator) :参数是一个执行双目运算的 Functional Interface,假如这个参数表示的操作为 op,stream 中的元素为 x, y, z, …,则 reduce() 执行的就是 x op y op z ...,所以要求 op 这个操作具有结合性(associative),即满足: (x op y) op z = x op (y op z),满足这个要求的操作主要有:求和、求积、求最大值、求最小值、字符串连接、集合并集和交集等。另外,该函数的返回值是 Optional 的:
Optional sum1 = numStream.reduce((x, y) -> x + y);
  • reduce(identity, accumulator) :可以认为第一个参数为默认值,但需要满足 identity op x = x,所以对于求和操作,identity 的值为 0,对于求积操作,identity 的值为 1。返回值类型是 stream 元素的类型:
Integer sum2 = numStream.reduce(0, Integer::sum);
  • reduce 如果不加参数identity则返回的是 optional 类型的,reduce 在进行双目运算时,其中一个场景是与identity做比较操作,因此我们应该满足identity op x = x
Stream stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8});

//求集合元素之和
Integer result = stream.reduce(0, Integer::sum);
System.out.println(result);

//求和
stream.reduce((i, j) -> i + j).ifPresent(System.out::println);

//求最大值
stream.reduce(Integer::max).ifPresent(System.out::println);

//求最小值
stream.reduce(Integer::min).ifPresent(System.out::println);

//做逻辑
stream.reduce((i, j) -> i > j ? j : i).ifPresent(System.out::println);

//求逻辑求乘积
int result2 = stream.filter(i -> i % 2 == 0).reduce(1, (i, j) -> i * j);

Optional.of(result2).ifPresent(System.out::println);

四 、接口新增:默认方法与静态方法

在JDK8之前,Interface之中是可以定义变量和方法的,变量必须是public static final 的 , 方法必须是public abstract的 , 由于这些修饰符是默认的,所以在JDK8之前,下面写法是等价的

public interface JDK8BeforeInterface {

    public static final int field1 = 0;

    int field2 = 0;

    public abstract void m1(int a) throws Exception ;

    void m2(int a) throws Exception;

}

JDK8及以后,允许我们在接口中定义static方法和default方法。

public interface  JDK8Interface {

    // static修饰符定义静态方法
    static void staticMethod() {
        System.out.println("接口中定义静态方法");
    }
    // default修饰符定义默认方法
    default void defaultMethod() {
        System.out.println("接口中的默认方法");
    }
}

再定义一个接口的实现类:

public class JDK8InterfaceImpl implements JDK8Interface ,JDK8Interface1 {
    //实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写
}

静态方法,只能通过接口名调用,不可以通过实现类的类名或者实现类的对象调用。default方法,只能通过接口实现类的对象来调用。

public class Main {
    public static void main(String[] args) {
        // static方法必须通过接口类调用
        JDK8Interface.staticMethod();
        //default方法必须通过实现类的对象调用
        new JDK8InterfaceImpl().defaultMethod();
    }
}

当然如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。

public class AnotherJDK8InterfaceImpl implements JDK8Interface {
    @Override
    public void defaultMethod() {
        System.out.println("接口实现类覆盖了接口中的default");
    }
}

五、最新的Date/Time API

最新日期api用法参考

新的时间及日期API位于java.time中,它们都是不可变且线程安全的

属性 含义
Instant 代表的是时间戳
LocalDate 代表日期,比如2020-01-14
LocalTime 代表时刻,比如12:59:59
LocalDateTime 代表具体时间 2020-01-12 12:22:26
ZonedDateTime 代表一个包含时区的完整的日期时间,偏移量是以UTC/ 格林威治时间为基准的
Period 代表时间段
ZoneOffset 代表时区偏移量,比如:+8:00
Clock 代表时钟,比如获取目前美国纽约的时间

1、获取当前时间

Instant instant = Instant.now(); //获取当前时间戳
System.out.println("instant ==> " + instant);
//instant ==> 2020-02-01T12:09:28.772Z

LocalDate localDate = LocalDate.now();  //获取当前日期
System.out.println("localDate ==> " + localDate);
//localDate ==> 2020-02-01

LocalTime localTime = LocalTime.now();  //获取当前时刻
System.out.println("localTime ==> " + localTime);
//localTime ==> 20:09:28.914

LocalDateTime localDateTime = LocalDateTime.now();  //获取当前具体时间
System.out.println("localDateTime ==> " + localDateTime);
//localDateTime ==> 2020-02-01T20:09:28.914

ZonedDateTime zonedDateTime = ZonedDateTime.now();   //获取带有时区的时间
System.out.println("zonedDateTime ==> " + zonedDateTime);
//zonedDateTime ==> 2020-02-01T20:09:28.916+08:00[Asia/Shanghai]

2、字符串转日期

String date = "2020-01-10";
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate localDate = LocalDate.parse(date, formatter1);
System.out.println("localDate ==> " + localDate);

3、日期转字符串

Date dd = new Date();
LocalDateTime ldt = dd.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(ldt);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format = ldt.format(dtf);
System.out.println(format);

4、Date转换LocalDate

Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();

// atZone()方法返回在指定时区从此Instant生成的ZonedDateTime。
LocalDate localDate = instant.atZone(zoneId).toLocalDate();
System.out.println("Date = " + date);
System.out.println("LocalDate = " + localDate);

5、LocalDate转Date

ZoneId zoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now();
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

Date date = Date.from(zdt.toInstant());

System.out.println("LocalDate = " + localDate);
System.out.println("Date = " + date);

6、时间戳转LocalDateTime

long timestamp = System.currentTimeMillis();

Instant instant = Instant.ofEpochMilli(timestamp);

LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

7、LocalDateTime转时间戳

LocalDateTime dateTime = LocalDateTime.now();

dateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();

dateTime.toInstant(ZoneOffset.of("+08:00")).toEpochMilli();

dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

8、LocalDate方法总结

getYear()                         int        获取当前日期的年份
getMonth()                        Month      获取当前日期的月份对象
getMonthValue()                   int        获取当前日期是第几月
getDayOfWeek()                    DayOfWeek  表示该对象表示的日期是星期几
getDayOfMonth()                   int        表示该对象表示的日期是这个月第几天
getDayOfYear()                    int        表示该对象表示的日期是今年第几天
withYear(int year)                LocalDate  修改当前对象的年份
withMonth(int month)              LocalDate  修改当前对象的月份
withDayOfMonth(intdayOfMonth)     LocalDate  修改当前对象在当月的日期
isLeapYear()                      boolean    是否是闰年
lengthOfMonth()                   int        这个月有多少天
lengthOfYear()                    int        该对象表示的年份有多少天(365或者366)
plusYears(longyearsToAdd)         LocalDate  当前对象增加指定的年份数
plusMonths(longmonthsToAdd)       LocalDate  当前对象增加指定的月份数
plusWeeks(longweeksToAdd)         LocalDate  当前对象增加指定的周数
plusDays(longdaysToAdd)           LocalDate  当前对象增加指定的天数
minusYears(longyearsToSubtract)   LocalDate  当前对象减去指定的年数
minusMonths(longmonthsToSubtract) LocalDate  当前对象减去注定的月数
minusWeeks(longweeksToSubtract)   LocalDate  当前对象减去指定的周数
minusDays(longdaysToSubtract)     LocalDate  当前对象减去指定的天数
compareTo(ChronoLocalDateother)   int        比较当前对象和other对象在时间上的大小,返回值如果为正,则当前对象时间较晚,
isBefore(ChronoLocalDateother)    boolean    比较当前对象日期是否在other对象日期之前
isAfter(ChronoLocalDateother)     boolean    比较当前对象日期是否在other对象日期之后
isEqual(ChronoLocalDateother)     boolean    比较两个日期对象是否相等

你可能感兴趣的:(JDK8新特性)