Java8新特性都到碗里來(lái)

原創(chuàng)文章&經(jīng)驗(yàn)總結(jié)&從校招到A廠一路陽(yáng)光一路滄桑

詳情請(qǐng)戳www.codercc.com

image

對(duì)于Java開發(fā)者來(lái)說(shuō),Java8的版本顯然是一個(gè)具有里程碑意義的版本,蘊(yùn)含了許多令人激動(dòng)的新特性,如果能利用好這些新特性掏呼,能夠大大提升我們的開發(fā)效率筷黔。Java8的函數(shù)式編程能夠大大減少代碼量和便于維護(hù),同時(shí)绰播,還有一些跟并發(fā)相關(guān)的功能。開發(fā)中常用到的新特性如下:

  1. 接口的默認(rèn)方法和靜態(tài)方法
  2. 函數(shù)式接口FunctionInterface與lambda表達(dá)式
  3. 方法引用
  4. Stream
  5. Optional
  6. Date/time API的改進(jìn)
  7. 其他改進(jìn)

1. 接口的默認(rèn)方法和靜態(tài)方法

在Java8之前尚困,接口中只能包含抽象方法蠢箩。那么這有什么樣弊端呢?比如事甜,想再Collection接口中添加一個(gè)spliterator抽象方法谬泌,那么也就意味著之前所有實(shí)現(xiàn)Collection接口的實(shí)現(xiàn)類,都要重新實(shí)現(xiàn)spliterator這個(gè)方法才行。而接口的默認(rèn)方法就是為了解決接口的修改與接口實(shí)現(xiàn)類不兼容的問題,作為代碼向前兼容的一個(gè)方法仪缸。

那么如何在接口中定義一個(gè)默認(rèn)方法呢妓蛮?來(lái)看下JDK中Collection中如何定義spliterator方法的:

default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}

可以看到定義接口的默認(rèn)方法是通過(guò)default關(guān)鍵字。因此窍侧,在Java8中接口能夠包含抽象方法外還能夠包含若干個(gè)默認(rèn)方法(即有完整邏輯的實(shí)例方法)。

public interface IAnimal {
    default void breath(){
        System.out.println("breath!");
    };
}


public class DefaultMethodTest implements IAnimal {
    public static void main(String[] args) {
        DefaultMethodTest defaultMethod = new DefaultMethodTest();
        defaultMethod.breath();
    }

}


輸出結(jié)果為:breath!

可以看出IAnimal接口中有由default定義的默認(rèn)方法后,那么其實(shí)現(xiàn)類DefaultMethodTest也同樣能夠擁有實(shí)例方法breath邻悬。但是如果一個(gè)類繼承多個(gè)接口,多個(gè)接口中有相同的方法就會(huì)產(chǎn)生沖突該如何解決随闽?實(shí)際上默認(rèn)方法的改進(jìn)父丰,使得java類能夠擁有類似多繼承的能力,即一個(gè)對(duì)象實(shí)例掘宪,將擁有多個(gè)接口的實(shí)例方法蛾扇,自然而然也會(huì)存在方法重復(fù)沖突的問題攘烛。

下面來(lái)看一個(gè)例子:

public interface IDonkey{
    default void run() {
        System.out.println("IDonkey run");
    }
}

public interface IHorse {

    default void run(){
        System.out.println("Horse run");
    }

}

public class DefaultMethodTest implements IDonkey,IHorse {
    public static void main(String[] args) {
        DefaultMethodTest defaultMethod = new DefaultMethodTest();
        defaultMethod.breath();
    }

}

定義兩個(gè)接口:IDonkey和IHorse,這兩個(gè)接口中都有相同的run方法镀首。DefaultMethodTest實(shí)現(xiàn)了這兩個(gè)接口医寿,由于這兩個(gè)接口有相同的方法,因此就會(huì)產(chǎn)生沖突蘑斧,不知道以哪個(gè)接口中的run方法為準(zhǔn)靖秩,編譯會(huì)出錯(cuò):inherits unrelated defaults for run.....

解決方法

針對(duì)由默認(rèn)方法引起的方法沖突問題,只有通過(guò)重寫沖突方法竖瘾,并方法綁定的方式沟突,指定以哪個(gè)接口中的默認(rèn)方法為準(zhǔn)

