一、異常
1.什麼是異常
異常就是不正常的情況。
2.異常的體系結構
Throwable
Error:大毛病。一般都是常見於:服務器宕機、數據庫服務器~~ 簡單來說就是程序員無力解決
Exception:小毛病。一般都是程序員所犯的小錯誤。例如:索引越界、空指針~~ 簡單來說就是程序員可以解決的問題
3.異常的分類
☆編譯時異常:
A:全部都是Exception的子類 ★★★
B:當我們寫好代碼後編譯器直接報錯了,這個時候必須要經過處理纔可以去運行程序
☆運行時異常:
A:全部都是RuntimeException的子類 ★★★
B:當程序運行的時候纔會出現的問題
錯誤:
A:第一種情況,需要修改源代碼。重新思考解決問題的思路。
B:第二種情況,程序員無力解決的問題。
4.Objects工具類的方法requireNonNull
|--對數據進行合法性的判斷
public class Demo04Objects {
public static void main(String[] args) {
method(null);
}
public static void method(Object obj){
//對傳遞過來的參數進行合法性判斷,判斷是否爲null
/*if(obj == null){
throw new NullPointerException("傳遞的對象的值是null");
}*/
//Objects.requireNonNull(obj);
Objects.requireNonNull(obj,"傳遞的對象的值是null");
}
}
5.throw關鍵字
作用:
可以使用throw關鍵字在指定的方法中拋出指定的異常
|--注意是在指定的方法中拋出指定的異常 ★★★
使用格式:
throw new xxxException("異常產生的原因");
注意:
1.throw關鍵字必須寫在方法的內部
2.throw關鍵字後邊new的對象必須是Exception或者Exception的子類對象
3.throw關鍵字拋出指定的異常對象,我們就必須處理這個異常對象
|--throw關鍵字後邊創建的是RuntimeException或者是 RuntimeException的子類對象,
我們可以不處理,默認交給JVM處理(打印異常對象,中斷程序)
|--throw關鍵字後邊創建的是編譯異常(寫代碼的時候報錯),我們就必須處理這個異常,
要麼throws,要麼try...catch
★★★清楚throw和throws的關係
示例代碼:
public class Demo03Throw {
public static void main(String[] args) {
//int[] arr = null;
int[] arr = new int[3];
int e = getElement(arr,3);
System.out.println(e); //ArrayIndexOutOfBoundsException
}
/*
定義一個方法,獲取數組指定索引處的元素
參數:
int[] arr
int index
以後(工作中)我們首先必須對方法傳遞過來的參數進行合法性校驗
如果參數不合法,那麼我們就必須使用拋出異常的方式,告知方法的調用者,傳遞的參數有問題
注意:
NullPointerException是一個運行期異常,我們不用處理,默認交給JVM處理
ArrayIndexOutOfBoundsException是一個運行期異常,我們不用處理,默認交給JVM處理
*/
public static int getElement(int[] arr,int index){
/*
我們可以對傳遞過來的參數數組,進行合法性校驗
如果數組arr的值是null
那麼我們就拋出空指針異常,告知方法的調用者"傳遞的數組的值是null"
*/
if(arr == null){
throw new NullPointerException("傳遞的數組的值是null");
}
/*
我們可以對傳遞過來的參數index進行合法性校驗
如果index的範圍不在數組的索引範圍內
那麼我們就拋出數組索引越界異常,告知方法的調用者"傳遞的索引超出了數組的使用範圍"
*/
if(index<0 || index>arr.length-1){
throw new ArrayIndexOutOfBoundsException("傳遞的索引超出了數組的使用範圍");
}
int ele = arr[index];
return ele; //合法性校驗沒有問題則返回原數組
}
}
6.第一種處理異常的方式
|--拋出--|
throws關鍵字:異常處理的第一種方式,交給別人處理
作用:
當方法內部拋出異常對象的時候,那麼我們就必須處理這個異常對象
可以使用throws關鍵字處理異常對象,會把異常對象聲明拋出給方法的調用者處理(自己不處理,給別人處理),
最終交給JVM處理-->中斷處理
使用格式:在方法聲明時使用
修飾符 返回值類型 方法名(參數列表) throws AAAExcepiton,BBBExcepiton...{
throw new AAAExcepiton("產生原因");
throw new BBBExcepiton("產生原因");
...
}
注意:
1.throws關鍵字必須寫在方法聲明處
2.throws關鍵字後邊聲明的異常必須是Exception或者是Exception的子類
3.方法內部如果拋出了多個異常對象,那麼throws後邊必須也聲明多個異常
如果拋出的多個異常對象有子父類關係,那麼直接聲明父類異常即可
4.調用了一個聲明拋出異常的方法,我們就必須的處理聲明的異常
要麼繼續使用throws聲明拋出,交給方法的調用者處理,最終交給JVM
要麼try...catch自己處理異常
示例代碼:
public class Demo05Throws {
/*
FileNotFoundException extends IOException extends Excepiton
如果拋出的多個異常對象有子父類關係,那麼直接聲明父類異常即可
*/
//public static void main(String[] args) throws FileNotFoundException,IOException {
//public static void main(String[] args) throws IOException {
public static void main(String[] args) throws Exception{
readFile("c:\\a.tx");
System.out.println("後續代碼");
}
/*
定義一個方法,對傳遞的文件路徑進行合法性判斷
如果路徑不是"c:\\a.txt",那麼我們就拋出文件找不到異常對象,告知方法的調用者
注意:
FileNotFoundException是編譯異常,拋出了編譯異常,就必須處理這個異常
可以使用throws繼續聲明拋出FileNotFoundException這個異常對象,讓方法的調用者處理
*/
public static void readFile(String fileName) throws Exception{
if(!fileName.equals("c:\\a.txt")){
throw new FileNotFoundException("傳遞的文件路徑不是c:\\a.txt");
}
/*
如果傳遞的路徑,不是.txt結尾
那麼我們就拋出IO異常對象,告知方法的調用者,文件的後綴名不對
*/
if(!fileName.endsWith(".txt")){
throw new IOException("文件的後綴名不對");
}
System.out.println("路徑沒有問題,讀取文件");
}
}
7.第二種處理異常方式
|--try...catch--|
public class Demo01 {
public static void main(String[] args) {
method2();
}
public static void method2() {
method();
}
public static void method() {
try{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("201807-25"); //這裏出現異常會執行下面的catch
System.out.println(d.toLocaleString()); //所以此句話不會輸出
System.out.println(2/0); //不執行
}catch(ParseException p) {
System.out.println("逗逼,趕快檢查一下parse方法中的字符串格式");
}catch(ArithmeticException a) {
System.out.println("除數不能爲0");
}
System.out.println("1111111111111111"); //但是後面的代碼依然執行
}
}
8.Throwable類的常用方法
String getMessage() 返回此 throwable 的簡短描述。
String toString() 返回此 throwable 的詳細消息字符串。
void printStackTrace() JVM打印異常對象,默認此方法,打印的異常信息是最全面的
9.finally的使用
|--不管程序有沒有發生異常,都會執行finally代碼塊,常用語IO、數據庫的資源釋放
示例代碼:
public class Demo02 {
public static void main(String[] args) {
try{
System.out.println("創建了數據庫的連接對象"); // ok
System.out.println(2/0); // 有問題
System.out.println("開始連接數據庫");
System.out.println("連接上了數據庫");
}catch(ArithmeticException a) {
System.out.println("除數不能爲0");
}finally {
System.out.println("釋放資源,關閉連接");
}
}
}
10.多異常的處理和注意事項
public class Demo01Exception {
public static void main(String[] args) {
/*
多個異常使用捕獲又該如何處理呢?
1. 多個異常分別處理。
2. 多個異常一次捕獲,多次處理。
3. 多個異常一次捕獲一次處理。
*/
//1. 多個異常分別處理。
/*
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}
try{
List list = List.of(1, 2, 3);
System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
*/
//2. 多個異常一次捕獲,多次處理。
/*
try {
int[] arr = {1,2,3};
//System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
List list = List.of(1, 2, 3);
System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
*/
/*
一個try多個catch注意事項:
catch裏邊定義的異常變量,如果有子父類關係,那麼子類的異常變量必須寫在上邊,否則就會報錯★★★★★
ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
*/
★★★★★
/*
try {
int[] arr = {1,2,3};
//System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
List list = List.of(1, 2, 3);
System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (IndexOutOfBoundsException e){ //這個catch裏面的異常時是下面catch裏面異常的子類
System.out.println(e);
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}
*/
//3. 多個異常一次捕獲一次處理。(太過於籠統,後面catch不確定給出拋出異常的信息)
/*
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
List list = List.of(1, 2, 3);
System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (Exception e){
System.out.println(e);
}
*/
//運行時異常被拋出可以不處理。即不捕獲也不聲明拋出。
//默認給虛擬機處理,終止程序,什麼時候不拋出運行時異常了,在來繼續執行程序
int[] arr = {1,2,3};
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
List list = List.of(1, 2, 3);
System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
System.out.println(“後續代碼!”);
}
}
11.finally代碼塊中是否可以有return語句
/*
如果finally有return語句,永遠返回finally中的結果,避免該情況.
*/
public class Demo02Exception {
public static void main(String[] args) {
int a = getA();
System.out.println(a);
}
//定義一個方法,返回變量a的值
public static int getA(){
int a = 10;
try{
return a;
}catch (Exception e){
System.out.println(e);
}finally {
//一定會執行的代碼
a = 100;
return a;
}
}
}
12.子父類關係方法重寫時異常的聲明情況
/*
子父類的異常:
- 如果父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異常。
- 父類方法沒有拋出異常,子類重寫父類該方法時也不可拋出異常。此時子類產生該異常,只能捕獲處理,不能聲明拋出
注意:
父類異常時什麼樣,子類異常就什麼樣
*/
public class Fu {
public void show01() throws NullPointerException,ClassCastException{}
public void show02() throws IndexOutOfBoundsException{}
public void show03() throws IndexOutOfBoundsException{}
public void show04() {}
}
class Zi extends Fu{
//子類重寫父類方法時,拋出和父類相同的異常
public void show01() throws NullPointerException,ClassCastException{}
//子類重寫父類方法時,拋出父類異常的子類
public void show02() throws ArrayIndexOutOfBoundsException{}
//子類重寫父類方法時,不拋出異常
public void show03() {}
/*
父類方法沒有拋出異常,子類重寫父類該方法時也不可拋出異常。
*/
//public void show04() throws Exception{}
//此時子類產生該異常,只能捕獲處理,不能聲明拋出
public void show04() /*throws ParseException*/ {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = sdf.parse("5555");
} catch (ParseException e) {
e.printStackTrace();
}
}
}
13.自定義異常
public class RegisterException extends /*Exception*/ RuntimeException{
//添加一個空參數的構造方法
public RegisterException(){
super();
}
/*
添加一個帶異常信息的構造方法
查看源碼發現,所有的異常類都會有一個帶異常信息的構造方法,
方法內部會調用父類帶異常信息的構造方法,讓父類來處理這個異常信息
*/
public RegisterException(String message){
super(message);
}
}
//測試類
public class Demo02RegisterException {
// 1.使用數組保存已經註冊過的用戶名(數據庫)
static String[] usernames = {"張三","李四","王五"};
public static void main(String[] args) /*throws RegisterException*/{
//2.使用Scanner獲取用戶輸入的註冊的用戶名(前端,頁面)
Scanner sc = new Scanner(System.in);
System.out.println("請輸入您要註冊的用戶名:");
String username = sc.next();
checkUsername(username);
}
//3.定義一個方法,對用戶輸入的中註冊的用戶名進行判斷
public static void checkUsername(String username) /*throws RegisterException*/ {
//遍歷存儲已經註冊過用戶名的數組,獲取每一個用戶名
for (String name : usernames) {
//使用獲取到的用戶名和用戶輸入的用戶名比較
if(name.equals(username)){
//true:用戶名已經存在,拋出RegisterException異常,告知用戶"親,該用戶名已經被註冊";
try {
//拋出運行期異常,無需處理,交給JVM處理,中斷處理
throw new RegisterException("親,該用戶名已經被註冊");
} catch (RegisterException e) {
e.printStackTrace();
//return;
}
}
}
//如果循環結束了,還沒有找到重複的用戶名,提示用戶"恭喜您,註冊成功!";
System.out.println("恭喜您,註冊成功!");
}
}
二、多線程
1.什麼是併發和並行
併發就是在同一段時間內交替執行多個任務,交替運行
並行就是在同一段時間內同時執行多個任務,同時運行
2.什麼是進程
指的就是一個軟件在運行期間,在內存中的一個執行區域
3.什麼是線程
指的就是在一個進程中包含多個執行任務,每一個任務就是一個線程
4.如何開啓一個多線程
一、創建一個類繼承Thread
示例代碼1:
現在商家有有100袋堅果,分別分爲實體店售出和官網售出
創建兩個線程一個表示官網、一個表示實體店
賣完爲止
//線程類
public class MyThread extends Thread {
public static int num = 100; //100份堅果
public static int count = 1;
private String name; //商家名稱
MyThread(String name) {
this.name = name;
}
@Override
public void run() {
while (num > 0) {
System.out.println(name + "正在賣出第" + (count++) + "份" + ",還剩下" + --num + " 份");
}
}
}
//測試類
public class Main {
public static void main(String[] args) {
MyThread my1 = new MyThread("官網");
MyThread my2 = new MyThread("實體店");
my1.start();
my2.start();
}
}
二、請使用繼承Thread類的方式開啓兩個線程
(1)第一個線程的名字設置爲:a 第二個線程的名字設置爲:b
(2)第一個線程裏面實現計算1+2+3+4+....+100的和
(3)第二個線程裏面實現計算1+2+3+4+....+200的和
程序最終打印結果:
a:5050
b:20100 (a和b的打印順序不作要求)
//線程類
public class MyThread extends Thread {
@Override
public void run() {
int a = 0;
int b = 0;
if(getName().equals("a"))
{
for(int i = 1 ; i <= 100 ;i++)
{
a = a +i;
}
System.out.println(getName() + ":" + a);
}
if(getName().equals("b"))
{
for(int i = 1 ;i <= 200 ;i++)
{
b = b +i;
}
System.out.println(getName() + ":" + b);
}
}
/* 第二實現方式*/
/*
@Override
public void run() {
while (true) {
if (getName().equals("a")) {
if (a1 < 100) {
sumA = sumA + (++a1);
}
else {
System.out.println(getName() + ":" + sumA); break;
}
}
if (getName().equals("b")) {
if (b1 < 200) {
sumB = sumB + (++b1);
} else {
System.out.println(getName() + ":" + sumB);break;
}
}
}
}
*/
}
//測試類
public class StartThread {
public static void main(String[] args) {
MyThread my1 = new MyThread();
my1.setName("a");
my1.run();
MyThread my2 = new MyThread();
my2.setName("b");
my2.run();
}
}