Java8新特性

一淹禾、Lambda表達(dá)式

Lambda表達(dá)式引入了一個(gè)新的操作符" -> "积瞒。Lambda表達(dá)式將函數(shù)當(dāng)成參數(shù)傳遞給某個(gè)方法较屿,或是把代碼本身當(dāng)作數(shù)據(jù)處理。

Lambda表達(dá)式分為兩部分:

() -> { //執(zhí)行的功能 }
左側(cè):Lambda表達(dá)式的參數(shù)列表
右側(cè):Lambda表達(dá)式中所需要執(zhí)行的功能

1筋岛、Lambda表達(dá)式格式

下面講解Lambda的寫(xiě)法:

  • Lambda表達(dá)式參數(shù)列表的數(shù)據(jù)類型可以省略不寫(xiě)娶视,JVM編譯器能通過(guò)上下文推斷出來(lái)
void test(){
  (x,y) -> { //語(yǔ)句 }
  (String x,String y) -> { //語(yǔ)句 }
}

上面兩種寫(xiě)法時(shí)一樣的。

  • 無(wú)參數(shù)睁宰、無(wú)返回值

() -> { //語(yǔ)句 }

  • 有一個(gè)參數(shù)歇万,無(wú)返回值
    當(dāng)只有一個(gè)參數(shù)時(shí),小括號(hào)可以不寫(xiě)勋陪。

(x) -> { //語(yǔ)句 }
x -> { //語(yǔ)句 }
上面兩句等價(jià)贪磺。

  • 有兩個(gè)參數(shù)或多個(gè)參數(shù),有返回值
    當(dāng)Lambda表達(dá)式中只有一條語(yǔ)句時(shí)诅愚,則大括號(hào)和return都可以省略寒锚。

(x,y) -> x + y;

當(dāng)Lambda表達(dá)式中有多條語(yǔ)句時(shí),需加上大括號(hào)和return违孝。

(x,y) -> {
  int i = x + y;
  System.out.println(i);
  return i;
}
2刹前、Lambda表達(dá)式使用
void test(){
  //不使用Lambda表達(dá)式
  Runnable r = new Runnable() {
    @Override
    public void run() {
      System.out.println("不使用Lambda表達(dá)式方式--->hello");
    }
  };
  r.run;

  //使用Lambda表達(dá)式
  Runnable runnable = () -> System.out.println("使用Lambda表達(dá)式方式--->hello");
  runnable.run;
}

上面的例子介紹了使用Lambda表達(dá)式和不使用時(shí)的區(qū)別。

3雌桑、Lambda表達(dá)式需要函數(shù)式接口的支持
  • 什么是函數(shù)式接口

函數(shù)式接口:接口中只有一個(gè)抽象方法的接口喇喉,稱為函數(shù)式接口⌒?樱可以使用注解@FunctionalInterface標(biāo)注拣技,此注解可以檢查接口是否是函數(shù)式接口,此注解可以加也可以不加耍目。

java中給我們提供了一些函數(shù)式接口膏斤,我們也可以自定義函數(shù)式接口,下面先舉個(gè)自定義函數(shù)式接口的例子邪驮,接著再講解java給我們提供的部分函數(shù)式接口莫辨。

/**自定義函數(shù)式接口*/
@FunctionalInterface
public interface HandleData<T> {
    public T handle(T t);
}

下面是自定義函數(shù)接口的使用:

需求:計(jì)算數(shù)組中元素的和,正常情況步驟如下:

int[] ints = {1,2};
HandleData<int[]> h = new HandleData<int[]>() {
    @Override
    public Object handle(int[] ints) {
        return ints[0] + ints[1];
    }
};
//調(diào)用handle方法,將數(shù)組ints傳進(jìn)去沮榜,獲得計(jì)算結(jié)果
int result = (int) h.handle(ints);

接下來(lái)我們來(lái)看看使用Lambda表達(dá)式的寫(xiě)法:

int[] ints = {1,2};
HandleData<int[]> h = (arr) -> arr[0] + arr[1];
int result = (int) h.handle(ints);

上面代碼中的(arr) -> arr[0] + arr[1];其實(shí)就是重寫(xiě)的handle(int[] ints)方法盘榨。是不是用Lambda表達(dá)式更簡(jiǎn)潔一些,但是需要注意的是Lambda表達(dá)式需要函數(shù)式接口的支持蟆融。

  • java給我們提供的部分函數(shù)式接口:

Function<T,R>:函數(shù)型接口.
??R apply(T t);
??T作為輸入较曼,返回的R作為輸出,輸入和返回的類型可以一致,T振愿、R可以不一致。

int i = 0;
//如果需要處理的代碼是多行弛饭,就需要加大括號(hào)
Function<Integer,Integer> fun = (x) -> x + 1;
int result = fun.apply(i);

Predicate<T>:判斷型接口冕末。
??boolean test(T t);
??T作為輸入,返回boolean值的輸出侣颂。

Consumer<T>:消費(fèi)型接口档桃。

? void accpet(T t);

? T作為輸入沒(méi)有輸出。

Supplier<T>:供給型接口憔晒。

? T get();

? 沒(méi)有輸入藻肄,T作為輸出。

BinaryOperator<T>:二元運(yùn)算型接口拒担。

? R apply(T t, U u);

? 兩個(gè)T作為輸入嘹屯,T同樣是輸出,是BiFunction的子接口。

UnaryOperator<T>:一元運(yùn)算型接口从撼。

? T apply(T t);

? 是Function的變種州弟,輸入輸出者都是T,與Function區(qū)別是低零,UnaryOperator輸入和返回的類型一致婆翔,在此種情況下可以使用UnaryOperator替換Function。

4掏婶、方法引用和構(gòu)造器引用

方法引用:若Lambda體中的功能有方法以及實(shí)現(xiàn)了啃奴,我們可以使用"方法引用",可以理解為方法引用是Lambda表達(dá)式的另外一種表現(xiàn)形式

使用條件:方法引用所引用的方法的參數(shù)列表和返回值必須和函數(shù)式接口中抽象方法的參數(shù)列表和返回值完全一致雄妥。

語(yǔ)法格式:主要有三種

  • 對(duì)象::實(shí)例方法名 ---> Object::method

  • 類名::靜態(tài)方法名 ---> ClassName::static method

  • 類名::實(shí)例方法名--->ClassName::method

注意類::靜態(tài)方法名類::實(shí)例方法名的區(qū)別:

? 類::實(shí)例方法名方式中Lambda的第一個(gè)參數(shù)是實(shí)例方法的調(diào)用者最蕾,例如:x,第二個(gè)參數(shù)(或無(wú)參)是實(shí)例方法的參數(shù)老厌,例如:y (參見(jiàn)下面ClassName::method中的代碼所示)揖膜。

下面看代碼來(lái)看如何使用:

  • Object::method
Employee emp = new Employee("name",18);
//獲取Employee中的age,因?yàn)間etAge()方法沒(méi)有輸入只有輸出梅桩,我們可以選擇供給型接口Supplier<R>供給型接口壹粟。接口方法:T get();Employee類中獲取年齡方法:int getAge();
Supplier<Integer> sup = () -> emp.getAge();
System.out.println("Lambda表達(dá)式方式:" + sup.get());

//emp對(duì)象::getAge
Supplier<Integer> supplier = emp::getAge;
System.out.println("方法引用方式:" + supplier.get());
  • ClassName::static method
//Integer類的compare方法
Comparator<Integer> c = (x,y) -> Integer.compare(x,y);
System.out.println("Lambda表達(dá)式方式:" + c.compare(1,2));

//Integer類名::compare
Comparator<Integer> com = Integer::compare;
System.out.println("方法引用方式:" + com.compare(1,2));
  • ClassName::method
BiPredicate<String ,String > b = (x, y) -> x.equals(y);
System.out.println("Lambda表達(dá)式方式:" + b.test("a","b"));

BiPredicate<String ,String > bip = String::equals;
System.out.println("方法引用方式:" + bip.test("a","b"));

構(gòu)造器引用:構(gòu)造器能與函數(shù)式接口中的方法相兼容。

語(yǔ)法格式:

  • 類名::new--->ClassName::new

使用條件:構(gòu)造器參數(shù)列表與函數(shù)式接口中抽象方法的參數(shù)列表一致。

//默認(rèn)無(wú)參構(gòu)造器
Supplier<Employee> s = () -> new Employee();
System.out.println("Lambda表達(dá)式方式:" + s.get());

Supplier<Employee> sup = Employee::new;
System.out.println("構(gòu)造器引用方式:" + sup.get());

//有參數(shù)構(gòu)造器,也可以使用BiFunction或是自己寫(xiě)函數(shù)式接口來(lái)實(shí)現(xiàn)兩個(gè)或多個(gè)構(gòu)造器引用
Function<String,Employee> f = (x) -> new Employee(x);
System.out.println("Lambda表達(dá)式方式:" + f.apply("z"));

Function<String,Employee> fun = Employee::new;
System.out.println("構(gòu)造器引用方式:" + fun.apply("z"));

//數(shù)組引用
Function<Integer,String[]> a = (x) -> new String[x];
System.out.println("Lambda表達(dá)式方式:" + a.apply(10).length);

Function<Integer,String[]> arr = String[]::new;
System.out.println("數(shù)組引用方式:" + arr.apply(10).length);

二趁仙、Stream流

1洪添、Stream流是什么?

是數(shù)據(jù)渠道雀费,用于操作數(shù)據(jù)源(集合干奢、數(shù)組等)所生成的元素序列。

  • Stream不會(huì)存儲(chǔ)元素盏袄。
  • Stream不會(huì)改變?cè)磳?duì)象忿峻。它會(huì)返回一個(gè)持有結(jié)果的新Stream。
  • Stream操作是延遲執(zhí)行的辕羽。它會(huì)等到需要結(jié)果的時(shí)候才執(zhí)行逛尚。
2、Stream的操作步驟
  • 創(chuàng)建Stream:從一個(gè)數(shù)據(jù)源刁愿、集合绰寞、數(shù)組中獲取流。
  • 中間操作:一個(gè)操作的中間鏈铣口,對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行操作滤钱。
  • 終止操作(終端操作):一個(gè)終止操作,執(zhí)行中間操作鏈脑题,并產(chǎn)生結(jié)果件缸。
3、創(chuàng)建Stream流的方式
  • 通過(guò)Collection系列集合提供的串行流stream()方法或并行流parallelStream()方法進(jìn)行創(chuàng)建

    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream();
    Stream<String> parallelStream = list.parallelStream();
    
  • 通過(guò)Arrays中的靜態(tài)方法stream()方法

    Employee[] emps = new Employee[10];
    Stream<Employee> stream = Arrays.stream(emps);
    
  • 通過(guò)Stream接口中的靜態(tài)方法of()

    Stream<Employee> emps1 = Stream.of(emps);
    
  • 通過(guò)Stream提供的iterate()和generate()創(chuàng)建無(wú)限流

    • Stream<T> iterate(final T seed, final UnaryOperator<T> f)

      T seed:初始元素叔遂。

      UnaryOperator<T> f:一元運(yùn)算型接口停团,輸入和輸出者都需要一樣,對(duì)seed進(jìn)行運(yùn)算掏熬。

      方法含義:返回一個(gè)無(wú)限有序的流佑稠,以seed為種子,以f為迭代方法旗芬,產(chǎn)生的一個(gè)由seed舌胶,f(seed),f(f(seed))等組成的Stream疮丛,也就是位置n的元素幔嫂,是由位置n-1的元素通過(guò)f方法產(chǎn)生的。

    Stream<Integer> stream = Stream.iterate(0, (x) -> x + 2);
    stream.forEach(System.out::println);  //以0為基礎(chǔ)誊薄,以2遞增履恩,一直
    
    • Stream<T> generate(Supplier<T> s)

      Supplier<T> s:供給型接口,沒(méi)有輸入呢蔫,T作為輸出

      方法含義:返回一個(gè)無(wú)限無(wú)序的流切心,其中每個(gè)元素都由Supplier生成飒筑,適用于生成恒定元素流、隨機(jī)元素流等绽昏。

    Stream<Double> stream = Stream.generate(() -> Math.random());
    stream.forEach(System.out::println);
    
4协屡、中間操作-篩選切片

filter(Predicate p)limit(long maxSize)全谤,skip(long n)肤晓,distinct(),下面介紹這四個(gè)方法:

//class Employee {name,age,salary}
List<Employee> list = Arrays.asList(new Employee("emp1",1,1000.00),new Employee("emp2",2,2000.00),new Employee("emp3",3,3000.00),new Employee("emp4",4,4000.00),new Employee("emp4",4,4000.00),
);
  • filter(Predicate p):接收Lambda认然,從流中排除某些元素补憾。
Stream<Employee> stream = empList.stream()
    .filter((e) -> {
        return e.getAge() > 2;
    });
//只會(huì)顯示年齡大于2的
  • limit(long maxSize):截?cái)嗔鳎蛊湓夭怀^(guò)給定數(shù)量卷员。
Stream<Employee> stream = empList.stream()
    .limit(2);
//只會(huì)得到前兩條
  • skip(long n):跳過(guò)元素盈匾,返回一個(gè)扔掉了前n個(gè)元素的流。若流中元素不足n個(gè)子刮,則返回一個(gè)空流。與limit(n)互補(bǔ)
Stream<Employee> stream = empList.stream()
    .skip(2);
//只會(huì)得到后兩條
  • distinct():篩選窑睁,通過(guò)流所生成元素的hashCode()和equals()去除重復(fù)元素
Stream<Employee> stream = empList.stream()
    .distinct();
//會(huì)去除掉最后一個(gè)new Employee("emp4",4,4000.00)的數(shù)據(jù)

distinct()方法是根據(jù)元素的hashCode()和equals()的規(guī)則來(lái)判斷元素是否重復(fù)的挺峡,所以如果有需求就重寫(xiě)這兩個(gè)方法。

5担钮、中間操作-映射

map(Function f)橱赠,mapToDouble(ToDoubleFunction f)mapToInt(ToIntFunction f)箫津,mapToLong(ToLongFunction f)狭姨,flatMap(Function f),下面介紹這五個(gè)方法:

  • map(Function f):接收一個(gè)函數(shù)作為參數(shù)苏遥,該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上饼拍,并將其映射成一個(gè)新的元素
//class StreamTest03
List<String> list = Arrays.asList("aa","bb");
list.stream()
    .map((str) -> str.toUpperCase())
    .forEach(System.out::println);
//結(jié)果:AA BB

//
Stream<Stream<Character>> characterStream = list.stream()
                .map(StreamTest03::filterCharacter);//結(jié)果:a a b b
//如何遍歷characterStream,Stream<Stream<Character>>里的數(shù)據(jù):{{a,a},{b,b}}
characterStream.forEach( (stream) -> {
    stream.forEach(System.out::println);
});

//將字符串拆成char然后存到list的方法
public static Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();
    for (Character c : str.toCharArray()) {
        list.add(c);
    }
    return list.stream();
}
  • mapToDouble(ToDoubleFunction f):接收一個(gè)函數(shù)作為參數(shù)田炭,該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上师抄,產(chǎn)生一個(gè)新的DoubleStream
List<Double> list = Arrays.asList(1.1,2.2);
list.stream().mapToDouble((d) -> d+1).forEach(System.out::println);
//結(jié)果:2.1  3.1
  • mapToInt(ToIntFunction f):接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上教硫,產(chǎn)生一個(gè)新的IntStream
  • mapToLong(ToLongFunction f):接收一個(gè)函數(shù)作為參數(shù)叨吮,該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上,產(chǎn)生一個(gè)新的LongStream
  • flatMap(Function f):接收一個(gè)函數(shù)作為參數(shù)瞬矩,將流中的每個(gè)值都換成另一個(gè)流茶鉴,然后把所有流連接成一個(gè)流
Stream<Character> cStream = list.stream()
    .flatMap(StreamTest03::filterCharacter);//{a,a,b,b}
cStream.forEach(System.out::println);//結(jié)果:a a b b

如上代碼所示,和map(Function f)的區(qū)別是它會(huì)把所有的連接成一個(gè)流景用。

6涵叮、中間操作-排序

sorted()sorted(Comparator comp),下面介紹這兩個(gè)方法:

  • sorted():產(chǎn)生一個(gè)新流围肥,其中按自然順序排序(Comparable)
List<String> list = Arrays.asList("bb","aa","cc");
list.stream()
    .sorted()
    .forEach(System.out::println);
//結(jié)果:aa bb cc
  • sorted(Comparator comp):產(chǎn)生一個(gè)新流剿干,其中按比較器順序排序(Comparator)
//list是上面的List<Employee>
list.stream()
    .sorted((e1,e2) -> {
        if (e1.getAge() == e2.getAge()) {   //如果年齡相等則根據(jù)名字排序
            return e1.getName().compareTo(e2.getName());
        }else {
            return e2.getAge() - e1.getAge();   //年齡不等從大往小排序
        }
    })
    .forEach(System.out::println);
//結(jié)果:4 4 3 2 1
7、終止操作-查找與匹配

直接介紹有哪些方法穆刻,例子就不列舉了置尔。

  • boolean allMatch(Predicate p):檢查是否匹配所有元素。如果都匹配或流為空氢伟,返回true榜轿。
  • boolean anyMatch(Predicate p):檢查是否至少匹配一個(gè)元素。
  • boolean noneMatch(Predicate p):檢查是否沒(méi)有匹配所有元素朵锣。如果沒(méi)有任何元素匹配或流為空谬盐,返回true。
  • Optional<T> findFirst():返回第一個(gè)元素诚些。
  • Optional<T> findAny():返回當(dāng)前流中的任意元素飞傀。
  • long count():返回流中元素的總個(gè)數(shù)。
  • Optional<T> min(Comparator<? super T> comparator):返回流中的最小值诬烹。
  • Optional<T> max(Comparator<? super T> comparator):返回流中的最大值砸烦。
8、歸約reduce
  • T reduce(T identity, BinaryOperator<T> accumulator):可以將流中元素反復(fù)結(jié)合起來(lái)绞吁,得到一個(gè)值幢痘。
    • T identity:起始值。
    • BinaryOperator<T> accumulator:二元運(yùn)算型接口家破。
List<Integer> list = Arrays.asList(1,2,3,4,5);
Integer reduce = list.stream()
    .reduce(0, (x, y) -> x + y);
System.out.println(reduce);
//結(jié)果:15

其實(shí)它是將identity這個(gè)起始值作為上面代碼中的x颜说,然后從流中取出一個(gè)作為y,然后進(jìn)行x+y運(yùn)算汰聋。

  • Optional<T> reduce(BinaryOperator<T> accumulator):可以將流中元素反復(fù)結(jié)合起來(lái)门粪,得到一個(gè)值。因?yàn)闆](méi)有初始值可能為空烹困,所以將結(jié)果封裝到Optional中去庄拇。