public class DefaultMethodTest implements IAnimal,IDonkey,IHorse {
    public static void main(String[] args) {
        DefaultMethodTest defaultMethod = new DefaultMethodTest();
        defaultMethod.run();
    }

    @Override
    public void run() {
        IHorse.super.run();
    }
}

DefaultMethodTest重寫了run方法捕传,并通過(guò) IHorse.super.run();指定以IHorse中的run方法為準(zhǔn)惠拭。

靜態(tài)方法

在Java8中還有一個(gè)特性就是,接口中還可以聲明靜態(tài)方法庸论,如下例:

public interface IAnimal {
    default void breath(){
        System.out.println("breath!");
    }
    static void run(){}
}

2.函數(shù)式接口FunctionInterface與lambda表達(dá)式

函數(shù)式接口

Java8最大的變化是引入了函數(shù)式思想职辅,也就是說(shuō)函數(shù)可以作為另一個(gè)函數(shù)的參數(shù)。函數(shù)式接口聂示,要求接口中有且僅有一個(gè)抽象方法域携,因此經(jīng)常使用的Runnable,Callable接口就是典型的函數(shù)式接口鱼喉⌒惚蓿可以使用@FunctionalInterface注解,聲明一個(gè)接口是函數(shù)式接口扛禽。如果一個(gè)接口滿足函數(shù)式接口的定義锋边,會(huì)默認(rèn)轉(zhuǎn)換成函數(shù)式接口。但是编曼,最好是使用@FunctionalInterface注解顯式聲明豆巨。這是因?yàn)楹瘮?shù)式接口比較脆弱,如果開發(fā)人員無(wú)意間新增了其他方法掐场,就破壞了函數(shù)式接口的要求往扔,如果使用注解@FunctionalInterface,開發(fā)人員就會(huì)知道當(dāng)前接口是函數(shù)式接口刻肄,就不會(huì)無(wú)意間破壞該接口瓤球。下面舉一個(gè)例子:

@java.lang.FunctionalInterface
public interface FunctionalInterface {
    void handle();
}

該接口只有一個(gè)抽象方法,并且使用注解顯式聲明敏弃。但是卦羡,函數(shù)式接口要求只有一個(gè)抽象方法卻可以擁有若干個(gè)默認(rèn)方法的(實(shí)例方法),比如下例:

@java.lang.FunctionalInterface
public interface FunctionalInterface {
    void handle();

    default void run() {
        System.out.println("run");
    }
}

該接口中,除了有抽象方法handle外绿饵,還有默認(rèn)方法(實(shí)例方法)run欠肾。另外,任何被Object實(shí)現(xiàn)的方法都不能當(dāng)做是抽象方法拟赊。

lambda表達(dá)式

lambda表達(dá)式是函數(shù)式編程的核心刺桃,lambda表達(dá)式即匿名函數(shù),是一段沒有函數(shù)名的函數(shù)體吸祟,可以作為參數(shù)直接傳遞給相關(guān)的調(diào)用者瑟慈。lambda表達(dá)式極大的增加了Java語(yǔ)言的表達(dá)能力。lambda的語(yǔ)法結(jié)構(gòu)為:

(parameters) -> expression
或
(parameters) ->{ statements; }
  • 可選類型聲明:不需要聲明參數(shù)類型屋匕,編譯器可以統(tǒng)一識(shí)別參數(shù)值葛碧。
  • 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)过吻。
  • 可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句进泼,就不需要使用大括號(hào)。
  • 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值纤虽,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值乳绕。

