Java8 新特性
Java8新特性
推薦閱讀:
- lambda表示式、方法引用、構造器引用、陣列引用
- Stream流
- optional
- 時間日期API [執行緒安全]
- 重複註解與型別註解
- ForkJoin框架
- 介面的預設方法和靜態方法
1. Lambda表示式
Java8中引入了一個新的操作符 “->” 該操作符稱位箭頭操作符
- 定義一個介面(函式式介面)
@FunctionalInterface
public interface MyFunction {
Integer getValue(Integer num);
}
- 使用lambda表示式
public class LambdaTest2 {
public Integer operation(Integer num, MyFunction mf) {
return mf.getValue(num);
}
@Test
public void test1() {
Integer num = operation(100, (x) -> x * x);
System.out.println(num);
Integer num2 = operation(100, (x) -> x + 200);
System.out.println(num2);
}
}
1.1 內建四大核心函式式介面
介面名稱 | 介面型別 | 方法 | 方法引數 | 方法返回值型別 |
---|---|---|---|---|
Consumer | 消費型 | void accept(T t) | √ | × |
Supplier | 供給型 | T get() | × | √ |
Function<T,R> | 函式型 | R apply (T t) | √ | √ |
Predicate | 斷言型 | boolean test(T t) | √ | √ |
消費型介面
/**
* Consumer<T>: 消費型介面
* 需求:
*/
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void happyTest() {
happy(10000, m -> System.out.println("今天吃飯花了" + m));
}
供給型介面
/**
* Supplier<T>:供給型介面
* 需求:隨機儲存10個數字存入集合
*/
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
System.out.println(list);
return list;
}
@Test
public void supTest() {
getNumList(10, () -> (int) (Math.random() * 100));
}
函式型介面
/**
* Function<T,R>: 函式型介面
*/
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
@Test
public void funTest() {
String s = strHandler("\t\t\t 你是誰啊! \t\t\t", (str) -> str.trim());
System.out.println(s);
}
斷言型介面
/**
* Predicate<T>: 斷言型介面
* 需求:將滿足條件的字串放入集合
*/
public static List<String> filterStr(List<String> strList, Predicate<String> pre) {
List<String> list = new ArrayList<>();
for (String str : strList) {
if (pre.test(str)) {
list.add(str);
}
}
return list;
}
@Test
public void preHandler(){
List<String> stringList = Arrays.asList("Hello", "Friends", "Lambda", "www", "ok");
List<String> strings = filterStr(stringList, (str) -> str.length() > 3);
System.out.println(strings);
}
1.2 方法引用
若Lambda體中的內容有方法已經實現了,我們可以使用“方法引用”(可理解為是lambda表示式的另外一種表現形式)
有三種語法形式
- 物件::例項方法名
- 類名::靜態方法名
- 類名::例項方法名
注意
Lambda體中呼叫方法的引數列表與返回值型別,要與函式式介面中抽象方法的函式列表和返回值型別保持一致!
若Lambda引數列表中的第一引數是例項方法的呼叫者,而第二個引數是例項方法的引數時,可使用 ClassName::methodName
物件::例項方法名
/**
* 物件::例項方法名
*/
@Test
public void test1() {
PrintStream ps = System.out;
// 方法引用
Consumer<String> con = ps::println;
con.accept("aqwsdasagas");
}
類名::靜態方法名
/**
* 類::靜態方法名
*/
@Test
public void test2() {
// lambda
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
// 方法引用
Comparator<Integer> com1 = Integer::compare;
}
類::例項方法名
/**
* 類::例項方法名
*/
@Test
public void test3() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp1 = String::equals;
}
@Test
public void test2() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com1 = Integer::compare;
}
1.3 構造器引用
注意:需要呼叫的建構函式的引數列表要與函式式介面中抽象方法的引數列表保持一致!
前提定義Employee和列舉類
- Employee
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private int id;
private String name;
private int age;
private double salary;
private Status status;
public Employee(int id) {
this.id = id;
}
public Employee(int id, int age) {
this.id = id;
this.age = age;
}
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public Employee(int i, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
- 列舉
public enum Status {
FREE,
BUSY,
VOCATION;
}
- 構造器引用案例
/**
* 構造器引用: 獲得了一個無參構造
*/
@Test
public void test4() {
// lambda
Supplier<Employee> sup = () -> new Employee();
// 構造器引用
Supplier<Employee> sup2 = Employee::new;
// 應該獲得了一個無參構造,因為get方法就是無參有返回值
Employee employee = sup2.get();
System.out.println(employee);
}
/**
* 構造器引用:獲取引數為int id的構造方法
*/
@Test
public void test5() {
Function<Integer, Employee> fun = (x) -> new Employee(x);
Employee apply = fun.apply(2);
Function<Integer, Employee> fun2 = Employee::new;
// 根據apply的引數個數去呼叫對應的構造方法(一個引數,int型別)
Employee apply2 = fun.apply(3);
System.out.println(apply);
System.out.println(apply2);
}
1.4 陣列引用
Type::new
/**
* 陣列引用
*/
@Test
public void test7(){
Function<Integer,String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer,String[]> fun2 = String[]::new;
System.out.println(fun2.apply(15).length);
}
1.5 情景處理
某公司員工定製排序
/**
* 某公司員工基本資訊
*/
List<Employee> emps = Arrays.asList(
new Employee(1,"張三", 18, 1111.11),
new Employee(2,"李四", 49, 2222.22),
new Employee(3,"趙六", 57, 3333.33),
new Employee(4,"田七", 35, 4444.44)
);
/**
* 定製排序:先按年齡比,年齡相同按姓名比
*/
@Test
public void test1() {
Collections.sort(emps, (e1, e2) -> {
if (e1.getAge() == e2.getAge()) {
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(),e2.getAge());
}
});
System.out.println("======================");
for (Employee emp : emps) {
System.out.println(emp);
}
}
字串處理
/**
* 函式式介面
*/
public interface StringFun {
String getValue(String str);
}
/**
* 用於處理字串
*/
public String strHandler(String str, StringFun sf) {
return sf.getValue(str);
}
/**
* 字串處理
*/
@Test
public void test1() {
String s1 = strHandler("\t\t\t hello , may i have a dog \t\t\t", str -> str.trim());
System.out.println(s1);
String s2 = strHandler("asdfghjkl", str -> str.toUpperCase());
System.out.println(s2);
String s3 = strHandler("asdfghjkl", str -> str.substring(0,3));
System.out.println(s3);
}
long型數字處理
/**
* 函式式介面
*/
public interface MyFunction<T, R> {
public R getValue(T t1, T t2);
}
/**
* 對於兩個long型資料進行處理
* MyFunction<Integer,Integer>: 傳入引數型別、返回值型別
*/
public void operacte(Integer l1, Integer l2, MyFunction<Integer,Integer> mf) {
System.out.println(mf.getValue(l1, l2));
}
@Test
public void test1() {
operacte(100, 100, ( x, y) -> x + y);
}
2. Stream流
Stream的三個操作步驟
- 建立Stream
- 中間操作
- 終止操作(終端操作)
2.1 建立Stream
建立stream的四種方式
- 通過Collection系列集合提供的
stream()
方法或parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.parallelStream();
- 通過靜態方法獲取一個陣列流
Employee[] employees = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(employees);
- 通過Stream類靜態類中的of方法
Stream<String> stream3 = Stream.of("aa","bb","cc","dd");
- 建立無限流:兩種(迭代和生成)
// 4.1迭代 : 傳一個起始值和一個一元運算
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
/// 中間操作 終止操作
stream4.limit(10).forEach(System.out::println);
// 4.2生成(無限生成隨機數)
Stream.generate(()->Math.random()).forEach(System.out::pri
2.2 中間操作
中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
2.2.1 篩選與切片
方法 | 描述 |
---|---|
filter | 接收Lambda,從流中排除某些元素 |
limit | 截斷流,使其元素不超過給定數量 |
skip(n) | 跳過元素,返回一個扔掉了前n個元素的流,若流中元素不足n個,則返回一個空流,與limit(n)互補 |
distinct | 篩選,通過流所生成元素的hashCode() 和 equals() 去除重複元素 |
例子:
- filter
List<Employee> employees = Arrays.asList(
new Employee(1, "張三", 18, 1111.11),
new Employee(2, "李四", 49, 2222.22),
new Employee(3, "趙六", 57, 3333.33),
new Employee(4, "田七", 35, 4444.44),
new Employee(4, "田七", 35, 4444.44)
);
/**
* 中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
* 1.篩選與切片:filter
*/
@Test
public void test1() {
// 內部迭代:迭代操作由Stream API完成
employees.stream()
.filter(e -> e.getAge() > 35)
.forEach(System.out::println);
// 外部迭代
Iterator<Employee> it = employees.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
- limit
/**
* 中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
* 1.篩選與切片:limit
*/
@Test
public void test2() {
// limit;短路,找到兩個符合的就終止了
employees.stream()
.filter(e -> {
System.out.println("短路!");
return e.getAge() > 25;
})
.limit(2)
.forEach(System.out::println);
}
- skip
/**
* 中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
* 1.篩選與切片:skip
*/
@Test
public void test3() {
// limit;跳過前兩個
employees.stream()
.filter(e -> {
System.out.println("呦!");
return e.getAge() > 25;
})
.skip(2)
.forEach(System.out::println);
}
- distinct
/**
* 中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
* 1.篩選與切片:distinct
*/
@Test
public void test4() {
// distinct;去重 【employees要重寫hashcode和equals】
employees.stream()
.filter(e -> {
System.out.println("呦!");
return e.getAge() > 25;
})
.distinct()
.forEach(System.out::println);
}
2.2.2 對映
map裡傳入Function函式型介面,傳入一個引數返回一個值
方法 | 描述 |
---|---|
map | 接收lambda,將元素轉換成其他形式或提取資訊,接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。 |
flatMap | 接收一個函式作為引數,將該流中的每一個值都換成另一個流,然後把所有流連成一個流 |
例子:
public class StreamApi3 {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
@Test
public void test1() {
list.stream()
.map(s -> s.toUpperCase())
.forEach(System.out::println);
System.out.println("--------------------------");
// 這種套娃式可以用flatMap替代 (見test2)
Stream<Stream<Character>> stream = list.stream().map(StreamApi3::filterCharacter);
stream.forEach(sm->{
sm.forEach(System.out::println);
});
}
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
@Test
public void test2() {
// 扁平化maop:原來把流放入map流,現在是直接將流中的元素放入flatmap流中
// 把{a,a,a},{b,b,b} ... 轉換成了 {a,a,a,b,b,b,c,c,c}
Stream<Character> characterStream = list.stream()
.flatMap(StreamApi3::filterCharacter);
characterStream.forEach(System.out::println);
}
}
2.2.3 排序
方法 | 描述 |
---|---|
sorted() | 自然排序(Comparable) |
sorted(Comparator com) | 定製排序(Comparator) |
例子:
/**
* 中間操作:不會執行任何操作,終止操作:一次性執行全部內容,即“惰性求值”
* 3. 排序
* sorted() -- 自然排序(Comparable)
* sorted(Comparator com) -- 定製排序(Comparator)
*/
@Test
public void test1() {
// 自然排序
Stream<Employee> sorted = employees.stream().sorted();
// 按年齡排序,年齡相同按名字排序
employees.stream()
.sorted((e1, e2) -> {
if (e1.getAge() == e2.getAge()) {
return e1.getName().compareTo(e2.getName());
} else {
Integer x = e1.getAge();
return x.compareTo(e2.getAge());
}
}).forEach(System.out::println);
}
2.3 終止操作
2.3.1 查詢與匹配
方法 | 描述 |
---|---|
allMatch | 檢查是否匹配所有元素 |
anyMatch | 檢查是否至少匹配一個元素 |
noneMatch | 檢查是否沒有匹配所有元素 |
findFirst | 返回第一個元素 |
findAny | 返回當前流中的任意元素 |
count | 返回流中元素的總個數 |
max | 返回流中的最大值 |
min | 返回流中的最小值 |
例子:
- 查詢的物件
List<Employee> employees = Arrays.asList(
new Employee(1, "張三", 18, 1111.11, Status.BUSY),
new Employee(2, "李四", 49, 2222.22, Status.FREE),
new Employee(3, "趙六", 57, 3333.33, Status.BUSY),
new Employee(4, "田七", 35, 4444.44, Status.VOCATION),
new Employee(2, "李十四", 49, 2222.22, Status.FREE),
new Employee(2, "李十四", 49, 2222.22, Status.FREE),
new Employee(2, "李十四", 49, 2222.22, Status.FREE)
);
- 查詢與匹配
@Test
public void test1() {
boolean b = employees.stream()
.allMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(b);
boolean b2 = employees.stream()
.anyMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(b2);
boolean b3 = employees.stream()
.noneMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(b3);
Optional<Employee> firste = employees.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(firste.get());
Optional<Employee> anye = employees.stream()
.filter(e -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(anye.get());
// 並行
Optional<Employee> anye2 = employees.parallelStream()
.filter(e -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(anye2.get());
}
- 查詢與匹配
@Test
public void test2() {
long count = employees.stream()
.count();
System.out.println(count);
Optional<Employee> maxe = employees.stream()
.max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
System.out.println(maxe.get());
Optional<Double> min = employees.stream()
.map(Employee::getSalary)
.min(Double::compare);
System.out.println(min.get());
}
2.3.2 規約
方法 | 描述 |
---|---|
reduce | 可以將流中的元素反覆結合起來,得到一個值 |
例子:
/**
* reduce(起始值,二元運算): 規約
*/
@Test
public void test3() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 先把起始值0作為x,再從list取數放入y
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------計算公司總工資---------");
Double reduce = employees.stream()
.map(e -> e.getSalary())
.reduce(0.0, (x, y) -> x + y);
// 優化寫法(map-reduce模式)
Optional<Double> reduce1 = employees.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(reduce1.get());
}
2.3.3 收集
包括集合、計算、分組、分割槽
方法 | 描述 |
---|---|
collect | 將流轉換為其他形式,接收一個Collectot介面的實現,用於給Stream中的元素彙總的方法 |
- 集合
@Test
public void test4() {
List<String> nameList = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
nameList.forEach(System.out::println);
System.out.println("-----------過濾重複資料--------------");
Set<String> nameSet = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
nameSet.forEach(System.out::println);
System.out.println("-----------放到其他型別集合--------------");
HashSet<String> nameHashSet = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
nameHashSet.forEach(System.out::println);
}
- 計算
/**
* collect 之 計算
*/
@Test
public void test5() {
// 總數
Long collect = employees.stream()
.collect(Collectors.counting());
System.out.println(collect);
// 平均值
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
// 總和
Double sum = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
// 最大值
Optional<Employee> maxo = employees.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(maxo.get());
// 最小值
Optional<Double> mino = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(mino.get());
}
- 分組
/**
* collect 之 分組
*/
@Test
public void test6() {
// 分組
Map<Status, List<Employee>> statusListMap = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
/*statusListMap.keySet().forEach(System.out::println);
statusListMap.entrySet().forEach(System.out::println);
System.out.println(statusListMap);*/
// 多級分組
/*employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus), Collectors.groupingBy((Employee e) -> {
if (e.getAge() <= 35) {
return "青年";
}else if (e.getAge() > 20) {
return "老年";
} else {
return "少年";
}
}));*/
// 分割槽: 滿足條件的分一個區,不滿足的分一個區
Map<Boolean, List<Employee>> collect = employees.stream()
.collect(Collectors.partitioningBy(e -> e.getSalary() > 2000.0));
System.out.println(collect);
// Double:強大計算
DoubleSummaryStatistics aDouble = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(aDouble.getAverage());
System.out.println(aDouble.getMax());
System.out.println(aDouble.getCount());
// 字串拼接
String str = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(str);
}
2.4 應用
- 交易員類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Trander {
private String name;
private String city;
}
- 交易類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transaction {
private Trander trander;
private int year;
private int value;
}
- 練習
package cn.luis.stream.practise;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Luis
* @version 1.0
* @description TODO
* @date 2020/12/8 9:25
*/
public class TransactionTest {
List<Transaction> transactions;
@Before
public void before() {
Trander raoul = new Trander("Raoul", "Cambridge");
Trander mario = new Trander("Mario", "Milan");
Trander alan = new Trander("Raoul", "Cambridge");
Trander brian = new Trander("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
}
/**
* 找到2011年發生的所有交易,並按交易額排序(從低到高)
* [有條件先篩選,排序記得寫規則]
*/
@Test
public void test1() {
transactions.stream()
.filter(t -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
}
/**
* 交易員都在哪些不同的城市工作過
* [記得去重]
*/
@Test
public void test2() {
transactions.stream()
.map(t -> t.getTrander().getCity())
.distinct()
.forEach(System.out::println);
}
/**
* 查詢所有來自劍橋的交易員,並按姓名排序
*/
@Test
public void test3() {
transactions.stream()
.map(t -> t.getTrander())
.distinct()
.filter(trander -> "Cambridge".equals(trander.getCity()))
.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
.forEach(System.out::println);
System.out.println("---------------------------------");
transactions.stream()
.map(t -> t.getTrander().getName())
.sorted()
.forEach(System.out::println);
System.out.println("---------------------------------");
String reduceStr = transactions.stream()
.map(t -> t.getTrander().getName())
.sorted()
.reduce("", String::concat);
System.out.println(reduceStr);
System.out.println("---------------------------------");
// 將所有姓名組成字串,然後按照子母排排序
transactions.stream()
.map(t->t.getTrander().getName())
.flatMap(TransactionTest::filterCharacter)
.sorted()
.forEach(System.out::print);
}
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
/**
* 返回所有交易員的姓名字串,按字母順序排序
*/
@Test
public void test4() {
String collect = transactions.stream()
.map(Transaction::getTrander)
.map(Trander::getName)
.sorted(String::compareToIgnoreCase)
.collect(Collectors.joining(","));
System.out.println(collect);
}
/**
* 有沒有交易員是在米蘭工作的
*/
@Test
public void test5() {
boolean b = transactions.stream()
.map(Transaction::getTrander)
.map(Trander::getCity)
.noneMatch(c -> "Milan".equals(c));
System.out.println(!b);
}
/**
* 列印生活在劍橋的交易員的所有交易額
*/
@Test
public void test6() {
Optional<Integer> reduce = transactions.stream()
.filter(t -> "Cambridge".equals(t.getTrander().getCity()))
.map(Transaction::getValue)
.reduce(Integer::sum);
System.out.println(reduce.isPresent());
System.out.println(reduce.get());
}
/**
* 所有交易中,最高的交易額
*/
@Test
public void test7() {
Optional<Integer> max = transactions.stream()
.map(Transaction::getValue)
.max(Double::compare);
System.out.println(max.get());
System.out.println("------------------------------------------");
Optional<Integer> collect = transactions.stream()
.map(Transaction::getValue)
.collect(Collectors.maxBy((v1, v2) -> v1.compareTo(v2)));
System.out.println(collect.get());
}
}
3. DateAPI
3.1 人讀的時間
api | 描述 | |
---|---|---|
LocalDate | 日期 | 年月日 |
LocalTime | 時間 | 時分秒 |
LocalDateTime | 時間和日期 | 年月日+時分秒 |
例子:
@Test
public void test1() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ldt2 = LocalDateTime.of(2020, 12, 9, 12, 56);
System.out.println(ldt2);
LocalDateTime ldt3 = ldt.plusYears(2);
System.out.println(ldt3);
LocalDateTime ldt4 = ldt.minusMonths(2);
System.out.println(ldt4);
System.out.println("----------------------------");
int year = ldt.getYear();
int monthValue = ldt.getMonthValue();
int dayOfMonth = ldt.getDayOfMonth();
int hour = ldt.getHour();
int minute = ldt.getMinute();
int second = ldt.getSecond();
System.out.println(year);
System.out.println(monthValue);
System.out.println(dayOfMonth);
System.out.println(hour);
System.out.println(minute);
System.out.println(second);
}
結果:
2020-12-10T17:05:55.025
2020-12-09T12:56
2022-12-10T17:05:55.025
2020-10-10T17:05:55.025
----------------------------
2020
12
10
17
5
55
3.2 機器讀的時間
方法 | 描述 | |
---|---|---|
Instant | 時間戳(以unix元年:1970年1月1日00:00:00到某個時間之間的毫秒值) | |
Duration | 計算兩個“時間”之間的間隔 | |
Period | 計算兩個“日期”之間的間隔 |
例子:
@Test
public void test2() {
// 預設獲取UTC時區(格林威治)的時間
Instant in1 = Instant.now();
System.out.println(in1);
// 加8小時
OffsetDateTime odt = in1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
// 毫秒值
long haomiao = in1.toEpochMilli();
System.out.println(haomiao);
Instant instant = Instant.ofEpochSecond(1000);
}
- 計算時間間隔
/**
* Duration:計算;兩個“時間”之間的間隔
*/
@Test
public void test3() throws InterruptedException {
Instant in1 = Instant.now();
Thread.sleep(3000);
Instant in2 = Instant.now();
Duration duration = Duration.between(in1, in2);
// 獲取秒和納秒用get~(),獲取毫秒用to~()
System.out.println(duration.toMillis()); // 3001
LocalTime lt = LocalTime.now();
Thread.sleep(2000);
LocalTime lt2 = LocalTime.now();
System.out.println(Duration.between(lt, lt2).toMillis()); // 2001
System.out.println(Duration.between(lt2, lt).toMillis()); // -2001
}
- 計算日期間隔
/**
* Period:計算;兩個“日期”之間的間隔
*/
@Test
public void test4() throws InterruptedException {
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2015, 1, 1);
Period period = Period.between(ld2, ld1);
System.out.println(period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
/**
P5Y11M9D
5
11
9
*/
}
3.3 時間校正器
api | 描述 |
---|---|
TemporalAdjuster | 時間校正器 |
TemporalAdjusters | 時間校正器工具類 |
例子:
@Test
public void test5() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// 將月中的天指定為10
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
// 下一個週日
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
// 自定義:下一個工作日是什麼時候
LocalDateTime gzr = ldt.with(l -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(4);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
} else {
return ldt4.plusDays(1);
}
});
System.out.println(gzr);
System.out.println("工作日: " + gzr.getMonthValue() + "月" + gzr.getDayOfMonth() + "日");
}
3.4 格式化時間、日期
api
api | 描述 |
---|---|
DateTimeFormatter | 時間格式化 |
方法
api | 描述 | 舉例 |
---|---|---|
.ofPattern() | 定義日期轉換格式 | DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”); |
例子:
@Test
public void test6() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// DateTimeFormatter、LocalDateTime 都有format方法
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// String strDate1 = dtf2.format(ldt);
String strDate2 = ldt.format(dtf2);
System.out.println(strDate2);
LocalDateTime ldt2 = LocalDateTime.parse(strDate2, dtf2);
System.out.println(ldt2);
}
3.5 時區的處理
api | 描述 |
---|---|
ZoneId | 時區 |
ZonedDate | 時間 |
ZonedTime | 日期 |
ZonedDateTime | 日期和時間 |
例子:
/**
* 時區的處理:ZonedDate、ZonedTime、ZonedDateTime
*/
@Test
public void test7() {
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
//zoneIds.forEach(System.out::println);
//System.out.println(zoneIds.stream().count()); // 599個時區
// 指定時區
LocalDateTime now1 = LocalDateTime.now();
// 兩種指定時區方法
LocalDateTime now2 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
// 兩種指定時區方法
ZonedDateTime zdt = now1.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(now1);
System.out.println(now2);
System.out.println(zdt);
}
3.6 Instant、LocalDateTime、Date間的轉換
3.6.1 Date to LocalDateTime
/**
* Date to LocalDateTime
*/
@Test
public void test9() {
Date todayDate = new Date();
System.out.println("todayDate = " + todayDate);
LocalDateTime todayLocalDateTime = todayDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
System.out.println("todayLocalDateTime = " + todayLocalDateTime);
}
結果:
todayDate = Fri Dec 11 10:26:00 CST 2020
todayLocalDateTime = 2020-12-11T10:26:00.115
3.6.2 LocalDateTime to Date
/**
* LocalDateTime to Date
*/
@Test
public void test10() {
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("localDateTime = " + localDateTime);
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("date = " + date);
}
結果:
localDateTime = 2020-12-11T10:29:17.403
date = Fri Dec 11 10:29:17 CST 2020
3.6.3 綜合
通過:ZonedDateTime
/**
* Instant、LocalDateTime、Date轉換
*/
@Test
public void test8() {
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(zoneId);
System.out.println("instant=== " + instant);
System.out.println("zonedDateTime=== " + zonedDateTime);
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
System.out.println("localDateTime=== " + localDateTime);
Date date = Date.from(instant);
System.out.println("date=== " + date);
}
結果:
instant=== 2020-12-11T02:21:52.939Z
zonedDateTime=== 2020-12-11T10:21:52.939+08:00[Asia/Shanghai]
localDateTime=== 2020-12-11T10:21:52.939
date=== Fri Dec 11 10:21:52 CST 2020
3.6.4 DateUtil
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class Dateutili {
public static Date asDate(LocalDate localDate) {
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
public static Date asDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
public static LocalDate asLocalDate(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
public static LocalDateTime asLocalDateTime(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
4. Optional
4.1 API
api | Optional |
---|---|
位置 | java.util.Optional |
描述 | 是一個容器類,是一個可以為null的容器物件 |
4.2 方法
4.2.1 建立Optional類物件的方法
方法 | 描述 |
---|---|
Optional.of(T t) | 建立一個Optional例項 |
Optional.empty() | 建立一個空的Optional例項 |
Optional.ofNullable(T t) | 若t不為null,建立Optional例項,否則建立空例項 |
例子:
Optional.of(T t) 建立一個Optional例項
/**
* Optional.of(T t) 建立一個Optional例項
*/
@Test
public void test1() {
Girl girl = new Girl();
girl = null;
// of(T t): 保證t是非空的
Optional<Girl> girl1 = Optional.of(girl);
System.out.println(girl1);
}
Optional.ofNullable(T t) 若t不為null,建立Optional例項,否則建立空例項
@Test
public void test2() {
Girl girl = new Girl();
girl = null;
// ofNullable(T t): t可以為null
Optional<Girl> girl1 = Optional.ofNullable(girl);
System.out.println(girl1);
}
4.2.2 判斷Optional容器中是否包含物件
方法 | 描述 |
---|---|
isPresent() | 判斷是否包含物件 |
void idPresent(Consumer<? super ?> consumer) | 如果有值,就執行Consumer介面的實現程式碼,並且該值會作為引數傳給它 |
4.2.3 獲取Optional容器的物件
方法 | 描述 |
---|---|
T get() | 如果呼叫物件包含值,返回該值,否則拋異常 |
orElse(T other) | 如果呼叫物件包含值,返回該值,否則返回 other物件 |
orElseGet(Supplier<? extends ?> s) | 果呼叫物件包含值,返回該值,否則返回 s 獲取的值 |
T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果有值將其返回,否則丟擲由Supplier介面實現提供的異常 |
map(Function f) | 如果有值對其處理,並返回處理後的Optional,否則返回Optional.empty() |
flatMap(Function mapper) | 與map類似,要求返回值必須是Optional |
4.3 情景應用
定義一個boy類、girl類
- boy
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Boy {
private Girl girl;
}
- girl
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Girl {
private String name;
}
測試類
如果boy為null,則空指標會在本類丟擲,若name為空,則在getGirlName方法丟擲
原始寫法
package cn.luis.lambda.atguigu.optional;
import org.junit.Test;
import java.util.Optional;
public class OptionalTest {
public String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
/**
* 練習
* 如果boy為null,則空指標會在本類丟擲,若name為空,則在getGirlName方法丟擲
*/
@Test
public void test3() {
Boy boy = new Boy();
//boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
}
}
普通優化
package cn.luis.lambda.atguigu.optional;
import org.junit.Test;
import java.util.Optional;
public class OptionalTest {
/**
* 普通優化
*/
public String getGirlName1(Boy boy) {
if (boy != null) {
Girl girl = boy.getGirl();
if (girl != null) {
return boy.getGirl().getName();
}
}
return null;
}
/**
* 普通優化
* 如果boy為null,則空指標會在本類丟擲,若name為空,則在getGirlName方法丟擲
*/
@Test
public void test33() {
Boy boy = new Boy();
//boy = null;
String girlName = getGirlName1(boy);
System.out.println(girlName);
}
}
使用Optional優化
package cn.luis.lambda.atguigu.optional;
import org.junit.Test;
import java.util.Optional;
public class OptionalTest {
/**
* Optional優化
*/
public String getGirlName2(Boy boy) {
Girl girl = boy.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("Mary"));
System.out.println(girl1.getName());
return null;
}
/**
* Optional優化
*/
@Test
public void test333() {
//Boy boy = new Boy();
Boy boy = new Boy(new Girl("LiLy"));
Optional<Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy());
String girlName = getGirlName1(boy1);
System.out.println(girlName);
}
}
5. 可重複註解、型別註解
5.1 可重複註解
重複註解:1. 定義註解,2.定義註解容器,3在註解上新增@Repeatable(容器註解.class)
- 自定義註解
@Repeatable(MyAnns.class)
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnn {
String value() default "luis";
}
- 定義一個註解容器
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnns {
MyAnn[] value();
}
- 測試類
public class AnnotationTest {
/**
* 重複註解:1. 定義註解,2.定義註解容器,3在註解上新增@Repeatable(容器註解.class)
*/
@MyAnn("hello")
@MyAnn("world")
public void show() {
}
/**
* 利用反射獲取註解的值
*/
@Test
public void test1(){
Class<AnnotationTest> clazz = AnnotationTest.class;
try {
Method showMethod = clazz.getMethod("show");
MyAnn[] ann = showMethod.getAnnotationsByType(MyAnn.class);
for (MyAnn myAnn : ann) {
System.out.println(myAnn.value());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
5.2 型別註解
java8需要搭配框架或者自己定義註解才可
public class AnnotationTest {
/**
* 型別註解,java8需要搭配框架或者自己定義註解才可
* @Target({ElementType.TYPE, METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
*/
// 提供編譯時檢查,不允許為null
public /*@NonNull*/ Object obj = null;
}
6. ForkJoin框架
[瞭解]:對併發優化!
package cn.luis.lambda.atguigu.fork_join;
import java.util.concurrent.RecursiveTask;
/**
* @author Luis
* @version 1.0
* @description TODO RecursiveAction:無返回值
* @date 2020/12/9 9:51
*/
public class FoekJoinCalculate extends RecursiveTask<Long> {
private static final long serialVersionUID = 134656970987L;
private long start;
private long end;
// 臨界值
private static final long THRESHOLD = 10000;
public FoekJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += 1;
}
return sum;
} else {
long middle = (start + end) / 2;
FoekJoinCalculate left = new FoekJoinCalculate(start, middle);
// 拆分子任務,同時壓入執行緒佇列
left.fork();
FoekJoinCalculate right = new FoekJoinCalculate(middle + 1, end);
// 拆分子任務,同時壓入執行緒佇列
right.fork();
return left.join()+right.join();
}
}
}
- 測試類
package cn.luis.lambda.atguigu.fork_join;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
* @author Luis
* @version 1.0
* @description TODO
* @date 2020/12/9 10:20
*/
public class ForkTest {
/**
* ForkJoin框架 (和普通for對比,要資料特大才有效果,因為拆分也需要時間)
*/
@Test
public void test1() {
// 計算時間戳
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new FoekJoinCalculate(0, 10000000000L); //1876
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗費時間:" + Duration.between(start, end).toMillis()); //34
}
/**
* 普通for
*/
@Test
public void test2() {
// 計算時間戳
Instant start = Instant.now();
long sum = 0L;
for (long i = 0; i <= 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗費時間:" + Duration.between(start, end).toMillis()); //4324
}
/**
* java8並行流
*/
@Test
public void test3(){
// 生成long型隨機數
// 序列流
long reduce = LongStream.rangeClosed(0, 10000000000L)
.reduce(0, Long::sum);
System.out.println(reduce);
System.out.println("------------------------------");
// 序列流
long reduce2 = LongStream.rangeClosed(0, 10000000000L)
.parallel()
.reduce(0, Long::sum);
System.out.println(reduce2);
}
}
本文總結自B站-尚矽谷
END
相關文章
- JAVA8新特性Java
- Java8新特性 - LambdaJava
- Java8的新特性Java
- JAVA8新特性用法Java
- Java8新特性之:OptionalJava
- Java8新特性--Stream APIJavaAPI
- java8新特性stream流Java
- Java8新特性實踐Java
- Java8新特性系列-LambdaJava
- Java8新特性系列(Stream)Java
- Java8新特性系列(Interface)Java
- Java8新特性系列(Lambda)Java
- Java8 新特性詳解Java
- java8新特性學習Java
- ?Java8新特性之Optional類Java
- java8 新特性之方法引用Java
- java8 新特性之Optional 類Java
- Java8 新特性之 Optional 類Java
- Java8的八個新特性Java
- Java8新特性(1):Lambda表示式Java
- Java8常用的新特性總結Java
- Java8新特性(一)-Lambda表示式Java
- java8 新特性之Lambda 表示式Java
- java8 新特性之預設方法Java
- Java8新特性探索之Stream介面Java
- Java8新特性系列(原子性操作)Java
- Java8 新特性(一)- 介面增強Java
- Java8 新特性之 Lambda 表示式Java
- Java8新特性都到碗裡來Java
- 【java8新特性】蘭姆達表示式Java
- JAVA8 新特性(二)轉載整理Java
- Java8新特性之時間APIJavaAPI
- Java8的新特性--函式式介面Java函式
- Java8新特性之日期-時間APIJavaAPI
- java8 新特性之函式式介面Java函式
- java8 新特性之日期時間 APIJavaAPI
- Java8 新特性 —— Stream 流式程式設計Java程式設計
- java8新特性之lambda表示式(一)Java