Optional<Integer> op1 = list.stream().reduce((x, y) -> x - y);
System.out.println(op1.get());
//結(jié)果:-13
Optional<Empoyee> op2 = employees.stream()
    .map(Employee::getSalary)
    .reduce(Double::sum);
System.out.println(op2.get());
//可以得到工資的總和
9、收集collect
  • <R, A> R collect(Collector<? super T, A, R> collector):collect將流轉(zhuǎn)換成其他形式韭邓。接收一個(gè)Collector接口的實(shí)現(xiàn)措近,用于Stream中元素做匯總的方法。

Collector接口中方法的實(shí)現(xiàn)決定了如何對(duì)流執(zhí)行收集操作(如收集到List女淑、Set瞭郑、Map)。

public interface Collector<T, A, R> {} 中:

T:歸約操作的輸入元素的類型

A:歸約操作的可變累積類型(通常作為一個(gè)實(shí)現(xiàn)細(xì)節(jié)隱藏)

R:歸約操作結(jié)果的類型

Collectors類是Collector的實(shí)現(xiàn)鸭你,它實(shí)現(xiàn)了各種有用的歸約操作屈张,例如將元素累積到集合中擒权,根據(jù)各種標(biāo)準(zhǔn)對(duì)元素進(jìn)行匯總等。提供了很多靜態(tài)方法阁谆,可以方便的創(chuàng)建常見(jiàn)收集器實(shí)例碳抄,具體方法與實(shí)例如下表:

方法 返回值類型 作用
toList() List<T> 把流中元素收集到list
toSet() Set<T> 把流中元素收集到set
toCollection(Supplier s) Collection<T> 把流中的元素收集到創(chuàng)建的集合
counting() Long 計(jì)算流中元素的個(gè)數(shù)
summingInt(ToIntFunction mapper) Integer 對(duì)流中元素的整數(shù)屬性求和
averagingInt(ToIntFunction mapper) Double 計(jì)算流中元素Integer屬性的平均值
summarizingInt(ToIntFunction mapper) IntSummaryStatistics 收集流中Integer屬性的統(tǒng)計(jì)值〕÷蹋可通過(guò)返回的對(duì)象獲取平均值等
joining() String 連接流中每個(gè)字符串
maxBy(Comparator com) Optional<T> 根據(jù)比較器選擇最大值
minBy(Comparator com) Optional<T> 根據(jù)比較器選擇最小值
collectingAndThen(Collector c,Function f) f返回的類型 包裹另一個(gè)收集器剖效,對(duì)其結(jié)果轉(zhuǎn)換函數(shù)
groupingBy(Function classifier) Map<K,List<T>> 根據(jù)某屬性值對(duì)流分組,屬性為K焰盗,結(jié)果為V
partitioningBy(Predicate p) Map<Boolean,List<T>> 根據(jù)true或false進(jìn)行分區(qū)