完整示例為(摘自菜鳥

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 類型聲明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用類型聲明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括號(hào)中的返回語(yǔ)句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 沒有大括號(hào)及返回語(yǔ)句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括號(hào)
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括號(hào)
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

另外,lambda還可以訪問外部局部變量逼纸,如下例所示:

int adder = 5;
Arrays.asList(1, 2, 3, 4, 5).forEach(e -> System.out.println(e + adder));

實(shí)際上在lambda中訪問類的成員變量或者局部變量時(shí)洋措,會(huì)隱式轉(zhuǎn)換成final類型變量,所以上例實(shí)際上等價(jià)于:

final int adder = 5;
Arrays.asList(1, 2, 3, 4, 5).forEach(e -> System.out.println(e + adder));

3. 方法引用

方法引用是為了進(jìn)一步簡(jiǎn)化lambda表達(dá)式樊展,通過(guò)類名或者實(shí)例名與方法名的組合來(lái)直接訪問到類或者實(shí)例已經(jīng)存在的方法或者構(gòu)造方法呻纹。方法引用使用::來(lái)定義,::的前半部分表示類名或者實(shí)例名专缠,后半部分表示方法名,如果是構(gòu)造方法就使用NEW來(lái)表示淑仆。

方法引用在Java8中使用方式相當(dāng)靈活涝婉,總的來(lái)說(shuō),一共有以下幾種形式:

  • 靜態(tài)方法引用:ClassName::methodName;
  • 實(shí)例上的實(shí)例方法引用:instanceName::methodName;
  • 超類上的實(shí)例方法引用:supper::methodName;
  • 類的實(shí)例方法引用:ClassName:methodName;
  • 構(gòu)造方法引用Class:new;
  • 數(shù)組構(gòu)造方法引用::TypeName[]::new

下面來(lái)看一個(gè)例子:

public class MethodReferenceTest {

    public static void main(String[] args) {
        ArrayList<Car> cars = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Car car = Car.create(Car::new);
            cars.add(car);
        }
        cars.forEach(Car::showCar);

    }

    @FunctionalInterface
    interface Factory<T> {
        T create();
    }

    static class Car {
        public void showCar() {
            System.out.println(this.toString());
        }

        public static Car create(Factory<Car> factory) {
            return factory.create();
        }
    }
}


輸出結(jié)果:

learn.MethodReferenceTest$Car@769c9116
learn.MethodReferenceTest$Car@6aceb1a5
learn.MethodReferenceTest$Car@2d6d8735
learn.MethodReferenceTest$Car@ba4d54
learn.MethodReferenceTest$Car@12bc6874

在上面的例子中使用了Car::new蔗怠,即通過(guò)構(gòu)造方法的方法引用的方式進(jìn)一步簡(jiǎn)化了lambda的表達(dá)式墩弯,Car::showCar,即表示實(shí)例方法引用寞射。

4. Stream

Java8中有一種新的數(shù)據(jù)處理方式渔工,那就是流Stream,結(jié)合lambda表達(dá)式能夠更加簡(jiǎn)潔高效的處理數(shù)據(jù)桥温。Stream使用一種類似于SQL語(yǔ)句從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)的直觀方式引矩,對(duì)數(shù)據(jù)進(jìn)行如篩選、排序以及聚合等多種操作。

4.1 什么是流Stream

Stream是一個(gè)來(lái)自數(shù)據(jù)源的元素隊(duì)列并支持聚合操作旺韭,更像是一個(gè)更高版本的Iterator,原始版本的Iterator氛谜,只能一個(gè)個(gè)遍歷元素并完成相應(yīng)操作。而使用Stream区端,只需要指定什么操作值漫,如“過(guò)濾長(zhǎng)度大于10的字符串”等操作,Stream會(huì)內(nèi)部遍歷并完成指定操作织盼。

Stream中的元素在管道中經(jīng)過(guò)中間操作(intermediate operation)的處理后杨何,最后由最終操作(terminal operation)得到最終的結(jié)果。

  • 數(shù)據(jù)源:是Stream的來(lái)源沥邻,可以是集合晚吞、數(shù)組、I/O channel等轉(zhuǎn)換而成的Stream谋国;
  • 基本操作:類似于SQL語(yǔ)句一樣的操作槽地,比如filter,map,reduce,find,match,sort等操作。

當(dāng)我們操作一個(gè)流時(shí)芦瘾,實(shí)際上會(huì)包含這樣的執(zhí)行過(guò)程:

獲取數(shù)據(jù)源-->轉(zhuǎn)換成Stream-->執(zhí)行操作捌蚊,返回一個(gè)新的Stream-->再以新的Stream繼續(xù)執(zhí)行操作--->直至最后操作輸出最終結(jié)果

