一、Java7
1、try-with-resources
JDK7之前的寫法:
String path = "/Users/hanxiantao/Desktop/hello.txt";
OutputStream out = null;
try {
out = new FileOutputStream(path);
out.write("hello world".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用try-with-resources:
在try後的括號中初始化資源,可以實現資源⾃動關閉
String path = "/Users/hanxiantao/Desktop/hello.txt";
try (OutputStream out = new FileOutputStream(path);) {
out.write("hello world".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
注意點:
- 實現了AutoCloseable接⼝的類,在try()⾥聲明該類實例的時候,try結束後⾃動調⽤的
close()
⽅法,這個動作會早於finally⾥調⽤的⽅法 - 不管是否出現異常,try()⾥的實例都會被調⽤
close()
⽅法 - try()⾥⾯可以聲明多個⾃動關閉的對象,越早聲明的對象,會越晚被close掉
二、Java8
1、接口的增強
在JDK1.8以前接⼝⾥⾯是只能有抽象⽅法,不能有任何⽅法的實現的
JDK1.8引⼊了新的關鍵字default,使⽤default修飾⽅法,可以在接⼝⾥⾯定義具體的⽅法實現
默認⽅法:接⼝⾥⾯定義⼀個默認⽅法,又叫做擴展方法,在實現該接口時,該默認擴展方法在子類上可以直接使用
public interface Animal {
void run();
default void breath() {
System.out.println("使用氧氣呼吸");
}
}
Animal dog = new Animal() {
@Override
public void run() {
System.out.println("狗run");
}
};
dog.breath();
dog.run();
靜態方法:通過接⼝名.靜態⽅法
來訪問接⼝中的靜態⽅法
public interface Animal {
void run();
default void breath() {
System.out.println("使用氧氣呼吸");
}
static void eat() {
System.out.println("吃");
}
}
Animal.eat();
2、base64加解密API
1)、什麼是Base64編碼
Base64是⽹絡上最常⻅的⽤於傳輸8Bit字節碼的編碼⽅式之⼀,Base64就是⼀種基於64個可打印字符來表示⼆進制數據的⽅法,基於64個字符A-Z、a-z、0-9、+、/的編碼⽅式(在日常使用中我們還會看到=
或==
號出現在Base64的編碼結果中,=
在此是作爲填充字符出現),是⼀種能將任意⼆進制數據⽤64種元字符組合成字符串的⽅法,⽽這個⼆進制數據和字符串之間是可以互相轉換的
2)、JDK1.8之前相關API
使⽤JDK⾥sun.misc
下的BASE64Encoder和BASE64Decoder這兩個類
public static void main(String[] args) throws IOException {
BASE64Encoder encoder = new BASE64Encoder();
BASE64Decoder decoder = new BASE64Decoder();
String text = "hello world";
byte[] bytes = text.getBytes("UTF-8");
//編碼
String encodedText = encoder.encode(bytes);
System.out.println(encodedText);
//解碼
byte[] bytes2 = decoder.decodeBuffer(encodedText);
System.out.println(new String(bytes2, "UTF-8"));
}
執行結果:
aGVsbG8gd29ybGQ=
hello world
缺點:編碼和解碼的效率⽐較差
3)、JDK1.8相關API
Base64.Encoder encoder = Base64.getEncoder();
Base64.Decoder decoder = Base64.getDecoder();
String text = "hello world";
byte[] bytes = text.getBytes("UTF-8");
//編碼
String encodedText = encoder.encodeToString(bytes);
System.out.println(encodedText);
//解碼
byte[] bytes2 = decoder.decode(encodedText);
System.out.println(new String(bytes2, "UTF-8"));
3、時間日期處理類
參考之前的博客開發中常用Java8日期和時間相關API
4、Optional類
Optional類主要解決的是空指針異常,本質是⼀個包含有可選值的包裝類,這意味着Optional類既可以含有對象也可以爲空
1)、創建Optional類
of()
:null值作爲參數傳遞進去,則會拋異常
Student student = null;
Optional<Student> optional = Optional.of(student);
ofNullable()
:如果對象即可能是null也可能是⾮null
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
2)、訪問Optional對象的值
get()
:獲取Optional對象的值
isPresent()
:如果值存在會返回true,調⽤get()
⽅法會返回該對象⼀般使⽤get()
之前需要先驗證是否有值,不然還會拋出異常java.util.NoSuchElementException: No value present
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
if (optional.isPresent()) {
System.out.println("optional不爲空");
Student s = optional.get();
} else {
System.out.println("optional爲空");
}
3)、兜底方法
orElse()
:如果Optional對象有值則返回該值,否則返回傳遞給它的參數值
Student s1 = null;
Student s2 = new Student("小明", 22);
Student student = Optional.ofNullable(s1).orElse(s2);
System.out.println(student);
Student s1 = null;
Integer age = Optional.ofNullable(s1).map(obj -> obj.getAge()).orElse(6);
System.out.println(age);
5、Lambda表達式
函數編程:即可理解是將⼀個函數(也稱爲⾏爲)作爲⼀個參數進⾏傳遞, ⾯向對象編程是對數據的抽象(各種各樣的POJO類),⽽函數式編程則是對⾏爲的抽象(將⾏爲作爲⼀個參數進⾏傳遞)
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);//降序
}
});
list.stream().forEach(e -> System.out.println(e));
或者:
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, Comparator.reverseOrder());//降序
list.stream().forEach(e -> System.out.println(e));
使用Lambda表達式:
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, (a, b) -> b.compareTo(a));//降序
list.stream().forEach(e -> System.out.println(e));
Lambda表達式使⽤場景:⼀個接⼝中只包含⼀個⽅法,則可以使⽤Lambda表達式,這樣的接⼝稱之爲函數接⼝語法 (params) -> expression
第⼀部分爲括號內⽤逗號分隔的形式參數,參數是函數式接⼝⾥⾯⽅法的參數;第⼆部分爲⼀個箭頭符號->;第三部分爲⽅法體,可以是表達式和代碼塊
參數列表:
括號中參數列表的數據類型可以省略不寫
括號中的參數只有⼀個,那麼參數類型和()都可以省略不寫
⽅法體:
如果{}中的代碼只有⼀⾏,⽆論是否返回值,可以省略{},return、分號要⼀起省略;其他則需要加上
Lambda表達式的實現⽅式在本質是以匿名內部類的⽅式進⾏實現
6、@FunctionalInterface註解
@FunctionalInterface
:聲明該接口爲函數式接口
函數式接口:當然首先是一個接口,然後就是在這個接口裏面只能有一個抽象方法
public static void main(String[] args) {
System.out.println(operator(5, 20, (x, y) -> x + y));
System.out.println(operator(5, 20, (x, y) -> x - y));
System.out.println(operator(5, 20, (x, y) -> x * y));
System.out.println(operator(5, 20, (x, y) -> x / y));
}
public static Integer operator(Integer x, Integer y,
OperFunction<Integer, Integer> of) {
return of.operator(x, y);
}
7、函數式編程
1)、Function接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
傳⼊⼀個值經過apply()
的計算返回另⼀個值,T爲入參類型,R爲出參類型
將轉換邏輯提取出來,解耦合
Function<Integer, Integer> func = p -> p + 100;
System.out.println(func.apply(100));//200
map()
方法:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
2)、BiFunction接口
Function只能接收⼀個參數,如果要傳遞兩個參數,則⽤BiFunction
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
System.out.println(biFunction.apply(10, 20));//30
3)、Consumer接口
Consumer爲消費型接⼝:有⼊參,⽆返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
⽤途: 因爲沒有出參,常⽤於打印、發送短信等消費動作
Consumer<String> consumer = a -> System.out.println(a);
consumer.accept("hello world");
forEach()
方法:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
List<String> list = Arrays.asList("hello", "world");
list.forEach(e -> System.out.println(e));
4)、Supplier接口
Supplier爲供給型接⼝:⽆⼊參,有返回值
@FunctionalInterface
public interface Supplier<T> {
T get();
}
⽤途:泛型⼀定和⽅法的返回值類型是⼀種類型,如果需要獲得⼀個數據,並且不需要傳⼊參數,可以使⽤Supplier接⼝,例如:⽆參的⼯⼚⽅法,即⼯⼚設計模式創建對象,簡單來說就是提供者
public static void main(String[] args) {
Student student = newStudent();
System.out.println(student.getName());
}
public static Student newStudent() {
Supplier<Student> supplier = () -> {
Student student = new Student();
student.setName("小明");
return student;
};
return supplier.get();
}
5)、Predicate接口
Predicate爲斷⾔型接⼝:有⼊參,有返回值,返回值類型確定是boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
⽤途:接收⼀個參數,⽤於判斷是否滿⾜⼀定的條件,過濾數據
public static void main(String[] args) {
List<String> list = Arrays.asList("aaa", "xsda", "a2weqe", "akkk", "eeq");
List<String> result = filter(list, obj -> obj.startsWith("a"));
System.out.println(result);
}
public static List<String> filter(List<String> list, Predicate<String> predicate) {
List<String> result = new ArrayList<>();
for (String str : list) {
if (predicate.test(str)) {
result.add(str);
}
}
return result;
}
filter()
方法:
Stream<T> filter(Predicate<? super T> predicate);
6)、⽅法引⽤與構造函數引⽤
//靜態函數
Function<String, Integer> func = Integer::parseInt;
System.out.println(func.apply("1024"));
//非靜態函數
String context = "hello world";
Function<Integer, String> func2 = context::substring;
System.out.println(func2.apply(1));
//構造函數 多個參數
BiFunction<String, Integer, Student> biFunction = Student::new;
System.out.println(biFunction.apply("小明", 22));
//構造函數 單個參數
Function<String, Student> fuc3 = Student::new;
System.out.println(fuc3.apply("小明"));
8、集合框架
1)、流stream的使⽤
1)什麼是stream
stream中⽂稱爲流,通過將集合轉換爲⼀種叫做流的元素隊列,通過聲明性⽅式,能夠對集合中的每個元素進⾏⼀系列並⾏或串⾏的流⽔線操作
元素是特定類型的對象,所以元素集合看作⼀種流,流在管道中傳輸,且可以在管道的節點上進⾏處理,⽐如排序、聚合、過濾等操作
2)操作詳情
- 數據元素:原始集合,如List、Set、Map等
- ⽣成流:可以是串⾏流
stream()
或者並⾏流parallelStream()
- 中間操作:可以是排序、聚合、過濾、轉換等
- 終端操作:很多流操作本身就會返回⼀個流,所以多個操作可以直接連接起來,最後統⼀進⾏收集
2)、map()
方法
將流中的每⼀個元素T映射爲R(類似類型轉換)
場景:轉換對象,如JavaWeb開發中集合⾥⾯的DO對象轉換爲DTO對象
List<User> list = Arrays.asList(new User(1, "Tom", "123"),
new User(2, "Jack", "123456"));
List<Object> userDTOList = list.stream().map(obj -> {
UserDTO dto = new UserDTO(obj.getId(), obj.getUsername());
return dto;
}).collect(Collectors.toList());
System.out.println(userDTOList);
3)、filter()
方法
⽤於通過設置的條件過濾出元素
需求:過濾出字符串⻓度⼤於5的字符串
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
List<String> resultList = list.stream().filter(obj -> obj.length() > 5)
.collect(Collectors.toList());
System.out.println(resultList);
4)、sorted()
方法
sorted()
對流進⾏⾃然排序,其中的元素必須實現Comparable接⼝
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
List<String> resultList = list.stream().sorted()
.collect(Collectors.toList());
System.out.println(resultList);
sorted(Comparator<? super T> comparator)
⽤來⾃定義升降序
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
//根據長度進行生序排序
List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length()))
.collect(Collectors.toList());
System.out.println(resultList);
//根據長度進行降序排序
// List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(), Comparator.reverseOrder()))
// .collect(Collectors.toList());
List<String> resultList2 = list.stream().sorted(Comparator.comparing(String::length).reversed())
.collect(Collectors.toList());
System.out.println(resultList2);
5)、limit()
方法
截斷流使其最多隻包含指定數量的元素
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
//截取長度前三的元素
List<String> resultList = list.stream().sorted(Comparator.comparing(String::length).reversed())
.limit(3).collect(Collectors.toList());
System.out.println(resultList);
6)、allMatch()
方法
檢查是否匹配所有元素,只有全部符合才返回true
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
boolean flag = list.stream().allMatch(obj -> obj.length() > 1);
System.out.println(flag);//true
boolean flag2 = list.stream().allMatch(obj -> obj.length() > 5);
System.out.println(flag2);//false
7)、anyMatch()
方法
檢查是否⾄少匹配⼀個元素
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
boolean flag = list.stream().anyMatch(obj -> obj.length() > 5);
System.out.println(flag);//true
8)、max()
方法
最大值
List<Student> list = Arrays.asList(new Student(10), new Student(12),
new Student(11), new Student(9));
Optional<Student> optional = list.stream().max(Comparator.comparingInt(Student::getAge));
System.out.println(optional.get().getAge());
9)、min()
方法
最小值
List<Student> list = Arrays.asList(new Student(10), new Student(12),
new Student(11), new Student(9));
Optional<Student> optional = list.stream().min(Comparator.comparingInt(Student::getAge));
System.out.println(optional.get().getAge());
10)、並行流
集合做重複的操作,如果使⽤串⾏執⾏會相當耗時,因此⼀般會採⽤多線程來加快,Java8的parallelStream()
⽤Fork/Join框架提供了併發執⾏能⼒
底層原理:線程池(ForkJoinPool)維護⼀個線程隊列,分割任務,將⽗任務拆分成⼦任務
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
//順序輸出
numbers.stream().forEach(System.out::println);
System.out.println("--------------------");
//並⾏亂序輸出
numbers.parallelStream().forEach(System.out::println);
1)parallelStream()
並⾏是否⼀定⽐Stream串⾏快嗎?
錯誤,數據量少的情況,可能串⾏更快,ForkJoin會耗性能
2)多數情況下並⾏⽐串⾏快,是否可以都⽤並⾏
不⾏,部分情況會有線程安全問題,parallelStream()
⾥⾯使⽤的外部變量,⽐如集合⼀定要使⽤線程安全集合,不然就會引發多線程安全問題
for (int i = 0; i < 10; ++i) {
List<Integer> list = new ArrayList<>();
IntStream.range(0, 100).parallel().forEach(list::add);
System.out.println(list.size());
}
執行拋出異常java.lang.ArrayIndexOutOfBoundsException
for (int i = 0; i < 10; ++i) {
List<Integer> list = new CopyOnWriteArrayList<>();
IntStream.range(0, 100).parallel().forEach(list::add);
System.out.println(list.size());
}
size均爲100
11)、reduce()
方法
1)reduce(BinaryOperator<T> accumulator)
方法
//累加器
Integer result = Stream.of(1, 2, 3, 4, 5)
.reduce((item1, item2) -> item1 + item2).get();
System.out.println(result);
2)reduce(T identity, BinaryOperator<T> accumulator)
方法
//初始值爲10,在此基礎上進行累加
Integer result = Stream.of(1, 2, 3, 4, 5)
.reduce(10, (sum, item) -> sum + item);
System.out.println(result);
12)、forEach()
方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
注意點:
- 不能修改包含外部變量的值
- 不能⽤break或者return或者continue等關鍵詞結束或者跳過循環
9、收集器和集合統計
1)、收集器collector
collect()
方法是一個終端操作,⽤於對流中的數據進⾏歸集操作,collect()
方法接收的參數是⼀個Collector
<R, A> R collect(Collector<? super T, A, R> collector);
Collector:收集器,也是⼀個接⼝,它的⼯具類Collectors提供了很多⼯⼚⽅法
Collectors:⼯具類,提供了很多常⻅的收集器實現
Collectors.toList()
Collectors.toMap()
Collectors.toSet()
Collectors.toCollection():⽤⾃定義的實現Collection的數據結構收集
1)Collectors.toCollection(LinkedList::new)
2)Collectors.toCollection(CopyOnWriteArrayList::new)
3)Collectors.toCollection(TreeSet::new)
案例:
List<User> list = Arrays.asList(new User(1, "Tom"),
new User(2, "Jack"), new User(1, "Lucky"));
//如果有重複的key,則保留key1,捨棄key2
Map<Integer, User> map = list.stream().collect(
Collectors.toMap(User::getId, e -> e, (k1, k2) -> k1));
System.out.println(map);
2)、joining()
方法
拼接函數joining()
public static Collector<CharSequence, ?, String> joining()
//參數1:元素之間的連接符
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
//參數1:元素之間的連接符 參數2:前綴 參數3:後綴
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix)
案例:
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
String result = list.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(result);
3)、partitioningBy()
方法
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
需求:根據list⾥⾯進⾏分組,字符串⻓度⼤於4的爲⼀組,其他爲另外⼀組
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
Map<Boolean, List<String>> map = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 4));
System.out.println(map);
4)、groupingBy()
方法
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
需求:根據學⽣所在的省份,進⾏分組
List<Student> students = Arrays.asList(new Student("⼴東", 23),
new Student("⼴東", 24), new Student("⼴東", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
Map<String, List<Student>> map = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince()));
map.forEach((key, value) -> {
System.out.println(key);
value.stream().forEach(e -> System.out.println(e.getAge()));
System.out.println("--------------------");
});
需求:統計各個省份的⼈數
List<Student> students = Arrays.asList(new Student("⼴東", 23),
new Student("⼴東", 24), new Student("⼴東", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
Map<String, Long> map = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting()));
map.forEach((key, value) -> {
System.out.println(key);
System.out.println(value);
System.out.println("--------------------");
});
5)、summarizing()
集合統計
summarizing()
可以⼀個⽅法把統計相關的基本上都完成,主要有如下幾種:
summarizingInt()
summarizingDouble()
summarizingLong()
List<Student> students = Arrays.asList(new Student("⼴東", 23),
new Student("⼴東", 24), new Student("⼴東", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
System.out.println("平均值:" + summaryStatistics.getAverage());
System.out.println("⼈數:" + summaryStatistics.getCount());
System.out.println("最⼤值:" + summaryStatistics.getMax());
System.out.println("最⼩值:" + summaryStatistics.getMin());
System.out.println("總和:" + summaryStatistics.getSum());