暫且介紹上面幾個(gè)方法璧尸,更多方法可以去看JDK8的API。

下面以CollectorscollectingAndThen為例來(lái)介紹collect方法的使用熬拒。

Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)方法:

此方法中泛型:

  • <T>:輸入元素的類型
  • <A>: downstream收集器中的中間積累的類型
  • <R>: downstream收集器的結(jié)果的類型
  • <RR>: 結(jié)果收集器的結(jié)果的類型

方法有兩個(gè)參數(shù):

  • Collector<T,A,R> downstream:對(duì)數(shù)據(jù)進(jìn)行處理
  • Function<R,RR> finisher:對(duì)downstream收集器最終的結(jié)果再處理一下

返回值:downstream收集器的操作完成后爷光,最終的結(jié)果再通過(guò)Function再處理一下,然后返回一個(gè)Collector澎粟。

說(shuō)明:finisher是對(duì)downstream的結(jié)果進(jìn)行處理蛀序,finisher接口的參數(shù)是downstream的結(jié)果。collectingAndThen方法的返回值是finisher中返回的值活烙。

//List<Employee> list
Employee collect = list.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.maxBy(Comparator.comparing(Employee::getName)),
        (e) -> e.get()
    ));

List<String> list = Arrays.asList("a","b");
String str = list.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.joining(","),
        String::toUpperCase
    ));