4.2 生成Stream的方式

生成Stream的方式主要有這樣幾種:

  1. 從接口Collection中和Arrays:

    • Collection.stream();
    • Collection.parallelStream(); //相較于串行流近弟,并行流能夠大大提升執(zhí)行效率
    • Arrays.stream(T array);
  2. Stream中的靜態(tài)方法:

    • Stream.of()缅糟;
    • generate(Supplier<T> s);
    • iterate(T seed, UnaryOperator<T> f);
    • empty();
  3. 其他方法

    • Random.ints()
    • BitSet.stream()
    • Pattern.splitAsStream(java.lang.CharSequence)
    • JarFile.stream()
    • BufferedReader.lines()

下面對(duì)前面常見的兩種方式給出示例:

public class StreamTest {


    public static void main(String[] args) {
        //1.使用Collection中的方法和Arrays
        String[] strArr = new String[]{"a", "b", "c"};
        List<String> list = Arrays.asList(strArr);
        Stream<String> stream = list.stream();
        Stream<String> stream1 = Arrays.stream(strArr);

        //2. 使用Stream中提供的靜態(tài)方法
        Stream<String> stream2 = Stream.of(strArr);
        Stream<Double> stream3 = Stream.generate(Math::random);
        Stream<Object> stream4 = Stream.empty();
        Stream.iterate(1, i -> i++);

    }
}   

4.3 Stream的操作

常見的Stream操作有這樣幾種:

  1. Intermediate(中間操作):中間操作是指對(duì)流中數(shù)據(jù)元素做出相應(yīng)轉(zhuǎn)換或操作后依然返回為一個(gè)流Stream,仍然可以供下一次流操作使用祷愉。常用的有:map (mapToInt, flatMap 等)窗宦、 filter、 distinct二鳄、 sorted赴涵、 peek、 limit订讼、 skip髓窜。
  2. Termial(結(jié)束操作):是指最終對(duì)Stream做出聚合操作,輸出結(jié)果欺殿。

中間操作

filter:對(duì)Stream中元素進(jìn)行過(guò)濾

過(guò)濾元素為空的字符串:

long count = stream.filter(str -> str.isEmpty()).count();

map:對(duì)Stream中元素按照指定規(guī)則映射成另一個(gè)元素

將每一個(gè)元素都添加字符串“_map”

stream.map(str -> str + "_map").forEach(System.out::println);

map方法是一對(duì)一的關(guān)系寄纵,將stream中的每一個(gè)元素按照映射規(guī)則成另外一個(gè)元素,而如果是一對(duì)多的關(guān)系的話就需要使用flatmap方法脖苏。

concat:對(duì)流進(jìn)行合并操作

concat方法將兩個(gè)Stream連接在一起程拭,合成一個(gè)Stream。若兩個(gè)輸入的Stream都時(shí)排序的棍潘,則新Stream也是排序的恃鞋;若輸入的Stream中任何一個(gè)是并行的崖媚,則新的Stream也是并行的;若關(guān)閉新的Stream時(shí)山宾,原兩個(gè)輸入的Stream都將執(zhí)行關(guān)閉處理至扰。

Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6)).
    forEach(System.out::println);

distinct:對(duì)流進(jìn)行去重操作

去除流中重復(fù)的元素

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

輸出結(jié)果:
a
b
c

limit:限制流中元素的個(gè)數(shù)

截取流中前兩個(gè)元素:

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

輸出結(jié)果:
a
a

skip:跳過(guò)流中前幾個(gè)元素

丟掉流中前兩個(gè)元素:

Stream<String> stream = Stream.of("a", "a", "b", "c");
        stream.skip(2).forEach(System.out::println);
輸出結(jié)果:
b
c

peek:對(duì)流中每一個(gè)元素依次進(jìn)行操作,類似于forEach操作

JDK中給出的例子:

Stream.of("one", "two", "three", "four")
            .filter(e -> e.length() > 3)
            .peek(e -> System.out.println("Filtered value: " + e))
            .map(String::toUpperCase)
            .peek(e -> System.out.println("Mapped value: " + e))
            .collect(Collectors.toList());
輸出結(jié)果:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR

sorted:對(duì)流中元素進(jìn)行排序资锰,可以通過(guò)sorted(Comparator<? super T> comparator)自定義比較規(guī)則

Stream<Integer> stream = Stream.of(3, 2, 1);
        stream.sorted(Integer::compareTo).forEach(System.out::println);
輸出結(jié)果:
1
2
3

match:檢查流中元素是否匹配指定的匹配規(guī)則

Stream 有三個(gè) match 方法敢课,從語(yǔ)義上說(shuō):

  • allMatch:Stream 中全部元素符合傳入的 predicate,返回 true绷杜;
  • anyMatch:Stream 中只要有一個(gè)元素符合傳入的 predicate直秆,返回 true;
  • noneMatch:Stream 中沒有一個(gè)元素符合傳入的 predicate鞭盟,返回 true圾结。

如檢查Stream中每個(gè)元素是否都大于5:

Stream<Integer> stream = Stream.of(3, 2, 1);
boolean match = stream.allMatch(integer -> integer > 5);
System.out.println(match);
輸出結(jié)果:
false

結(jié)束操作

Collectors中常見歸約方法總結(jié)

重構(gòu)和定制收集器Collectors

count:統(tǒng)計(jì)Stream中元素的個(gè)數(shù)

long count = stream.filter(str -> str.isEmpty()).count();

max/min:找出流中最大或者最小的元素

Stream<Integer> stream = Stream.of(3, 2, 1);
    System.out.println(stream.max(Integer::compareTo).get());

輸出結(jié)果:
3

forEach

forEach方法前面已經(jīng)用了好多次,其用于遍歷Stream中的所元素齿诉,避免了使用for循環(huán)筝野,讓代碼更簡(jiǎn)潔,邏輯更清晰粤剧。

示例:

Stream.of(5, 4, 3, 2, 1)
    .sorted()
    .forEach(System.out::println);
    // 打印結(jié)果
    // 1歇竟,2,3,4,5

reduce

Stream中的Reduce講解

Stream歸約方法總結(jié)如下:

Collectors提供的歸約方法.png

5. Optional

為了解決空指針異常抵恋,在Java8之前需要使用if-else這樣的語(yǔ)句去防止空指針異常焕议,而在Java8就可以使用Optional來(lái)解決。Optional可以理解成一個(gè)數(shù)據(jù)容器弧关,甚至可以封裝null盅安,并且如果值存在調(diào)用isPresent()方法會(huì)返回true。為了能夠理解Optional世囊。先來(lái)看一個(gè)例子:

public class OptionalTest {


    private String getUserName(User user) {
        return user.getUserName();
    }

    class User {
        private String userName;

        public User(String userName) {
            this.userName = userName;
        }

        public String getUserName() {
            return userName;
        }
    }
}

事實(shí)上别瞭,getUserName方法對(duì)輸入?yún)?shù)并沒有進(jìn)行判斷是否為null,因此茸习,該方法是不安全的畜隶。如果在Java8之前,要避免可能存在的空指針異常的話就需要使用if-else進(jìn)行邏輯處理号胚,getUserName會(huì)改變?nèi)缦拢?/p>

private String getUserName(User user) {
    if (user != null) {
        return user.getUserName();
    }
    return null;
}

這是十分繁瑣的一段代碼。而如果使用Optional則會(huì)要精簡(jiǎn)很多:

private String getUserName(User user) {
    Optional<User> userOptional = Optional.ofNullable(user);
    return userOptional.map(User::getUserName).orElse(null);
}

Java8之前的if-else的邏輯判斷浸遗,這是一種命令式編程的方式猫胁,而使用Optional更像是一種函數(shù)式編程,關(guān)注于最后的結(jié)果跛锌,而中間的處理過(guò)程交給JDK內(nèi)部實(shí)現(xiàn)弃秆。

到現(xiàn)在届惋,可以直觀的知道Optional對(duì)避免空指針異常很有效,下面菠赚,對(duì)Optional的API進(jìn)行歸納:

