JAVA8_新特性简单介绍

一、Lambda 表达式

1.1 基本概念

​ Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

​ Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

​ 使用 Lambda 表达式可以使代码变的更加简洁紧凑。

1.2 语法

(parameters) -> expression
或
(parameters) ->{ statements; }

1.3 重要特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

使用 Lambda 表达式需要注意以下两点:

  • Lambda 表达式主要用来定义行内执行的方法类型接口。
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

1.4 变量作用域

​ lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

​ lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)。

​ 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

二、方法引用

2.1 基本概念

​ 方法引用通过方法的名字来指向一个方法。、

​ 方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

​ 方法引用使用一对冒号 :: ;

2.2 引用方法

  • 构造器引用
    Class::new ,或者更一般的Class<T>::new
  • 静态方法引用
    Class::static_method
  • 特定类的任意对象的方法引用
    Class::method
  • 特定对象的方法引用
    instance::method

三、函数式接口

3.1 基本概念

​ 函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的的接口。

​ 函数式接口可以被隐式转换为lambda表达式。

例:

@FunctionalInterface
interface GreetingService {
    void sayMesage(String message);
}

如果使用lambda表达式来创建一个函数式接口实例,那这个lambda表达式的入参和返回必须符合这个函数式接口中唯一的抽象方法的定义。

3.2 默认方法

​ 简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

​ 我么只需在方法名前面加个 default 关键字即可实现默认方法。

为什么要有这个特性?

​ 首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

3.3 Supplier

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

3.4 Function

@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

3.5 Predicate

@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

3.6 Consumer

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

四、Optional

4.1 基本概念

​ 为了解决NullPointerException问题,减少代码中的判空,实现函数式编程,给工程师们提供函数式的API。

​ Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

​ Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

​ Optional 类的引入很好的解决空指针异常。

4.2 类方法

修饰符和类型 方法 描述
static<T> Optional<T> empty() 返回空的Optional实例
boolean equals(Object obj) 判断其他对象是否等于Optional
int hashCode() 返回存在值的哈希码,如果值不存在 返回 0
static <T> Optional<T> of(T value) 返回一个指定非null值的Optional
static <T> Optional<T> ofNullable(T value) 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional
T orElse(T other) 如果存在该值,返回值, 否则返回 other
T orElseGet(Supplier<? extends T> other) 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果
boolean isPresent() 如果值存在则方法会返回true,否则返回 false
T get() 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
String toString() 返回一个Optional的非空字符串,用来调试
Optional<T> filter(Predicate<? super <T> predicate) 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional
Optional<U> flatMap(Function<? super T, Optional<U>> mapper) 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情
Optional<U> map(Function<? super T,? extends U> mapper) 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常

五、Stream

5.1 基本概念

​ Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

​ Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

​ 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

​ 元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

流操作由3部分组成:

  1. 创建流
  2. 零个或多个中间操作
  3. 终止操作(到这一步才会执行整个stream pipeline计算)

5.2 什么是stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

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

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

5.3 创建流的方式:

  1. 通过Stream接口的of静态方法创建一个流

    Stream<String> stream1 = Stream.of("a", "b", "c");
    
  2. 创建一个空的流

    Stream<Object> empty = Stream.empty();
    
  3. 通过builder创建

    Stream<Object> build = Stream.builder().add("a").add("b").add("c").build();
    
  4. 通过Arrays类的stream方法,实际上第一种of方法底层也是调用的Arrays.stream(values)

    String[] array = new String[]{"hello","world","helloworld"};
    Stream<String> stream3 = Arrays.stream(array);
    
  5. 通过集合的stream方法,该方法是Collection接口的默认方法,所有集合都继承了该方法

    Stream<String> stream2 = Arrays.asList("hello","world","helloworld").stream();
    
  6. 合并多个Stream

    Stream stream = Stream.concat(stream1,stream2);
    
  7. generate()和iterate()

    两个都是生成一个无限的流,通常跟limit()一起使用,限制流中元素的个数。

    不同的是前者可以根据任何计算方式来生成,后者只能根据给定的seed来生成。

    Stream<T> generate(Supplier<T> s):
    Stream.generate(UUID.randomUUID()::toString).limit(10).forEach(System.out::println);
    Stream<T> iterate(final T seed, final UnaryOperator<T> f):
    //从1开始,每个元素比前一个元素大2,最多生成10个元素
    Stream.iterate(1,item -> item + 2).limit(10).forEach(System.out::println);
    