//結(jié)果是:A,B

上面代碼溉知,downstream先用joining方法將字符串連接成:"a,b"卒落,然后將使用String::toUpperCase對(duì)"a,b"轉(zhuǎn)大寫(xiě)儒鹿。

三唬涧、Optional類

Optional<T> 類(java.util.Optional)是一個(gè)容器類譬正,代表一個(gè)值存在或不存在宫补,原來(lái)用null表示一個(gè)值不存在,現(xiàn)在Optional可以更好的表達(dá)這個(gè)概念曾我。并且可以避免空指針粉怕。

常用方法:

Optional.of(T t):創(chuàng)建一個(gè)Optional實(shí)例

Optional.empty():創(chuàng)建一個(gè)空的Optional實(shí)例

Optional.ofNullable(T t):若t不為null,創(chuàng)建Optional實(shí)例抒巢,否則創(chuàng)建空實(shí)例

isPresent():判斷是否包含空值贫贝,空返回false

orElse(T t):如果調(diào)用對(duì)象包含值,返回該值蛉谜,否則返回t

orElseGet(Supplier s):如果調(diào)用對(duì)象包含值稚晚,返回該值,否則返回s獲取的值

map(Function f):如果有值對(duì)其處理型诚,并返回處理后的Optional客燕,否則返回Optional.empty()