創(chuàng)建Optional

  1. Optional.empty():通過(guò)靜態(tài)工廠方法Optional.empty脑豹,創(chuàng)建一個(gè)空的Optional對(duì)象;
  2. Optional<T> of(T value):如果value為null的話衡查,立即拋出NullPointerException瘩欺;
  3. Optional<T> ofNullable(T value):使用靜態(tài)工廠方法Optional.ofNullable,你可以創(chuàng)建一個(gè)允許null值的Optional對(duì)象拌牲。

實(shí)例代碼:

//創(chuàng)建Optional
Optional<Object> optional = Optional.empty();
Optional<Object> optional1 = Optional.ofNullable(null);
Optional<String> optional2 = Optional.of(null);

常用方法

1.  boolean equals(Object obj):判斷其他對(duì)象是否等于 Optional俱饿;
2. Optional<T> filter(Predicate<? super <T> predicate):如果值存在,并且這個(gè)值匹配給定的 predicate塌忽,返回一個(gè)Optional用以描述這個(gè)值拍埠,否則返回一個(gè)空的Optional;
3. <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper):如果值存在土居,返回基于Optional包含的映射方法的值枣购,否則返回一個(gè)空的Optional;
4. T get():如果在這個(gè)Optional中包含這個(gè)值擦耀,返回值棉圈,否則拋出異常:NoSuchElementException;
5. int hashCode():返回存在值的哈希碼埂奈,如果值不存在 返回 0迄损;
6. void ifPresent(Consumer<? super T> consumer):如果值存在則使用該值調(diào)用 consumer , 否則不做任何事情;
7. boolean isPresent():如果值存在則方法會(huì)返回true账磺,否則返回 false芹敌;
8. <U>Optional<U> map(Function<? super T,? extends U> mapper):如果存在該值,提供的映射方法垮抗,如果返回非null氏捞,返回一個(gè)Optional描述結(jié)果;
9. T orElse(T other):如果存在該值冒版,返回值液茎, 否則返回 other;
10. T orElseGet(Supplier<? extends T> other):如果存在該值辞嗡,返回值捆等, 否則觸發(fā) other,并返回 other 調(diào)用的結(jié)果续室;
11. <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier):如果存在該值栋烤,返回包含的值,否則拋出由 Supplier 繼承的異常挺狰;
12. String toString():返回一個(gè)Optional的非空字符串明郭,用來(lái)調(diào)試

Optional常用方法總結(jié):

Option的一些方法.png

6. Date/time API的改進(jìn)

在Java8之前的版本中买窟,日期時(shí)間API存在很多的問題,比如:

  • 線程安全問題:java.util.Date是非線程安全的薯定,所有的日期類都是可變的始绍;
  • 設(shè)計(jì)很差:在java.util和java.sql的包中都有日期類,此外话侄,用于格式化和解析的類在java.text包中也有定義亏推。而每個(gè)包將其合并在一起,也是不合理的满葛;
  • 時(shí)區(qū)處理麻煩:日期類不提供國(guó)際化径簿,沒有時(shí)區(qū)支持,因此Java中引入了java.util.Calendar和Java.util.TimeZone類嘀韧;

針對(duì)這些問題篇亭,Java8重新設(shè)計(jì)了日期時(shí)間相關(guān)的API,Java 8通過(guò)發(fā)布新的Date-Time API (JSR 310)來(lái)進(jìn)一步加強(qiáng)對(duì)日期與時(shí)間的處理锄贷。在java.util.time包中常用的幾個(gè)類有:

  • 它通過(guò)指定一個(gè)時(shí)區(qū)译蒂,然后就可以獲取到當(dāng)前的時(shí)刻,日期與時(shí)間谊却。Clock可以替換System.currentTimeMillis()與TimeZone.getDefault()
  • Instant:一個(gè)instant對(duì)象表示時(shí)間軸上的一個(gè)時(shí)間點(diǎn)柔昼,Instant.now()方法會(huì)返回當(dāng)前的瞬時(shí)點(diǎn)(格林威治時(shí)間);
  • Duration:用于表示兩個(gè)瞬時(shí)點(diǎn)相差的時(shí)間量炎辨;
  • LocalDate:一個(gè)帶有年份捕透,月份和天數(shù)的日期,可以使用靜態(tài)方法now或者of方法進(jìn)行創(chuàng)建碴萧;
  • LocalTime:表示一天中的某個(gè)時(shí)間乙嘀,同樣可以使用now和of進(jìn)行創(chuàng)建;
  • LocalDateTime:兼有日期和時(shí)間破喻;
  • ZonedDateTime:通過(guò)設(shè)置時(shí)間的id來(lái)創(chuàng)建一個(gè)帶時(shí)區(qū)的時(shí)間虎谢;
  • DateTimeFormatter:日期格式化類,提供了多種預(yù)定義的標(biāo)準(zhǔn)格式曹质;