集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。

5.4 基本方法

5.4.1 中间处理

  • 筛选
    接收一个lambda表达式,过滤掉某些元素,仅留下符合要求的元素。
    filter(d -> true)

    Stream<Integer> stream = Stream.of(1, 2, -1, 0, 3, -2);
    stream.filter(value -> value > 0).forEach(System.out::print);
    
  • 截断
    仅保留流中的前n个元素。由于中间处理是惰性的,所以limit在某些情况下可以很大的提升处理速度。
    limit(5)

    Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
    stream.limit(5).forEach(System.out::print);
    
  • 舍弃
    舍弃流中的前n个元素,仅保留第n+1个及其之后的元素。

    skip(5)

    Stream<Integer> stream = Stream.iterate(1, 2, 3, 4, 5, 6, 7);
    stream.skip(3).forEach(System.out::print);
    
  • 去重
    去掉Stream中重复的元素,它使用hashCode和equals方法来判断元素是否相等。

    distinct()

    Stream<String> stream = Stream.of("a", "b", "c", "b", "c");
    stream.distinct().forEach(System.out::print);
    
  • 排序
    对Stream中的元素进行排序。

    sorted()
    sorted(Comparator.comparingInt(v -> v))sorted((v1, v2) -> v2 - v1)

    Stream<Integer> stream = Stream.of(2, 4, 1, 5, 3);
    stream.sorted().forEach(System.out::print);
    stream.sorted(Comparator.reverseOrder()).forEach(System.out::print);
    
  • 映射
    通过Lambda表达式,将每一个元素一一映射为一个新的元素。

    map(v -> v + 55)

    Stream<Integer> stream = Stream.of(2, 4, 1, 5, 3);
    stream.map(v -> v + 55).forEach(System.out::println);
    
  • 扁平化映射
    通过Lambda表达式,将每一个元素一一映射为一个新的Stream后,将新的Stream全部连起来。
    flatMap(theList -> theList.stream())

    Stream<String> stream = Stream.of("abc", "def", "ghi");
    stream.flatMap(str -> str.chars().boxed()).forEach(System.out::print);
    