flatMap(Function mapper):與map類似,要求返回值必須是Optional

四狰贯、接口中的默認(rèn)方法和靜態(tài)方法

1也搓、接口中的默認(rèn)方法

java8中接口里面包含具體的實(shí)現(xiàn)方法赏廓,該方法稱為默認(rèn)方法,默認(rèn)方法使用default關(guān)鍵字修飾傍妒。

public interface MyInterface {
    default String getName(){
        return "MyInterface";
    }
}

對(duì)于默認(rèn)的方法幔摸,遵循的原則:

若一個(gè)接口中定義了一個(gè)默認(rèn)方法,另一個(gè)的父類或接口中又定義一個(gè)同名方法時(shí):

  • 選擇父類中的方法颤练。如果一個(gè)父類提供了具體實(shí)現(xiàn)既忆,那么接口中具有相同名稱和參數(shù)的默認(rèn)方法會(huì)被忽略
public class MyClass {
    public String getName(){
        return "MyClass";
    }
}
public interface MyInterface {
    default String getName(){
        return "MyInterface";
    }
}
public class SubClass extends MyClass implements MyInterface {}

SubClass sub = new SubClass();
String name = sub.getName();
System.out.println(name);
//MyClass
  • 接口沖突。如果一個(gè)父接口提供一個(gè)默認(rèn)方法昔案,而另一個(gè)接口也提供了一個(gè)具有相同名稱和參數(shù)列表的方法(不管方法是否是默認(rèn)方法)尿贫,那么必須覆蓋該方法來(lái)解決沖突
public interface MyInterface2 {
    default String getName(){
        return "MyInterface2";
    }
}
public class SubClass implements MyInterface1,MyInterface2{
    @Override
    public String getName() {
        return "SubClass重寫(xiě)該方法";
    }
}

如上代碼,實(shí)現(xiàn)類SubClass里要重寫(xiě)該方法解決沖突踏揣。

2庆亡、接口中的靜態(tài)方法
public interface MyInterface1 {
    default String getName(){
        return "MyInterface1";
    }
    public static void show(){
        System.out.println("接口中的靜態(tài)方法");
    }
}

MyInterface1.show();
//接口中的靜態(tài)方法

五、新時(shí)間日期API

JDK8新增的時(shí)間API主要有三種:

java.time.LocalDate:只對(duì)年月日做出處理

java.time.LocatTime:只對(duì)時(shí)分秒納秒做出處理

java.time.LocalDateTime:同時(shí)可以處理年月日和時(shí)分秒

java.time.format.DateTimeFormatter:日期時(shí)間對(duì)象格式化和解析

1捞稿、LocalDate又谋、LocalTime、LocalDateTime

LocalDate娱局、LocalTime彰亥、LocalDateTime 類的實(shí)例是不可變的對(duì)象,分別表示使用 ISO-8601日系統(tǒng)的日期衰齐、時(shí)間任斋、日期和時(shí)間。它們提供了簡(jiǎn)單的日期或時(shí)間耻涛,并不包含當(dāng)前的時(shí)間信息废酷。也不包含與時(shí)區(qū)相關(guān)的信息。

方法 描述 示例
now() 靜態(tài)方法抹缕,根據(jù)當(dāng)前時(shí)間創(chuàng)建對(duì)象 LocalDate.now();<br />LocalTime.now();<br />LocalDateTime.now();
of() 靜態(tài)方法澈蟆,根據(jù)指定日期/時(shí)間創(chuàng)建對(duì)象 LocalDate.of(2020,01,01);
plusDays<br />plusWeeks
plusMonths
plusYears
向當(dāng)前LocalDate對(duì)象添加幾天、幾周卓研、幾個(gè)月趴俘、幾年
minusDays<br />minusWeeks
minusMonths
minusYears
從當(dāng)前LocalDate對(duì)象減去幾天、幾周奏赘、幾個(gè)月寥闪、幾年
plus,minus 添加或減去一個(gè)Duration或Period
with 通過(guò)TemporalAdjuster將日期修改為指定的值,并返回新的LocalDate對(duì)象
withDayOfMonth
withDayOfYear
withMonth
withYear
將月份天數(shù)磨淌,年份天數(shù)疲憋,月份,年份修改為指定的值并返回新的LocalDate對(duì)象
getDayOfMonth 獲得月份天數(shù)(1-31)
getDayOfYear 獲得年份天數(shù)(1-366)
getDayOfWeek 獲得星期幾(返回一個(gè)DayOfWeek枚舉值)
getMonth 獲得月份伦糯,返回一個(gè)Month枚舉值
getMonthValue 獲得月份(1-12)
getYear 獲得年份
until 獲得兩個(gè)日期之間的Period對(duì)象柜某,或者指定ChronoUnits的數(shù)字
isBefore,isAfter 比較兩個(gè)LocalDate
isLeapYear 判斷是否是閏年
2嗽元、Instant時(shí)間戳

用于“時(shí)間戳”的運(yùn)算。它是以Unix元年(傳統(tǒng)的設(shè)定為UTC時(shí)區(qū)1970年1月1日午夜時(shí)分)開(kāi)始所經(jīng)歷的描述進(jìn)行運(yùn)算喂击,默認(rèn)UTC時(shí)區(qū)剂癌。

  • 獲取當(dāng)前時(shí)間:Instant.now();返回當(dāng)前時(shí)間,默認(rèn)獲取UTC時(shí)區(qū)
  • 調(diào)整偏移量:Instant.now().atoffset(ZoneOffset.ofHours(8));調(diào)整為北京時(shí)間
  • 獲取毫秒數(shù):Instant.now().toEpochMilli();獲取時(shí)間戳
3翰绊、Duration和Period計(jì)算時(shí)間/日期間隔
  • Duration:用于計(jì)算兩個(gè)“時(shí)間”間隔
Instant start = Instant.now();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
     e.printStackTrace();
}
Instant end = Instant.now();
Duration d = Duration.between(start, end);
d.toMillis();//獲取相差的毫秒
d.toMillis();//獲取相差的秒
  • Period:用于計(jì)算兩個(gè)“日期”間隔
LocalDate old = LocalDate.of(2019,10,1);
LocalDate now = LocalDate.now();//2020-10-01
Period p = Period.between(old, now);

p.getYears();//兩個(gè)日期之間間隔的年佩谷,結(jié)果為:1
p.getMonths();//兩個(gè)日期之間間隔的月,結(jié)果為:0
p.getDays();//兩個(gè)日期之間間隔的日监嗜,結(jié)果為:0
//這三個(gè)加起來(lái)就是兩個(gè)日期之間相差的時(shí)間
4谐檀、TemporalAdjuster時(shí)間校正器
  • TemporalAdjuster : 時(shí)間校正器。有時(shí)我們可能需要獲取裁奇,例如:將日期調(diào)整到下周一等操作桐猬。

  • **TemporalAdjusters : **該類通過(guò)靜態(tài)方法提供了大量的常用 TemporalAdjuster 的實(shí)現(xiàn)。

