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<Student> 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<String, Student> map = Maps.uniqueIndex(list, new Function<Student, String>() {
    @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<String> 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));
// 定义接口
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>:接收 T 并返回 boolean
  • Consumer<T>:接收 T,不返回值
  • Function<T, R>:接收 T,返回 R
  • Supplier<T>:提供 T 对象(例如工厂),不接收值
  • UnaryOperator<T>:接收 T 对象,返回 T
  • BinaryOperator<T>:接收两个 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<String> supplier1 = () -> student.getName();
        System.out.println("Lambda形式: "+supplier1.get());

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

    // 2、实例对象名::实例方法名
    public static void method2(){
        //传统的lambda表达式
        Consumer<String> 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<String> 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<String,String> 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<String> 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<Student> list = Arrays.asList(
            new Student("张三", 12),
            new Student("李四", 15),
            new Student("王五", 21)
);
  • map:将List<Student> 转换为List<String>, collect是将结果转换为List
List<String> 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<Integer> integerStream = intStream.boxed();
Optional<Integer> max = integerStream.max(Integer::compareTo);
System.out.println(max.get());
  • flatMap:将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
List<String> 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 <integer>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<Integer> 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    比较两个日期对象是否相等
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • 为什么要学Java8 Java8让你的编程变得更容易 充分稳定的利用计算机硬件资源 Lambda lambda 是...
    李庆雪阅读 4,550评论 0 5
  • JDK8新特性介绍 JDK8新特性:​ 1,Lambda表达式​ 2,新的日期API​ 3,引入Optional​...
    偏偏爱吃梨阅读 691评论 0 2
  • 阅读原文 Chapter 14 . JDK8新特性 14.1 Lambda 表达式 Lambda 是一个匿名函数,...
    GeekGray阅读 995评论 0 10
  • 基础语法 Lambda需要函数式接口支持 接口用@Functiona1Interface修饰,就是函数式接口 La...
    小草丶body阅读 497评论 0 0
  • 当然这篇文章并不时髦,但是我希望记录一些干货,主要涉及常用的 函数式接口、Stream,Collector接口及其...
    苏恨阅读 3,997评论 2 6