5.4.2 结束处理

  • 迭代
    对Stream内的每一个元素进行循环处理。

    forEach(System.out::println)

    Stream<String> stream = Stream.of("a", "b", "c");
    stream.forEach(System.out::print);
    

    匹配
    判断Stream中的元素是否匹配某条件,返回boolean结果。

    allMatch:Stream中是否所有元素都匹配
    allMatch(v -> true)

    anyMatch:Stream中是否有任一元素匹配
    noneMatch(v -> true)

    noneMatch:Stream中是否所有元素都不匹配
    anyMatch(v -> true)

    Stream<String> stream = Stream.of(1, 2, 4, 0, -3, -5);
    stream.allMatch(value -> value > 0);            // 返回false
    stream.anyMatch(value -> value > 0);          // 返回true
    stream.noneMatch(value -> value > 0);       // 返回false
    
  • 查找

    查找Stream中的一个元素,返回Optional类型。一般与filter等一起使用。
    findFirst:查找第一个元素
    findAny:查找任一个元素。在并行流(parallelStream)中性能提升比较明显。

    findFirst().orElse(0)
    findAny().orElse(0)

    Stream<String> stream = Stream.of(1, 2, 4, 0, -3, -5);
    System.out.print(stream.findFirst().orElse(0));
    System.out.print(stream.findAny().orElse(0));
    
  • 统计
    count:统计Stream中元素的个数。

    min:获取Stream中的最小元素。
    max:获取Stream中的最大元素。
    count()
    min(Comparator.comparing(v -> v))mapToInt(v -> v + 3).min().orElse(0)
    max(Comparator.comparing(v -> v))mapToInt(v -> v + 3).max().orElse(0)

    System.out.println(Stream.of(1, 2, 4, 0, -3, -5).count());
    System.out.println(Stream.of(1, 2, 4, 0, -3, -5).min(Comparator.comparing(v -> v)).orElse(0));
    System.out.println(Stream.of(1, 2, 4, 0, -3, -5).max((v1, v2) -> v1.compareTo(v2)).orElse(0));
    
  • 规约

    将Stream中的每一个元素进行指定的叠加处理,最终生成一个值。
    reduce(BigDecimal.ZERO, BigDecimal::add).get()

    Stream<String> stream = Stream.of("abc", "def", "ghi");
    System.out.println(stream.reduce((s, s2) -> s + ", " + s2).get());
    
  • 收集
    将Stream收集成各种形式。主要利用Collectors中的静态方法来实现。
    collect(Collectors.toList())

    List<String>list = Stream.of("abc", "def", "ghi").collect(Collectors.toList());
    Set<String>set = Stream.of("abc", "def", "ghi").collect(Collectors.toSet());
    LinkedList<String>list = Stream.of("abc", "def", "ghi").collect(Collectors.toCollection(LinkedList::new));
    

    collect(Collectors.toMap(v -> v, v -> v.toUpperCase())

    Map<String, String> map = Stream.of("abc", "def", "ghi").collect(Collectors.toMap(v -> v, v -> v.toUpperCase()));
    
  • 计数
    collect(Collectors.counting())

    long count = Stream.of(1, 2, 4, 0, -3, -5).collect(Collectors.counting());
    
  • 平均
    collect(Collectors.averagingDouble(v -> v))
    collect(Collectors.averagingInt(v -> v))
    collect(Collectors.averagingLong(v -> v))

    double average = Stream.of(1, 2, 4, 0, -3, -5).collect(Collectors.averagingInt(v -> v));
    
  • 最小值
    collect(Collectors.minBy(Integer::compare)))

    Optional<Integer> min = Stream.of(1, 2, 4, 0, -3, -5).collect(Collectors.minBy(Integer::compare));
    
  • 最大值
    collect(Collectors.maxBy(Integer::compare)))

    Optional<Integer> max = Stream.of(1, 2, 4, 0, -3, -5).collect(Collectors.maxBy(Integer::compare));
    
  • 合计
    collect(Collectors.summingInt(v -> v))

  • 分组
    collect(Collectors.groupingBy(v -> v.equals("11")))

    Stream<Goods> goodsStream = Stream.of(new Goods("A", 18), new Goods("A", 15), new Goods("B", 5), new Goods("B", 20));
    Map<String, List<Goods>> groupedGoods = goodsStream.collect(Collectors.groupingBy(Goods::getGoodsName));
    Map<String, Map<String, List<Goods>>> groupedGoods = goodsStream.collect(Collectors.groupingBy(Goods::getGoodsType, Collectors.groupingBy(Goods::getGoodsName)));  
    
  • 分组合计
    collect(Collectors.groupingBy(v -> v, Collectors.summarizingInt(v -> v)))

    Map<String, Double> groupedGoods = Stream.of(new Goods("A", 18), new Goods("A", 15), new Goods("B", 5), new Goods("B", 20)).collect(Collectors.groupingBy(Goods::getGoodsType, Collectors.summingDouble(Goods::getPrice))); 
    
  • 分区
    collect(Collectors.partitioningBy(g -> g.getPrice() > 15))

    Map<Boolean, List<Goods>> groupedGoods = Stream.of(new Goods("A", 18), new Goods("A", 15), new Goods("B", 5), new Goods("B", 20)).collect(Collectors.partitioningBy(g -> g.getPrice() > 15)); 
    
  • 分区合计
    collect(Collectors.partitioningBy(g -> g.getPrice() > 15, Collectors.summingDouble(d -> d.get("quantity")))

    Map<Boolean, Double> groupedGoods = Stream.of(new Goods("A", 18), new Goods("A", 15), new Goods("B", 5), new Goods("B", 20)).collect(Collectors.partitioningBy(g -> g.getPrice() > 15, , Collectors.summingDouble(Goods::getPrice)));
    

六、日期时间API

6.1 基本概念

旧版Java中,日期时间API存在的问题:

  • 非线程安全
    java.util.Date是非线程安全的,所有的日期类都是可变的
  • 设计很差
    Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。
    java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。
    另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  • 时区处理麻烦

Java8在java.time中提供的API:

  • Local(本地)
    简化了日期时间的处理,没有时区的问题
  • Zoned(时区)
    通过制定的时区处理日期时间

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

6.2 关键类

java.time包里有许多可以代表时间和日期的类。

  • Instant类,提供了一个机器视角的时间线。
  • LocalDate, LocalTimeLocalDateTime类提供了人类视角的日期和时间,不涉及到时区。
  • ZoneId, ZoneRulesZoneOffset类描述了时区,时区偏移量和时区规则。
  • ZonedDateTime类,代表了与时区关联的时间和日期。OffsetDateTimeOffsetTime分别代表了日期和时间和时间。这些类描述了时区偏移。
  • Duration类在秒和毫秒尺度丈量一段时间。
  • Period类在年、月和日尺度上丈量一段时间。

6.3 常用的API

  • 获取当前日期时间

    LocalDate date = LocalDate.now();
    LocalTime time = LocalTime.now();
    LocalDateTime dateTime = LocalDateTime.now().withNano(0);
    
  • 获取指定日期时间

    LocalDate specDatefromString = LocalDate.parse(“2014-12-12”);
    LocalDate specDate = LocalDate.of(2014, 2, 20);
    specDate = LocalDate.ofYearDay(2015, 100);
    specDate = LocalDate.ofEpochDay(200);//自1970年1月1日起200天后的日期
    
  • 获取今天是今年的第几天

    int dayOfYear = LocalDate.now().getDayOfYear();
    
  • 当前月的最后一天

    LocalDate date = LocalDate.now();
    // 不用考虑是28、29、30还是31天
    LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
    
  • 2015年11月第一个周一

    LocalDate firstMondayInOneMonth = LocalDate.parse("2015-11-11")
          .with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
    
  • 日期加减与时间间隔

    LocalTime time = LocalTime.now();
    
    // 当前时间加20分钟
    LocalTime timeAfterPlus = time.plusMinutes(20);
    // 当前时间减2小时
    LocalTime timeAfterMinus = time.minusHours(2);
    
    // 两个时间间隔(单位:分钟),如第二个参数比第一个大,结果为负数
    long duration = ChronoUnit.MINUTES.between(time, timeAfterPlus);
    
  • 获取两个日期间的距离

    LocalDate date = LocalDate.now();
    Period period = Period.between(LocalDate.of(2014, 2, 10), date);
    
  • 日期判断

    LocalDate date = LocalDate.now();
    LocalDate date1 = LocalDate.now();
    
    // 判断是否相等
    boolean isEqual = date.equals(date1);
    // 判断是否在另一个日期之前
    boolean isBefore = date.isBefore(date1);
    // 判断是否为闰年
    boolean isLeapYear = date.isLeapYear();
    
  • 查看时区

    // 获得所有时区
    Set<String> allZone = ZoneId.getAvailableZoneIds();
    ZoneId zone = ZoneId.systemDefault();
    // 获得美国时间
    ZoneId zoneInUSA = ZoneId.of("America/New_York");
    LocalTime timeInUSA = LocalTime.now(zoneInUSA);
    
  • 时间戳转换为日期

    Instant second = Instant.ofEpochSecond(1234567890L);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”);
    
    // 等同于 formatter.format(LocalDateTime.ofInstant(second, ZoneId.systemDefault()));
    String time = LocalDateTime.ofInstant(second, ZoneId.systemDefault()).format(formatter);
    
    // 另一种方法
    LocalDateTime now = LocalDateTime.ofEpochSecond(12468312, 0, ZoneOffset.of("+8"));
    
  • 日期转换为时间戳

    // 第一种方式
    Instant instant1 = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
    // 第二种方式
    OffsetDateTime offsetTime = OffsetDateTime.now(ZoneId.systemDefault());
    ZoneOffset offset = offsetTime.getOffset();
    Instant instant2 = LocalDateTime.now().toInstant(offset);
    // 获得绝对秒
    long millisecond = instant1.getEpochSecond();
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 为什么使用 Lambda 表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传...
    强某某阅读 14,784评论 0 15
  • 目录结构 介绍 Java语言的新特性2.1 Lambdas表达式与Functional接口2.2 接口的默认与静态...
    夜风月圆阅读 459评论 0 2
  • 前言:Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级。在Java Code Geek...
    糖宝_阅读 1,321评论 1 1
  • Java 8自Java 5(发行于2004)以来最具革命性的版本。Java 8 为Java语言、编译器、类库、开发...
    谁在烽烟彼岸阅读 884评论 0 4
  • 一、重要数据结构和JVM的改动 1.HashMap的改动 HashMap维护了一个Entry数组,put(K ke...
    一只爱java的猫阅读 467评论 0 0