LocalDateTime now = LocalDateTime.now();
now.with(TemporalAdjusters.next(DayOfWeek.MONDAY));//返回以今天為準(zhǔn)的下周一的時(shí)間

自定義方式:

LocalDate result = now.with((time) -> {
    LocalDate date = (LocalDate) time;
    return date.plusDays(1);
});
//返回下一天的日期
5刽肠、DateTimeFormatter時(shí)間/日期格式化

DateTimeFormatter是時(shí)間日期格式化的類溃肪,更復(fù)雜的格式化可以使用DateTimeFormatterBuilder類。

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatterBuilder dtfb  = new DateTimeFormatterBuilder();
dtfb.append(dtf).appendLiteral(" 星期三");
String format = now.format(dtfb.toFormatter());
//2020-10-01 17:39:33 星期三

這個(gè)類提供了三種格式化方法:

  • 使用預(yù)定義的標(biāo)準(zhǔn)格式音五,如ISO_local_date
  • 使用語(yǔ)言環(huán)境相關(guān)的格式惫撰,如uuuu-MMM-dd
  • 自定義的格式,如long
//格式化
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDate = now.format(dtf);
System.out.println("formatDate--->" + formatDate);//formatDate--->2020-10-01 17:57:00
//解析
LocalDateTime parseDate = now.parse(formatDate, dtf);
System.out.println("parseDate--->" + parseDate);//parseDate--->2020-01-14T17:57
6躺涝、ZonedDateTime厨钻、ZoneId時(shí)區(qū)處理

帶時(shí)區(qū)的時(shí)間分別為:ZonedDateZonedTime坚嗜、ZonedDateTime夯膀。

其中每個(gè)時(shí)區(qū)都對(duì)應(yīng)著 ID,地區(qū)ID都為 “{區(qū)域}/{城市}”的格式:例如 :Asia/Shanghai 等惶傻。

ZoneId:該類中包含了所有的時(shí)區(qū)信息

  • getAvailableZoneIds(): 可以獲取所有時(shí)區(qū)時(shí)區(qū)信息

  • of(id): 用指定的時(shí)區(qū)信息獲取ZoneId對(duì)象

  • systemDefault():獲取系統(tǒng)默認(rèn)時(shí)區(qū)

LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zdt = now.atZone(ZoneId.of("Asia/Shanghai"));

六棍郎、重復(fù)注解與類型注解

對(duì)注解處理提供了兩點(diǎn)改進(jìn):可重復(fù)注解及可用于類型的注解其障。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}
@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}
@MyAnnotation("Hello")
@MyAnnotation("World")
public void test(@MyAnnotation("a") String str){}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載银室,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末励翼,一起剝皮案震驚了整個(gè)濱河市蜈敢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汽抚,老刑警劉巖抓狭,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異造烁,居然都是意外死亡否过,警方通過(guò)查閱死者的電腦和手機(jī)午笛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)苗桂,“玉大人药磺,你說(shuō)我怎么就攤上這事∶何埃” “怎么了癌佩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)便锨。 經(jīng)常有香客問(wèn)我围辙,道長(zhǎng),這世上最難降的妖魔是什么放案? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任姚建,我火速辦了婚禮,結(jié)果婚禮上吱殉,老公的妹妹穿的比我還像新娘桥胞。我一直安慰自己,他們只是感情好考婴,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布贩虾。 她就那樣靜靜地躺著,像睡著了一般沥阱。 火紅的嫁衣襯著肌膚如雪缎罢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天考杉,我揣著相機(jī)與錄音策精,去河邊找鬼。 笑死崇棠,一個(gè)胖子當(dāng)著我的面吹牛咽袜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枕稀,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼询刹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了萎坷?” 一聲冷哼從身側(cè)響起凹联,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哆档,沒(méi)想到半個(gè)月后蔽挠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓜浸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年澳淑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了比原。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杠巡,死狀恐怖春寿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忽孽,我是刑警寧澤绑改,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站兄一,受9級(jí)特大地震影響厘线,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜出革,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一造壮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骂束,春花似錦耳璧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至混驰,卻和暖如春攀隔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背栖榨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工昆汹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婴栽。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓满粗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親愚争。 傳聞我的和親對(duì)象是個(gè)殘疾皇子映皆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344