示例代碼如下:

public class TimeTest {
    public static void main(String[] args) {
        Clock clock = Clock.systemUTC();
        Instant instant = clock.instant();
        System.out.println(instant.toString());

        LocalDate localDate = LocalDate.now();
        System.out.println(localDate.toString());

        LocalTime localTime = LocalTime.now();
        System.out.println(localTime.toString());

        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime.toString());

        ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(zonedDateTime.toString());
    }
}
輸出結(jié)果為:
2018-04-14T12:50:27.437Z
2018-04-14
20:50:27.646
2018-04-14T20:50:27.646
2018-04-14T20:50:27.647+08:00[Asia/Shanghai]

7. 其他改進(jìn)

Java8還在其他細(xì)節(jié)上也做出了改變婴噩,歸納如下:

  1. 之前的版本,注解在同一個(gè)位置只能聲明一次羽德,而Java8版本中提供@Repeatable注解几莽,來(lái)實(shí)現(xiàn)可重復(fù)注解;
  2. String類中提供了join方法來(lái)完成字符串的拼接宅静;
  3. 在Arrays上提供了并行化處理數(shù)組的方式银觅,比如利用Arrays類中的parallelSort可完成并行排序;
  4. 在Java8中在并發(fā)應(yīng)用層面上也是下足了功夫:(1)提供功能更強(qiáng)大的Future:CompletableFuture坏为;(2)StampedLock可用來(lái)替代ReadWriteLock究驴;(3)性能更優(yōu)的原子類::LongAdder,LongAccumulator以及DoubleAdder和DoubleAccumulator;
  5. 編譯器新增一些特性以及提供一些新的Java工具

參考資料

Stream的參考資料:

Stream API講解

Stream講解系列文章

對(duì)Stream的講解很細(xì)致

Java8新特性之Stream API

Optional的參考資料:

Optional的API講解很詳細(xì)

對(duì)Optional部分的講解還不錯(cuò)匀伏,值得參考

Java8新特性的介紹:

Java8新特性指南洒忧,很好的資料

跟上 Java 8 – 你忽略了的新特性

Java8新特性學(xué)習(xí)系列教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市够颠,隨后出現(xiàn)的幾起案子熙侍,更是在濱河造成了極大的恐慌,老刑警劉巖履磨,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛉抓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡剃诅,警方通過(guò)查閱死者的電腦和手機(jī)巷送,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)矛辕,“玉大人笑跛,你說(shuō)我怎么就攤上這事×钠罚” “怎么了飞蹂?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)翻屈。 經(jīng)常有香客問我陈哑,道長(zhǎng),這世上最難降的妖魔是什么伸眶? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任惊窖,我火速辦了婚禮,結(jié)果婚禮上赚抡,老公的妹妹穿的比我還像新娘爬坑。我一直安慰自己,他們只是感情好涂臣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布盾计。 她就那樣靜靜地躺著,像睡著了一般赁遗。 火紅的嫁衣襯著肌膚如雪署辉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天岩四,我揣著相機(jī)與錄音哭尝,去河邊找鬼。 笑死剖煌,一個(gè)胖子當(dāng)著我的面吹牛材鹦,可吹牛的內(nèi)容都是我干的逝淹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桶唐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼栅葡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起尤泽,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欣簇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后坯约,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熊咽,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年闹丐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了横殴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妇智,死狀恐怖滥玷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巍棱,我是刑警寧澤惑畴,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站航徙,受9級(jí)特大地震影響如贷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜到踏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一杠袱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窝稿,春花似錦楣富、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至踪少,卻和暖如春塘安,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背援奢。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工兼犯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓切黔,卻偏偏與公主長(zhǎng)得像砸脊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绕娘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354