day08 - 異常、マルチスレッド
メインコンテンツ :
- 例外の概要
- 異常な分類
- 例外処理
- カスタム例外
- マルチスレッド化
1 例外の概要
1.1 例外とは何ですか?
- 例外とは、プログラムの異常な状態であり、プログラムの実行中にデータによってプログラムが異常になり、最終的にはJVMが異常停止することがあります。
- 注: ステートメント エラーは例外階層にはカウントされません。
1.2 異常な存在形態
- 例外には、これまでに経験したよく知られた配列範囲外例外 (ArrayIndexOutOfBoundsException)、null ポインタ例外 (NullPointerException)、型変換例外 (ClassCastException) など、さまざまな種類があります。プログラム内で例外が発生すると、例外のオブジェクトが例外の発生場所に実際に作成され、そのオブジェクトは関連する例外情報を保持します。
- 簡単に言えば、例外は Java で提供されるクラスのオブジェクトです。
1.3 プログラム内で例外が発生した場合の対処方法
- プログラム内で例外が発生すると、まず下方向の実行が中断されます。例外の送信は処理メソッドによって異なります (次の章で説明します)。例外が処理されない場合、デフォルトではこのメソッドの呼び出し元に例外が渡されます。JVM が例外を受け取るまで返し続けます。例外を受け取ると、プログラムは実行を終了します。
2 例外の分類
- コンパイル時例外
- Non-RuntimeException とそのサブクラス: コンパイル時例外はコンパイル中に発生する可能性のある例外であり、コンパイル中に処理する必要があります。処理しないとプログラムを実行できません。
- 実行時例外
- RuntimeException とそのサブクラス: ランタイム例外は、実行時に発生する可能性のある例外であり、コンパイル時に処理する必要はありません。
3 例外処理方法
3.1 JVM が例外を処理する方法
-
プログラムに問題がある場合、何も処理をしていないため、最終的には JVM がデフォルトの処理を行うことになりますが、JVM はどのように処理するのでしょうか。
- 例外の種類、理由、および場所をコンソールに出力します。
- プログラムの実行が停止する
-
注: プログラムで例外が発生した場合、例外の情報を含むこの例外のオブジェクトが現在の場所に作成され、例外は処理のためにこのメソッドの呼び出し元に渡されます。
-
短所: ユーザーエクスペリエンスが悪い
3.2 手動による例外処理
3.2.1 例外の宣言
-
例外の宣言 - スロー
- 修飾子の戻り値の型メソッド名 (パラメータリスト) throws 例外の型 1、例外の型 2... { ... }
- 例 : public void show() throws NullPointerException , ArrayIndexOutOfBoundsException { … }
-
関数 :
- 現在のメソッドを呼び出すときにいくつかの例外が発生する可能性があり、使用する場合は注意する必要があることを示します。
- 現在のメソッドに例外がない場合、コードは通常どおり実行されます。
- 現在のメソッドで例外が発生した場合、例外は処理 (ポットのスロー) のためにこのメソッドの呼び出し元に渡されます。
-
package com.bn.exception_demo; import sun.java2d.pipe.SpanShapeRenderer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /* 声明异常—— throws 格式 : 修饰符 返回值类型 方法名(参数列表) throws 异常类型1 , 异常的类型2... { ... } 举例 : public void show() throws NullPointerException , ArrayIndexOutOfBoundsException { .... } 作用 : 1 表示告知调用者当前的方法可能会出现某些异常,使用时需要注意哦! 2 如果当前方法没有出现任何异常, 那么代码会正常执行 3 如果当前方法中出现了异常 , 会把异常交给本方法调用者处理(甩锅) 需求 : 练习 : 定义两个方法一个运行时期异常 , 一个声明编译时期异常 ! 注意 : 1 编译时异常因为在编译时就会检查,所以必须要写在方法后面进行显示声明 2 运行时异常因为在运行时才会发生,所以在方法后面可以不写 3 如果声明多个异常有子父类关系 , 那么只要声明一个父类即可(多态) */ public class Exception_Throws { public static void main(String[] args) throws ParseException{ printArr();// 如果此方法出现了异常 , 会交给jvm进行处理 StringToDate();// 如果此方法出现了异常 , 会交给jvm进行处理 } // 1 告诉调用者 , 此方法可能会出现异常哦 // 2 如果此方法没有出现异常 , 那么会正常执行 // 3 如果此方法中出现了异常 , 会把此异常交给调用者处理 // 注意 : 如果声明的异常是一个运行时期异常 , 那么此声明可以省略 public static void printArr() /*throws NullPointerException*/ { int[] arr = null; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } // 1 告诉调用者 , 此方法可能会出现异常哦 // 2 如果此方法没有出现异常 , 那么会正常执行 // 3 如果此方法中出现了异常 , 会把此异常交给调用者处理 // 注意 : 如果声明的异常 是一个编译时期异常 , 那么在编译时期必须处理 , 要么程序无法执行 public static void StringToDate() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = sdf.parse("2000-03-11 12:12:12"); } }
3.2.2 例外のスロー
-
考える:
- 以前に例外が発生しましたが、仮想マシンは例外オブジェクトを作成して呼び出し元にスローするのに役立ちました。しかし、手動で例外オブジェクトを自分で作成する必要がある場合はどうすればよいでしょうか?
-
フォーマット :
-
修饰符 返回值类型 方法名(参数列表) { throw new 异常对象(); }
-
-
知らせ :
- スローされた例外のフォーマットはメソッド内で行う必要があります。
- 手動で例外がスローされた場合、次のコードは実行できません
-
package com.bn.exception_demo; /* 抛出异常演示 : 格式 : 修饰符 返回值类型 方法名(参数列表) { throw new 异常对象(); } 注意 : 1 抛出异常的格式必须在方法的内部完成 2 如果手动抛出一个异常,下面的代码无法执行 */ public class Exception_Throw { public static void main(String[] args) { System.out.println("家里有一个貌美如花的老婆"); System.out.println("还有一个当官的兄弟"); System.out.println("自己还有一个买卖"); System.out.println("这样的生活你要不要?"); // 程序不想往下执行了 ,怎么做 ??? // 1 自己手动制造出一个异常 // 2 当前异常也是交给了方法的调用者处理 , 也就是jvm处理 // 3 下面代码无法执行 throw new RuntimeException(); // System.out.println("武大郎的标准生活!"); } }
-
関数 :
- メソッド内では、渡されたパラメータが間違っていて実行を続ける意味がない場合に、メソッドの実行を終了することを示すためにスローが採用されています。
- 呼び出し元にメソッド内の問題の原因を伝える
package com.itheima.exception_demo; /* 抛出异常存在的意义所在 : 1 在方法中,当传递的参数有误,没有继续运行下去的意义了,则采取抛出处理,表示让该方法结束运行。 2 告诉调用者方法中出现了问题 练习 : 定义一个方法 , 方法的参数接收一个数组 , 在方法中遍历数组 . 需求1 : 如果方法接收的数组为null , 使用输出语句提示 需求2 : 如果方法接收的数组为null , 使用抛出异常解决 思考 : 两种方式的区别在哪里 ? */ public class Exception_Throw2 { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5}; arr = null; // printArr1(arr); printArr2(arr);// 接收方法返回的异常 , 但是此异常有jvm进行处理 } // 需求1 : 如果方法接收的数组为null , 使用输出语句提示 public static void printArr1(int[] arr) { if (arr == null) { System.out.println("数组为null"); } else { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } } // 需求2 : 如果方法接收的数组为null , 使用抛出异常解决 public static void printArr2(int[] arr) { if (arr == null) { throw new RuntimeException(); } else { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } } }
3.2.3 スローとスローの違い
- スローします:
- メソッド宣言の後に例外クラス名が続いて使用されます。
- ステートメントが異常であることを示し、このメソッドを呼び出すときにそのような例外が発生する可能性があることを呼び出し元に通知します。
- 投げる :
- メソッド本体で使用され、その後に例外オブジェクト名が続きます
- データが誤って渡されたことを呼び出し元に通知するために、例外オブジェクトが手動でスローされることを示します。
3.2.4 例外のキャッチ
-
例外のキャッチと処理の概要: try、catch
- 前のステートメントまたはスローは、例外を渡して呼び出し元に例外情報を知らせることです。
このメソッドによってキャプチャ プロセスが内部的に処理されるため、例外の送信を防ぐことができ、プログラムの実行を継続できます。
- 前のステートメントまたはスローは、例外を渡して呼び出し元に例外情報を知らせることです。
-
例外をキャッチするための形式
-
try { try中存放可能会出现问题的代码 1.代码... 2.代码... 3.代码... } catch (异常类型 变量名) { 4.处理异常方案 打印异常,获取异常原因记录日志......) } 5.其他代码...
-
-
実装方法
- try で問題が発生しなかった場合、どのように実行すればよいでしょうか?
- catch ではなく、上から下に順番に実行します。
- try のコード 2 で問題が発生した場合、問題の下にあるコードは引き続き実行されますか?
- 実行されませんが、現在の例外オブジェクトと例外タイプが一致し、一致する例外コードは正常に実行されます。
- 問題が見つからない場合、プログラムはどのように動作するのでしょうか?
- 例外がキャッチされなかった場合、仮想マシンが例外の処理に役立ちます。
- try で問題が発生しなかった場合、どのように実行すればよいでしょうか?
-
複数の例外のキャプチャと処理のスキーム
-
複数の例外が個別に処理される
-
try{ 异常1 }catch(异常1){ } try{ 异常2 }catch(异常2){ }
-
-
複数の例外を一度捕捉し、複数回処理する
-
try{ 异常1 异常2 }catch(异常1){ }catch(异常2){ }
-
-
複数の例外。例外は 1 回キャッチされ、1 回処理されます。
-
try{ 异常1 异常2 }catch(Exception e){ }
-
-
3.3 Throwableのメンバーメソッド
メソッド名 | 説明する |
---|---|
パブリック String getMessage() | このスロー可能オブジェクトの詳細メッセージ文字列を返します。 |
パブリック String toString() | このスロー可能オブジェクトの簡単な説明を返します |
public void printStackTrace() | 異常なエラーメッセージをコンソールに出力します。 |
3.4 例外演習
package com.bn.exception_demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
定义一个方法接收一个生日日期字符串(xxxx年xx月xx)
main方法中让用户输入一个生日日期字符串,调用设计好的方法计算在地球上活了多少天。
要求:如果解析发生异常,捕获异常,提示用户要重新输入生日日期字符串,直到输入正确的日期为止。
思考:设计代此码的过程中想想什么时候捕获异常,什么时候声明异常?
*/
public class ExceptionTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入生日(xxxx-xx-xx):");
while (true) {
String birthday = sc.nextLine();
try {
method(birthday);
break;// 如果生日没有问题结束死循环
} catch (ParseException e) {
System.out.println("录入生日格式有误!");
}
}
}
public static void method(String strDate) throws ParseException {
// 创建日期模板对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 解析字符串
Date date = sdf.parse(strDate);
// 获取生日到1970/1/1 经历的毫秒值
long time1 = date.getTime();
// 当前系统时间到1970/1/1 经历的毫秒值
Date d2 = new Date();
long time2 = d2.getTime();
System.out.println("活了" + (time2 - time1) / (1000L * 60 * 60 * 24) + "天");
}
}
4 カスタム例外
4.1 概要
- JDK の例外タイプが実際のビジネス ニーズを満たしていない場合。例外は自分で定義できます。たとえば、学生の年齢データが負の場合、またはデータが 150 を超えている場合、それは違法とみなされ、例外をスローする必要があります。JDK には年齢を示す例外がないため、自分で例外を定義する必要があります
4.2 実装手順
- 例外クラスを定義する
- 継承関係を書く
- 空のパラメータの構築
- パラメータを使用して構築する
4.3 カスタム例外の注意
- コンパイル時の例外をカスタマイズする場合は、Exception を継承します。
- 実行時例外をカスタマイズする場合は、RuntimeException を継承します。
5 マルチスレッドの入門
5.1 マルチスレッドに関する概念
- 同時実行性と並列処理
- 並列処理: 同時に、複数のタスクが複数の CPU 上で同時に実行されます。
- 同時実行性: 単一の CPU 上で同時に複数のタスクが交互に実行されます。
- プロセスとスレッド
- プロセス: オペレーティング システム上で実行されるアプリケーション プログラムです。
- スレッド: それはアプリケーションで行われることです。例: 360 ソフトウェアのウイルス対策、トロイの木馬のスキャン、ゴミのクリーンアップ。
5.2 マルチスレッドとは何ですか
- ソフトウェアまたはハードウェアから複数のスレッドの同時実行を実現する技術のことを指します。
マルチスレッド機能を備えたコンピューターは、ハードウェアのサポートにより複数のスレッドを同時に実行できるため、パフォーマンスが向上します。 - 利点: タスクの実行パフォーマンスが向上します。
5.3 マルチスレッドの作成方法
5.3.1 Threadメソッドの継承
-
基本的な手順:
- Thread クラスを拡張するクラスを作成します。
- クラス内の run メソッドをオーバーライドします (スレッドによって実行されるタスクはここに配置されます)。
- スレッド オブジェクトを作成し、スレッドの start メソッドを呼び出してスレッドを開始します。
- プログラムを実行し、コンソールにデータが表示される現象を観察します。
package com.bn.thread_demo; /* 线程的创建方式1:继承Thread方式 基本步骤 : 1 创建一个类继承Thread类。 2 在类中重写run方法(线程执行的任务放在这里) 3 创建线程对象,调用线程的start方法开启线程。 需求 : 我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程) 接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。 此时主线程和启动的线程在并发执行,观察控制台打印的结果。 */ public class MyThread01 { public static void main(String[] args) { // 创建线程对象,调用线程的start方法开启线程。 MyThread mt = new MyThread(); mt.start(); // main方法中的任务 for (int i = 1; i <= 100; i++) { System.out.println("i:" + i); } } } // 创建一个类继承Thread类。 class MyThread extends Thread { // 在类中重写run方法(线程执行的任务放在这里) @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("i:" + i); } } }
5.3.2 Runable メソッドの実装
-
施工方法
- public Thread(実行可能ターゲット)
- public Thread(Runnalbe target , String name)
-
実装手順
- Runnableを実装するタスククラスを定義し、runメソッドをオーバーライドします。
- タスクオブジェクトの作成
- スレッド オブジェクトを作成し、Runnable パラメーターを指定したコンストラクターを使用してタスクを指定します。
- スレッドの start メソッドを呼び出してスレッドを開始します
package com.itheima.thread_demo; /* 线程的创建方式2:实现Runnable方式 基本步骤 : 1 定义任务类实现Runnable,并重写run方法 2 创建任务对象 3 使用含有Runnable参数的构造方法,创建线程对象并指定任务。 4 调用线程的start方法,开启线程 需求 : 我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程) 接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。 此时主线程和启动的线程在并发执行,观察控制台打印的结果。 */ public class MyThread02 { public static void main(String[] args) { // 创建线程对象,调用线程的start方法开启线程。 MyRunnable mr = new MyRunnable(); Thread thread= new Thread(mr); thread.start(); // main方法中的任务 for (int i = 1; i <= 100; i++) { System.out.println("i:" + i); } } } // 1 定义任务类实现Runnable,并重写run方法 class MyRunnable implements Runnable { // 在类中重写run方法(线程执行的任务放在这里) @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("i:" + i); } } }
5.3.3 Thread クラスでよく使用されるメソッド
-
String getName(): このスレッドの名前を返します。
-
Threadクラスにスレッド名を設定します。
- void setName(String name): このスレッドの名前をパラメータ名と同じになるように変更します。
- スレッド名はコンストラクターを通じて設定することもできます
-
public static Thread currentThread(): 現在実行中のスレッド オブジェクトへの参照を返します。
-
public static void sleep(long time): ミリ秒単位で指定された時間、スレッドをスリープさせます。
-
スレッドには 2 つのスケジューリング モデルがあります
- タイムシェアリング スケジューリング モデル: すべてのスレッドが順番に CPU を使用し、各スレッドが占有する CPU のタイム スライスを均等に分配します プリエンプティブ スケジューリング モデル: 優先順位の高いスレッドを優先して CPU を使用します
。優先度の高いスレッドはランダムに選択されます。 - 比較的多くの CPU タイム スライスを取得します
package com.bn.thread_demo.thread_method; /* 线程有两种调度模型 1 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 2 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程 获取的 CPU 时间片相对多一些 注意 : Java使用的是抢占式调度模型 优先级高 , 只是抢夺到cpu执行的概率高而已 , 只是一种概率问题 */ public class PriorityDemo { public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }); // 优先级最低 thread1.setPriority(1); thread1.start(); Thread thread2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }); // 优先级最高 thread2.setPriority(10); thread2.start(); } }
- タイムシェアリング スケジューリング モデル: すべてのスレッドが順番に CPU を使用し、各スレッドが占有する CPU のタイム スライスを均等に分配します プリエンプティブ スケジューリング モデル: 優先順位の高いスレッドを優先して CPU を使用します