Java进阶(八)Stream、异常体系
八、Stream、異常體系
需要學會什么?
- 不可變集合:有些業務場景下需要有不可變集合對象,Java如何得到不可變集合對象。
- Stream流:集合自己提供的API非常繁瑣。JDK8開始,得益于Lambda,提供了操作集合、數組更好用的技術:Stream。
- 認識異常體系:程序一旦出現了bug則會終止,如何經理避免程序出現異常,出現異常如何進行處理讓程序更穩健。
- 日志框架:系統在開發階段或者上線后,一旦業務出現問題,需要有信息去定位,如何記錄程序的運行情況?
1.創建不可變集合
什么是不可變集合?
- 不可變集合,就是不可被修改的集合。
- 集合的數據項在創建的時候提供,并且在整個生命周期都不可改變,否則報錯。
為什么要創建不可變集合?
- 如果要某個數據不能被修改,把它防御性地拷貝到不可變集合中是個很好的實踐。
- 或者當集合對象被不信任的庫調用時,不可變形式是安全的。
如何創建不可變集合?
- 在List、Set、Map接口中,都存在of方法,可以創建一個不可變的集合。
- 這個集合不能添加,不能刪除,不能修改。
| static List of(E…elements) | 創建一個具有指定元素的List集合對象。 |
| static Set of(E…elements) | 創建一個具有指定元素的Set集合對象。 |
| static <K, V> Map <K, V> of(E…elements) | 創建一個具有指定元素的Map集合對象。 |
Test.java
import java.util.List; import java.util.Map; import java.util.Set;public class Test {public static void main(String[] args) {// 不可變List集合 不能增加/刪除/修改List<Double> list = List.of(569.9, 700.9, 499.9, 600.0, 500.0);// 700.9System.out.println(list.get(1));// [569.9, 700.9, 499.9, 600.0, 500.0]System.out.println(list);// 不可變Set集合 不能增加/刪除/修改 不允許元素重復Set<String> set = Set.of("白子畫", "花千骨", "霓漫天");// [花千骨, 白子畫, 霓漫天]System.out.println(set);// 不可變Map集合 不能增加/刪除/修改 不允許鍵重復Map<String, Integer> map = Map.of("顯示屏", 2, "主機", 1, "鍵盤", 1, "鼠標", 1);// {鍵盤=1, 顯示屏=2, 主機=1, 鼠標=1}System.out.println(map);} }2.Stream流
a.Stream流的概述
什么是Stream流?
- 在Java8中,得益于Lambda所帶來的函數式編程,引入了一個全新的Stream流概念。
- 目的:用于簡化集合和數組操作的API。
案例:體驗Stream流的作用。
需求:
-
創建一個集合,存儲多個字符串元素。
List<String> list = new ArrayList<>();list.add("張無忌");list.add("趙敏");list.add("周芷若");list.add("小昭");list.add("殷離");list.add("張翠山");list.add("謝遜");list.add("滅絕師太");list.add("張三豐");list.add("張中");list.add("張士誠"); -
把集合中所有以”張“開頭的元素存儲到一個新的集合。
-
把”張“開頭的集合中的長度為3的元素存儲到一個新的集合。
-
遍歷上一步得到的集合中的元素輸出。
Test.java
import java.util.ArrayList; import java.util.Collections; import java.util.List;public class Test {public static void main(String[] args) {// 創建集合List<String> names = new ArrayList<>();Collections.addAll(names, "張無忌", "趙敏", "周芷若", "小昭", "殷離", "張翠山", "謝遜", "滅絕師太", "張三豐", "張中", "張士誠");// [張無忌, 趙敏, 周芷若, 小昭, 殷離, 張翠山, 謝遜, 滅絕師太, 張三豐, 張中, 張士誠]System.out.println(names);// 從集合中找出姓"張"的放到新集合List<String> zhangList = new ArrayList<>();for (String name : names) {if (name.startsWith("張")) {zhangList.add(name);}}// [張無忌, 張翠山, 張三豐, 張中, 張士誠]System.out.println(zhangList);// 把"張"開頭的集合中的長度為3的元素存儲到一個新的集合List<String> zhangThreeList = new ArrayList<>();for (String name : zhangList) {if (name.length() == 3) {zhangThreeList.add(name);}}// [張無忌, 張翠山, 張三豐, 張士誠]System.out.println(zhangThreeList);/* Stream實現*/names.stream().filter(s -> s.startsWith("張")).filter(s -> s.length() == 3).forEach(System.out::println);names.stream().filter(s -> s.startsWith("張") && s.length() == 3).forEach(System.out::println);} }Stream流式思想的核心:
總結:
- 簡化集合、數組操作的API。結合了Lambda表達式。
b.Stream流的獲取
Stream流的三類方法:
- 獲取Stream流。
- 創建一條流水線,并把數據放到流水線上準備進行操作。
- 中間方法。
- 流水線上的操作。依次操作完畢之后,還可以繼續進行其他操作。
- 終結方法。
- 一個Stream流只能由一個終結方法,是流水線上的最后一個操作。
Stream操作集合或者數組的第一步是先得到Stream流,然后才能使用流的功能。
集合獲取Stream流的方式:
- 可以使用Collection接口中的默認方法stream()生成流。
| defalut Stream stream() | 獲取當前集合對象的Stream流。 |
數組獲取Stream流的方式:
| public static Stream steam(T[] array) | 獲取當前數組的Stream流。 |
| public static Stream of(T…values) | 獲取當前數組/可變數據的Stream流。 |
Test.java
import java.util.*; import java.util.stream.Stream;public class Test {public static void main(String[] args) {/* Collection集合獲取流 */Collection<String> list = new ArrayList<>();Stream<String> listStream = list.stream();/* Map集合獲取流 */Map<String, Integer> map = new HashMap<>();// 鍵流Stream<String> keyStream = map.keySet().stream();// 值流Stream<Integer> valueStream = map.values().stream();// 鍵值對流(拿整體)Stream<Map.Entry<String, Integer>> keyValueStream = map.entrySet().stream();/* 數組獲取流 */String[] names = {"白子畫", "花千骨", "殺阡陌"};Stream<String> stringStream = Arrays.stream(names);Stream<String> stringStream1 = Stream.of(names);} }c.Stream流的常用API
Stream流的常用API(中間操作方法):
| Stream filter(Predicate<? super T> predicate) | 用于對流中的數據進行過濾。 |
| Stream limit(long maxSize) | 獲取前幾個元素。 |
| Stream skip(long n) | 跳過前幾個元素。 |
| Stream distinct() | 去除流中重復的元素。依賴(hashCode()和equals()) |
| static Stream concat(Stream a, Stream b) | 合并a和b兩個流為一個流。 |
注意:
- 中間方法也稱為非終結方法,調用完成后返回新的Stream流可以繼續使用,支持鏈式編程。
- 在Stream流中無法直接修改集合、數組中的數據。
Test.java
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream;public class Test {public static void main(String[] args) {// 創建集合List<String> list = new ArrayList<>();Collections.addAll(list, "張無忌", "趙敏", "周芷若", "小昭", "殷離", "張翠山", "謝遜", "滅絕師太", "張三豐", "張中", "張士誠");/* 對流中的數據進行過濾 */// Stream<T> filter(Predicate<? super T> var1)list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {return s.startsWith("張");}});list.stream().filter(s -> s.startsWith("張")).forEach(System.out::println);/* 統計元素個數 */long size = list.stream().filter(s -> s.length() == 3).count();System.out.println(size);/* 獲取前幾個元素 */list.stream().filter(s -> s.startsWith("張")).limit(2).forEach(System.out::println);/* 跳過前幾個元素 */list.stream().filter(s -> s.startsWith("張")).skip(2).forEach(System.out::println);/* Map加工方法 */// 給集合元素的前面都加上一個標識list.stream().map(s -> "加工" + s).forEach(System.out::println);// 把所有名字都加工成一個學生對象list.stream().map(s -> new Student(s)).forEach(System.out::println);list.stream().map(Student::new).forEach(System.out::println);/* 合并流 */Stream<String> stringStream = list.stream().filter(s -> s.startsWith("張"));Stream<String> stringStream1 = Stream.of("Java1", "Java2");// 合并 // Stream<String> stringStreamConcat = Stream.concat(stringStream, stringStream1); // stringStreamConcat.forEach(System.out::println);// 合并兩個不同類型的流 要用兩個類型的共同父類來接Stream<Integer> stringStream2 = Stream.of(99, 100);Stream<Object> stringStreamConcat1 = Stream.concat(stringStream, stringStream2);stringStreamConcat1.forEach(System.out::println);} }Stream流的常見終結操作方法:
| void forEach(Consumer action) | 對此流的每個元素執行遍歷操作。 |
| long count() | 返回此流中的元素數。 |
注意:終結操作方法,調用完成后流就無法繼續使用了,原因是不會返回Stream了。
總結:
- 終結方法后流不可以繼續使用,非終結方法會返回新的流,支持鏈式編程。
?
d.Stream流的綜合應用
需求:
- 某個公司的研發部門,分為開發一部和二部,現在需要進行年終數據結算。
分析:
- 員工信息至少包含了(姓名、性別、工資、獎金、處罰記錄)。
- 開發一部有4名員工、開發二部有5名員工。
- 分別篩選處2個部門的最高工資的員工信息,封裝成優秀員工對象TopPerformer。
- 分別統計出2個部門的平均月收入,要求去掉最高和最低工資。
- 統計2個開發部門整體的平均工資,要求去掉最高和最低工資。
Employee.java
import java.util.List;public class Employee {// 姓名private String name;// 性別private char sex;// 月薪private double salary;// 獎金private double bonus;// 處罰private String punish;public Employee() {}public Employee(String name, char sex, double salary, double bonus, String punish) {this.name = name;this.sex = sex;this.salary = salary;this.bonus = bonus;this.punish = punish;}public String getName() {return name;}public void setName(String name) {this.name = name;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public double getBonus() {return bonus;}public void setBonus(double bonus) {this.bonus = bonus;}public String getPunish() {return punish;}public void setPunish(String punish) {this.punish = punish;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", sex=" + sex +", salary=" + salary +", bonus=" + bonus +", punish='" + punish + '\'' +'}';} }TopPerformer.java
public class TopPerformer {// 姓名private String name;// 月薪private double salary;public TopPerformer() {}public TopPerformer(String name, double salary) {this.name = name;this.salary = salary;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}@Overridepublic String toString() {return "TopPerformer{" +"name='" + name + '\'' +", salary=" + salary +'}';} }Test.java
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.stream.Stream;public class Test {// 各部門所有月收入public static double allMoney = 0;// 兩個部門所有月收入public static double allMoneyAll = 0;public static void main(String[] args) {// 開發一部List<Employee> employeeList1 = new ArrayList<>();employeeList1.add(new Employee("白子畫", '男', 10000, 200000000, null));employeeList1.add(new Employee("花千骨", '女', 10000, 2000, "頂撞師父"));employeeList1.add(new Employee("殺阡陌", '男', 10000, 200000, null));// 開發二部List<Employee> employeeList2 = new ArrayList<>();employeeList2.add(new Employee("張無忌", '男', 30000, 2000000, "錯放陳友諒"));employeeList2.add(new Employee("趙敏", '女', 10000, 20000, null));employeeList2.add(new Employee("周芷若", '女', 10000, 2000, "反復無常"));employeeList2.add(new Employee("小昭", '女', 10000, 20000, null));employeeList2.add(new Employee("殷離", '女', 10000, 10000, null));employeeList2.add(new Employee("張翠山", '男', 10000, 500, "沖動易怒惹人厭"));// 最高工資的員工 // Employee topSalaryEmployee1 = employeeList1.stream().max((employee1, employee2) -> { // return Double.compare(employee1.getSalary() * 12 + employee1.getBonus(), employee2.getSalary() * 12 + employee2.getBonus()); // }).get(); // System.out.println(topSalaryEmployee1); // // Employee topSalaryEmployee2 = employeeList2.stream().max((employee1, employee2) -> { // return Double.compare(employee1.getSalary() * 12 + employee1.getBonus(), employee2.getSalary() * 12 + employee2.getBonus()); // }).get(); // System.out.println(topSalaryEmployee2);/* 封裝成優秀員工對象 */// 封裝成優秀員工對象 開發一部TopPerformer topPerformer1 = employeeList1.stream().max(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {return 0;}}).map(employee -> new TopPerformer(employee.getName(), employee.getSalary() * 12 + employee.getBonus())).get();System.out.println(topPerformer1);// 封裝成優秀員工對象 開發二部TopPerformer topPerformer2 = employeeList2.stream().max(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {return 0;}}).map(employee -> new TopPerformer(employee.getName(), employee.getSalary() * 12 + employee.getBonus())).get();System.out.println(topPerformer2);/* 統計平均月收入 要求去掉最高和最低工資 */// 統計平均月收入 要求去掉最高和最低工資 開發一部employeeList1.stream().sorted(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {return Double.compare(employee1.getSalary() * 12 + employee1.getBonus(), employee2.getSalary() * 12 + employee2.getBonus());}}).skip(1).limit(employeeList1.size() - 2).forEach(employee -> {// 去掉最高和最低工資后的工資總和allMoney += (employee.getSalary() * 12 + employee.getBonus());});System.out.println("開發一部的平均工資是:" + allMoney / (employeeList1.size() - 2));// 統計平均月收入 要求去掉最高和最低工資 開發二部allMoney = 0;employeeList2.stream().sorted(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {return Double.compare(employee1.getSalary() * 12 + employee1.getBonus(), employee2.getSalary() * 12 + employee2.getBonus());}}).skip(1).limit(employeeList2.size() - 2).forEach(employee -> {// 去掉最高和最低工資后的工資總和allMoney += (employee.getSalary() * 12 + employee.getBonus());});System.out.println("開發二部的平均工資是:" + allMoney / (employeeList2.size() - 2));/* 統計2個開發部門整體的平均工資,要求去掉最高和最低工資 */Stream<Employee> employeeStream1 = employeeList1.stream();Stream<Employee> employeeStream2 = employeeList2.stream();Stream<Employee> employeeStreamAll = Stream.concat(employeeStream1, employeeStream2);employeeStreamAll.sorted(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {return Double.compare(employee1.getSalary() * 12 + employee1.getBonus(), employee2.getSalary() * 12 + employee2.getBonus());}}).skip(1).limit(employeeList1.size() + employeeList2.size() - 2).forEach(employee -> {allMoneyAll += (employee.getSalary() * 12 + employee.getBonus());});System.out.println("2個開發部門整體的平均工資是:" + allMoneyAll / (employeeList1.size() + employeeList1.size() - 2));} }e.收集Stream流
Stream流的收集操作:
- 收集Stream流的含義:就是把Stream流操作后的結果數據轉回到集合或者數組中去。
- Stream流:方便操作集合/數組的手段。
- 集合/數組:才是開發中的目的。
Stream流的收集方法:
| R collect(Collector collector) | 開始收集Stream流,指定收集器。 |
Collectors工具類提供了具體的收集方式:
| public static Collector toList() | 把元素收集到List集合中。 |
| public static Collector toSet() | 把元素收集到Set集合中。 |
| public static Collector toMap(Function keyMapper, Function valueMapper) | 把元素收集到Map集合中。 |
Test.java
import java.util.*; import java.util.function.IntFunction; import java.util.stream.Collectors; import java.util.stream.Stream;/*** 目標:收集Stream流的數據到 集合或數組中去。*/ public class Test {public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "張無忌", "趙敏", "周芷若", "小昭", "殷離", "張翠山", "謝遜", "滅絕師太", "張三豐", "張中", "張士誠");System.out.println(list);Stream<String> stringStream1 = list.stream().filter(s -> s.startsWith("張"));// 得到不可變集合// System.out.println(stringStream1.toList());// 把元素收集到List集合中List<String> zhangList = stringStream1.collect(Collectors.toList());// [張無忌, 張翠山, 張三豐, 張中, 張士誠]System.out.println(zhangList);Stream<String> stringStream2 = list.stream().filter(s -> s.startsWith("張"));// 把元素收集到Set集合中Set<String> zhangSet = stringStream2.collect(Collectors.toSet());// [張翠山, 張中, 張三豐, 張無忌, 張士誠]System.out.println(zhangSet);Stream<String> stringStream3 = list.stream().filter(s -> s.startsWith("張"));// 把元素收集到數組中Object[] array = stringStream3.toArray();// 拓展String[] stringArray = stringStream3.toArray(new IntFunction<String[]>() {@Overridepublic String[] apply(int i) {return new String[i];}});// [張無忌, 張翠山, 張三豐, 張中, 張士誠]System.out.println(Arrays.toString(array));} }總結:
- Stream流是操作集合/數組的手段。
- 操作的結果數據最終要恢復到集合或者數組中去。
3.異常處理
a.異常概述、體系
什么是異常?
- 異常是程序在“編譯”或者“執行”的過程中可能出現的問題。(注意:語法錯誤不算在異常體系中。)
- 比如:數組索引越界、空指針異常、日期格式化異常,等…。
為什么要學習異常?
- 異常一旦出現了,如果沒有提前處理,程序就會退出JVM虛擬機而終止。
- 研究異常并且避免異常,然后提前處理異常,體現的是程序的安全性、健壯性。
異常體系:
#mermaid-svg-qCagFyvomFmaJPhV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-qCagFyvomFmaJPhV .error-icon{fill:#552222;}#mermaid-svg-qCagFyvomFmaJPhV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qCagFyvomFmaJPhV .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-qCagFyvomFmaJPhV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qCagFyvomFmaJPhV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qCagFyvomFmaJPhV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qCagFyvomFmaJPhV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qCagFyvomFmaJPhV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qCagFyvomFmaJPhV .marker.cross{stroke:#333333;}#mermaid-svg-qCagFyvomFmaJPhV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qCagFyvomFmaJPhV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qCagFyvomFmaJPhV .cluster-label text{fill:#333;}#mermaid-svg-qCagFyvomFmaJPhV .cluster-label span{color:#333;}#mermaid-svg-qCagFyvomFmaJPhV .label text,#mermaid-svg-qCagFyvomFmaJPhV span{fill:#333;color:#333;}#mermaid-svg-qCagFyvomFmaJPhV .node rect,#mermaid-svg-qCagFyvomFmaJPhV .node circle,#mermaid-svg-qCagFyvomFmaJPhV .node ellipse,#mermaid-svg-qCagFyvomFmaJPhV .node polygon,#mermaid-svg-qCagFyvomFmaJPhV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qCagFyvomFmaJPhV .node .label{text-align:center;}#mermaid-svg-qCagFyvomFmaJPhV .node.clickable{cursor:pointer;}#mermaid-svg-qCagFyvomFmaJPhV .arrowheadPath{fill:#333333;}#mermaid-svg-qCagFyvomFmaJPhV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qCagFyvomFmaJPhV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qCagFyvomFmaJPhV .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-qCagFyvomFmaJPhV .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-qCagFyvomFmaJPhV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qCagFyvomFmaJPhV .cluster text{fill:#333;}#mermaid-svg-qCagFyvomFmaJPhV .cluster span{color:#333;}#mermaid-svg-qCagFyvomFmaJPhV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qCagFyvomFmaJPhV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Throwable Error Exception RuntimeException及其子類 除RuntimeException之外所有的異常Error:
- 系統級別問題,JVM退出等,代碼無法控制。
Exception:java.lang包下,稱為異常類,它代表程序本身可以處理的問題。
- RuntimException及其子類:運行時異常,編譯階段不會報錯。(如:空指針異常、數組索引越界異常等。)
- 除RuntimeException之外所有的異常:編譯時異常,編譯期必須處理的,否則程序不能通過編譯。(日期格式化異常。)
編譯時異常和運行時異常:
#mermaid-svg-e3o5gp5L2577rjbQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .error-icon{fill:#552222;}#mermaid-svg-e3o5gp5L2577rjbQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e3o5gp5L2577rjbQ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-e3o5gp5L2577rjbQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e3o5gp5L2577rjbQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e3o5gp5L2577rjbQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e3o5gp5L2577rjbQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e3o5gp5L2577rjbQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e3o5gp5L2577rjbQ .marker.cross{stroke:#333333;}#mermaid-svg-e3o5gp5L2577rjbQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e3o5gp5L2577rjbQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .cluster-label text{fill:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .cluster-label span{color:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .label text,#mermaid-svg-e3o5gp5L2577rjbQ span{fill:#333;color:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .node rect,#mermaid-svg-e3o5gp5L2577rjbQ .node circle,#mermaid-svg-e3o5gp5L2577rjbQ .node ellipse,#mermaid-svg-e3o5gp5L2577rjbQ .node polygon,#mermaid-svg-e3o5gp5L2577rjbQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e3o5gp5L2577rjbQ .node .label{text-align:center;}#mermaid-svg-e3o5gp5L2577rjbQ .node.clickable{cursor:pointer;}#mermaid-svg-e3o5gp5L2577rjbQ .arrowheadPath{fill:#333333;}#mermaid-svg-e3o5gp5L2577rjbQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e3o5gp5L2577rjbQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e3o5gp5L2577rjbQ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-e3o5gp5L2577rjbQ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-e3o5gp5L2577rjbQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e3o5gp5L2577rjbQ .cluster text{fill:#333;}#mermaid-svg-e3o5gp5L2577rjbQ .cluster span{color:#333;}#mermaid-svg-e3o5gp5L2577rjbQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-e3o5gp5L2577rjbQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Javac.exe 編譯時異常, 是在編譯成class文件時必須要處理的異常,也稱之為受檢異常. Java.exe 運行時異常, 在編譯成class文件不需要處理, 在運行字節碼文件時可能出現的異常. Java文件 字節碼文件 運行結果- 編譯時異常就是編譯的時候出現的異常。
- 運行時異常就是在運行時出現的異常。
總結:
- 異常就是代碼在編譯或者執行的過程中可能出現的錯誤。
- 編譯時異常、運行時異常。
- 編譯時異常:沒有繼承RuntimeException的異常,編譯階段就會出錯。
- 運行時異常:繼承自RuntimeException的異?;蚱渥宇?#xff0c;編譯階段不報錯,運行可能報錯。
- 避免異常的出現,同時處理可能出現的異常,讓代碼更穩健。
b.常見運行時異常
運行時異常示例:
- 數組索引越界異常:ArrayIndexOutOfBoundsException。
- 空指針異常:NullPointerException,直接輸出沒有問題,但是調用空指針的變量的功能就會報錯。
- 數字操作異常:arithmeticException。
- 類型轉換異常:ClassCastException。
- 數字轉換異常:NumberFormatException。
運行時異常:一般是程序員業務沒有考慮好或者是編程邏輯不嚴謹引起的程序錯誤。
Test.java
public class Test {public static void main(String[] args) {/* 數組索引越界異常:ArrayIndexOutOfBoundsException */int[] array = {10, 20, 30};System.out.println(array[2]);// 日志棧 先執行的日志在下面// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3// at com.javase.exceptiondemo.Test1.main(Test1.java:11)// System.out.println(array[3]);/* 空指針異常:NullPointerException,直接輸出沒有問題,但是調用空指針的變量的功能就會報錯 */String name = null;System.out.println(name);// System.out.println(name.length());/* 數字操作異常:arithmeticException */// int c = 10 / 0;/* 類型轉換異常:ClassCastException */Object o = 23;// String s = (String) o;/* 數字轉換異常:NumberFormatException */String number = "23A";Integer it = Integer.valueOf(number);System.out.println(it + 1);System.out.println("程序結束");} }c.常見編譯時異常
編譯時異常:
- 不是RuntimeException或者其子類的異常,編譯階段就報錯,必須處理,否則代碼不通過。
編譯時異常示例:
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class Test {public static void main(String[] args) throws ParseException {String dateString = "2022-07-23 16:00:00";// 創建簡單日期格式化類對象:解析字符串時間成為日期對象SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = simpleDateFormat.parse(dateString);System.out.println(date);} }編譯時異常的作用是什么:
- 是擔心程序員的技術不行,在編譯階段就爆出一個錯誤,目的在于提醒不要出錯。
- 編譯時異常是可遇不可求。
d.異常的默認處理流程
Test.java
public class Test {public static void main(String[] args) {System.out.println("---程序開始---");division(10, 5);division(10, 0);System.out.println("---程序結束---");}/*** 除法* @param a 被除數* @param b 除數*/public static void division(int a, int b) {System.out.println(a);System.out.println(b);int c = a / b;System.out.println(c);} }總結:
- 默認的異常處理機制并不好,一旦出現異常,程序立即死亡。
e.編譯時異常的處理機制
編譯時異常的處理形式有三種:
- 出現異常直接拋出去給調用者,調用者也繼續拋出去。
- 出現異常自己捕獲處理,不麻煩別人。
- 前兩者結合,出現異常直接拋出去給調用者,調用者捕獲處理。
異常處理方式一:throws
- throws:用在方法上,可以將方法內部出現的異常拋出去給本方法的調用者處理。
- 這種方式并不好,發生異常的方法自己不處理異常,如果異常最終拋出去給虛擬機將引起程序死亡。
拋出異常格式:
方法 throws 異常1, 異常2, 異常3 ... {}規范做法:
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class Test {public static void main(String[] args) throws ParseException {System.out.println("---程序開始---");parseTime("2022-07-23 18:00:00");System.out.println("---程序結束---");}private static void parseTime(String s) throws ParseException {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = simpleDateFormat.parse(s);System.out.println(date);} }異常處理方式2:try…catch…
- 監視捕獲異常,用在方法內部,可以將方法內部出現的異常直接捕獲處理。
- 方式異常的方法自己獨立完成異常的處理,程序可以繼續往下執行。
格式:
try {// 監視可能出現 } catch(異常類型1 變量) {// 處理異常 } catch(異常類型2 變量) {// 處理異常 } ...建議格式:
try {// 監視可能出現 } catch(Exception e) {// 打印異常棧信息e.printStackTrace(); }// Exception可以捕獲處理一切異常類型!Test.java
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class Test {public static void main(String[] args){System.out.println("---程序開始---");parseTime("2022-07-23 18:00:00");System.out.println("---程序結束---");}private static void parseTime(String s) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = null;// 整體捕獲異常try {date = simpleDateFormat.parse(s);InputStream inputStream = new FileInputStream("a.jpg");System.out.println(inputStream);} catch (ParseException | FileNotFoundException e) {e.printStackTrace();}System.out.println(date);// 分開捕獲異常 // try { // date = simpleDateFormat.parse(s); // } catch (ParseException e) { // // 解析出現問題 // System.out.println("解析時間出現異常!"); // e.printStackTrace(); // // throw new RuntimeException(e); // } // System.out.println(date); // // try { // InputStream inputStream = new FileInputStream("a.jpg"); // System.out.println(inputStream); // // } catch (FileNotFoundException e) { // System.out.println("文件不存在!"); // e.printStackTrace(); // }}}異常處理方式3:前兩者結合
- 方法直接將異常通過throws拋出去給調用者。
- 調用者收到異常后直接捕獲處理。
Test.java
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class Test {public static void main(String[] args) {System.out.println("---程序開始---");try {parseTime("2022-07-24 18:00:00");System.out.println("操作成功!");} catch (Exception e) {e.printStackTrace();System.out.println("操作失敗!");}System.out.println("---程序結束---");}private static void parseTime(String s) throws ParseException {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = simpleDateFormat.parse(s);System.out.println(date);} }總結:
- 在開發中按照規范來說第三種方式是最好的:底層的異常拋出去給最外層,最外層集中捕獲處理。
- 實際應用中,只要代碼能夠編譯通過,并且功能能完成,那么每一種異常處理方式都是可以的。
f.運行時異常的處理機制
運行時異常的處理形式:
- 運行時異常編譯階段不會出錯,是運行時才可能出錯的,所以編譯階段不處理也可以。
- 按照規范建議還是處理:建議在最外層調用處集中捕獲處理即可。
Test.java
public class Test {public static void main(String[] args) {System.out.println("---程序開始---");try {division(10, 0);} catch (Exception e) {throw new RuntimeException(e);}System.out.println("---程序結束---");}public static void division(int a, int b) {System.out.println(a);System.out.println(b);int c = a / b;System.out.println(c);} }g.異常處理使代碼更穩健的案例
需求:
- 鍵盤錄入一個合理的價格為止(必須是數值,值必須大于0)。
分析:
- 定義一個死循環,讓用戶不斷的輸入價格。
Test.java
import java.util.Scanner;public class Test {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (true) {try {System.out.println("請您輸入合法的價格:");String priceStr = scanner.nextLine();// 轉成double類型的價格double price = Double.parseDouble(priceStr);if (price > 0) {System.out.println("定價:" + price);break;} else {System.out.println("價格必須是正數!");}} catch (NumberFormatException e) {System.out.println("輸入的數據不正確,請輸入合法的數值!");}}} }h.自定義異常
自定義異常的必要?
- Java無法為這個世界上全部的問題提供異常類。
- 如果企業想通過異常的方法來管理自己的某個業務問題,就需要自定義異常類。
自定義異常的好處:
- 可以使用異常的機制管理業務問題,如提醒程序員注意。
- 同時一旦出現bug,可以用異常的形式清晰的指出出錯的地方。
自定義異常的分類:
-
自定義編譯時的異常:提醒強烈,一定要處理。
- 定義一個異常類繼承Exception。
- 重寫構造器。
- 在出現異常的地方用throw new 自定義對象拋出。
-
自定義運行時異常:提醒不強烈,編譯階段不報錯,運行時才可能出現。
- 定義一個異常類繼承RuntimeException。
- 重寫構造器。
- 在出現異常的地方用throw new自定義對象拋出!
AgeIllegalException.java
/*** 年齡非法異常*/ public class AgeIllegalException extends Exception{public AgeIllegalException() {}public AgeIllegalException(String message) {super(message);} }Test.java
/*** 需求:認為年齡小于0歲,大于200歲就是一個異常**/ public class Test {public static void main(String[] args) {try {checkAge(201);} catch (AgeIllegalException e) {e.printStackTrace();}}public static void checkAge(int age) throws AgeIllegalException {if (age < 0 || age > 200) {// 拋出一個異常給調用者// throw:在方法內部直接創建一個異常對象 并從此點拋出// throws:用在方法申明上的 拋出方法內部的異常throw new AgeIllegalException(age + " is illegal!");} else {System.out.println("年齡合法:推薦商品給其購買");}} }AgeIllegalRuntimeException.java
package com.javase.exceptiondemo2;public class AgeIllegalRuntimeException extends RuntimeException{public AgeIllegalRuntimeException() {}public AgeIllegalRuntimeException(String message) {super(message);} }Test.java
/*** 需求:認為年齡小于0歲,大于200歲就是一個異常**/ public class Test {public static void main(String[] args) {try {checkAgeRuntime(0);} catch (Exception e) {e.printStackTrace();}}public static void checkAgeRuntime(int age) {if (age < 0 || age > 200) {// 拋出一個異常給調用者// throw:在方法內部直接創建一個異常對象 并從此點拋出// throws:用在方法申明上的 拋出方法內部的異常throw new AgeIllegalRuntimeException(age + " is illegal!");} else {System.out.println("年齡合法:推薦商品給其購買");}} }總結
以上是生活随笔為你收集整理的Java进阶(八)Stream、异常体系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 低代码常见场景【上】|如何解决业务问题
- 下一篇: 【078】Town Scaper-创造属