文章目錄
- 一、String
- 1.字符串的不可變性
- 2.字符串的修改
- 3.StringBuilder和StringBuffer
- 4.【字符串練習】
- 4.1 字符串中的第一個唯一字符
- 4.2 字符串最后一個單詞的長度
- 4.3 驗證回文串
- 二、異常
- 1.初識異常
- 2.異常的分類
- 3.異常的處理
- 4.異常處理流程總結
- 5.自定義異常
在上一章節中,我們已經對String類型的 常用的方法進行了學習,String的知識不止于此。本章節,我們再來對String進行探究,并學習有關異常的知識。
一、String
1.字符串的不可變性
在之前對字符串的學習中,我們了解到 字符串是一種不可變對象,即字符串不能被修改。當我們對字符串進行操作時,都會生成一個新的字符串。
是什么原因導致字符串的不可變性的呢?我們來閱讀一下String類型的源碼:
字符串不能修改的真正原因是因為:value被private修飾,在類外拿不到value的引用,并非是因為final。
2.字符串的修改
public static void main(String[] args) {String s = "hello";//避免直接對String類型的對象進行修改,因為String的不可變性,所有的修改都會創建新對象,效率低s += " world";System.out.println(s);}
使用上面這種方法,雖然可以完成對字符串的修改,但是效率很低。
我們可以使用StringBuilder和StringBuffer對字符串進行修改。
3.StringBuilder和StringBuffer
StringBuilder和StringBuffer的大部分功能都是相同的,我們就以StringBuilder舉例:
public static void main(String[] args) {//StringBuilder和StringBuffer功能類似StringBuilder stringBuilder = new StringBuilder();StringBuilder stringBuilder1 = new StringBuilder("666");//在原數據上進行修改,不會生成新的對象//String不可變,而StringBuilder和StringBuffer可變stringBuilder1.append(888);stringBuilder1.append("222");stringBuilder1.append(888).append(99.99);System.out.println(stringBuilder1);stringBuilder1.setCharAt(1, 'M');stringBuilder1.insert(1, "==");System.out.println(stringBuilder1);stringBuilder1.reverse();System.out.println(stringBuilder1);/*** String 和 StringBuilder并不能直接轉換,要想轉換:* 1.String 變 StringBuilder:利用StringBuilder構造方法 或 append方法* 構造方法:StringBuilder stringBuilder1 = new StringBuilder("666");* 2.StringBuilder 變 String:toString方法*/String str = stringBuilder1.toString();System.out.println(str);}
當我們需要頻繁修改字符串的內容時,建議使用StringBuilder和StringBuffer。
在使用StringBuilder和StringBuffer時我們也需要注意:String和StringBuilder并不能直接轉換。
有關字符串的知識我們已經了解的差不多了,接下來我們通過幾道習題嘗試運用一下學過的知識。
4.【字符串練習】
4.1 字符串中的第一個唯一字符
字符串中的第一個唯一字符
解題思路:
在這道題目中,有兩個點值得我們注意:
1.因為字符’a’對應的ASCII碼值為97,在申請數組時,為了節省空間,我們只申請26個,后續遍歷數組時,用字符對應的ASCII碼值 - ‘a’ 即可。
2.在第二次遍歷數組時,需要對字符串進行遍歷,尋找第一個只出現一次的字符,而不應對計數數組遍歷。
class Solution {public int firstUniqChar(String s) {//定義一個計數數組,下標分別對應 26個英文字母 - 'a'int[] array = new int[26];//遍歷字符串,將出現的字符在計數數組中對應的位置+1for (int i = 0; i < s.length(); i++) {char ch = s.charAt(i);array[ch - 'a']++;}//再次遍歷字符串,尋找字符在計數數組中是否為1//注意:遍歷字符串,不是遍歷計數數組!!!for (int i = 0; i < s.length(); i++) {char ch = s.charAt(i);if (array[ch - 'a'] == 1) {return i;}}return -1;}
}
4.2 字符串最后一個單詞的長度
字符串最后一個單詞的長度
import java.util.Scanner;// 注意類名必須為 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的區別while (in.hasNext()) { // 注意 while 處理多個 caseString s = in.nextLine();String[] str = s.split(" ");String ss = str[str.length - 1];System.out.println(ss.length());}}
}
在這道題目中,我們使用了split方法對字符串進行分割,獲取最后一個單詞,計算最后一個單詞的長度即可。
4.3 驗證回文串
驗證回文串
解題思路:
class Solution {//判斷是否為合法字符public boolean isLegal(char ch) {if (ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z') {return true;}return false;}public boolean isPalindrome(String s) {//將所有大寫字符轉換為小寫字符s = s.toLowerCase();int left = 0;int right = s.length() - 1;//left < right:相遇結束循環while (left < right) {//left < right:防止越界while (left < right && !isLegal(s.charAt(left))) {left++;}while (left < right && !isLegal(s.charAt(right))) {right--;}//至此,left 和 right均為合法字符if (s.charAt(left) != s.charAt(right)) {return false;} else {left++;right--;}}//循環條件不滿足,結束循環,是回文串return true;}
}
在這道題目中,我們需要注意:最外層的while循環的"left < right"判斷的是結束條件,而內層的while循環的"left < right"是判斷是否越界(如:字符串中的字符 均為 非字母數字字符的情況)。
二、異常
1.初識異常
在Java中,將程序執行過程中發生的不正常的行為稱為異常。 在之前的學習中,我們也已經遇到過很多可能會出現異常的情況:
- 算術異常:
- 數組越界異常
- 空指針異常
2.異常的分類
異常可能在編譯時發生,也可能在程序運行時發生,根據發生的時機不同,可以將異常分為:編譯時異常和運行時異常。
- 編譯時異常:在程序編譯期間發生的異常,稱為編譯時異常,也稱為受查異常。
- 運行時異常:在程序執行期間發生的異常,稱為運行時異常,也稱為非受查異常。RuntimeException及其子類對應的異常,均稱為運行時異常。
3.異常的處理
在Java中,對異常進行處理依賴于5個關鍵字:throw,try,catch,finally,throws。
- try-catch捕獲異常
使用 try-catch 進行防御性編程:
public static void main(String[] args) {try {//System.out.println(10 / 0);int[] array1 = new int[]{1,2,3};//System.out.println(array1[6]);int[] array = null;System.out.println(array.length);} catch (ArithmeticException e) {System.out.println("捕獲算術異常");} catch (ArrayIndexOutOfBoundsException e) {System.out.println("數組越界異常");} catch (NullPointerException e) {System.out.println("空指針異常");}}
將可能會出現異常的代碼放在try的代碼塊中,使用catch匹配異常,當try中的代碼出現異常時,就會尋找catch的匹配條件,如果滿足,就會進入到對應的catch語句中。
try-catch 還可以與 finally 一起使用:
public static void main(String[] args) {try {System.out.println(10 / 0);} catch (ArithmeticException e) { //catch需捕獲到對應的異常,否則程序依舊異常終止e.printStackTrace(); //打印異常信息System.out.println("捕獲異常");return;} finally {//finally中的代碼一定會被執行,哪怕之前的代碼中存在returnSystem.out.println("finally一定被執行");//不建議在finally中使用return語句}System.out.println("666");}
在使用 try - catch - finally 對異常進行捕獲時,我們需要注意:
1.try代碼塊中,對于拋出異常位置之后的代碼均不會執行。
2.如果拋出的異常的類型與catch的異常類型不匹配,則不會捕捉到異常,程序就會報錯。
3.使用try-catch語句成功捕捉到異常之后,try-catch后的代碼可以正常執行。
4.finally中的代碼一定會被執行,哪怕之前的代碼中存在return;不建議在finally中使用return語句,如果finally中和 try或catch 中均存在return語句,則執行finally中的return語句。
5.可以使用"printStackTrace"打印異常信息。
當多個異常的處理方式相同時,可以合并寫為:
public static void main(String[] args) {try {//System.out.println(10 / 0);int[] array1 = new int[]{1,2,3};System.out.println(array1[6]);int[] array = null;System.out.println(array.length);} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {System.out.println("捕獲異常");} catch (NullPointerException e) {System.out.println("空指針異常");}}
Exception是所有異常的父類,當異常的處理具有父子關系時,一定是子類異常在前,父類異常在后,否則會報錯:
public static void main(String[] args) {try {//System.out.println(10 / 0);int[] array1 = new int[]{1,2,3};System.out.println(array1[6]);int[] array = null;System.out.println(array.length);} catch (NullPointerException e) {System.out.println("空指針異常");} catch (Exception e) {System.out.println("異常");}}
也可以通過Exception捕獲所有異常,但并不推薦,捕獲到的異常太模糊。
- throw拋出異常
在Java中,可以借助throw關鍵字,拋出一個指定的異常對象:
//通過throws拋出異常,交給調用者處理(JVM)public static void main1(String[] args) throws CloneNotSupportedException {int a = 10;if (a == 10) {//throw 常用于拋出自定義異常throw new CloneNotSupportedException("a == 10"); //編譯時異常}}
注意事項:
throw:
1.throw必須寫在方法體內部。
2.通過throw拋出的對象必須是Exception或Exception的子類。
3.如果拋出的對象是RuntimeException或RuntimeException的子類,則可以不用自己處理,交給JVM處理。
4.如果拋出的是編譯時異常,則必須用戶自己處理,否則編譯不通過。
5.異常一旦拋出,其后的代碼均不會執行。
throws:
1.throws用于處理編譯時異常,當用戶不想處理方法中的編譯時異常時,就可以通過throws將異常拋給方法的調用者處理。即當前方法不處理異常,提醒方法的調用者處理異常。
2.throws必須跟在方法的參數列表之后。
3.聲明的異常必須是Exception或Exception的子類。
4.如果方法內部拋出了多個異常,throws之后必須跟多個異常類型,多個異常類型之間用","隔開;如果拋出的多個異常類型之間具有父子關系,也可以直接聲明父類。
5.當調用聲明拋出異常的方法時,調用者必須對該異常進行處理,或者繼續使用throws拋出。
throw 和 throws:
當通過throw拋出一個運行時異常時,無需進行處理;但如果拋出的是一個編譯時異常,就需要對其進行處理,最簡單的方式就是通過throws處理。
4.異常處理流程總結
5.自定義異常
在我們進行自定義異常之前,我們先來看看異常的源碼是怎樣的(以算術異常為例):
自定義異常實現方式:
1.自定義一個類,并讓其繼承Exception或者RuntimeException,成為異常類。
2.實現一個帶有String參數類型的構造方法,參數為出現異常的原因。
我們仿照源碼編寫自定義異常:
//運行時異常(非受查異常):extends RuntimeException
public class myException extends RuntimeException{public myException() {}public myException(String message) {super(message);}
}
此時我們就編寫好了一個自定義的運行時異常。
使用也很簡單:
//運行時異常不用throwspublic static void main(String[] args) {int a = 10;if (a == 10) {throw new myException("我的異常");}}
我們再來編寫一個自定義的編譯時異常:
//編譯時異常(受查異常):extends Exception
public class my_Exception extends Exception{public my_Exception() {}public my_Exception(String message) {super(message);}
}
使用編譯時異常:
//編譯時異常必須throwspublic static void main1(String[] args) throws my_Exception {int a = 10;if (a == 10) {throw new my_Exception("我的異常");}}
由上面兩個自定義異常的例子我們可以得出:
1.自定義異常通常繼承Exception或者RuntimeException。
2.繼承Exception的異常類默認是受查異常。
3.繼承RuntimeException的異常類默認是非受查異常。
Ending。