異常,隨機數,包裝類,日期類
正如 “人無完人”一樣,程式也不是完美的,它總會存在這樣那樣的問題,而有些問題並不是能夠透過程式設計師開發更好的程式碼來解決的,如果我們忽視它,可能就會造成程式的終止,甚至是系統的崩潰。因此,我們需要想辦法來合理的解決它,這就是Java中異常的由來。
異常
- 異常的定義:是指在程式執行的過程中出現的非正常的情況,最終可能導致JVM的終止,異常本身是一個類,產生異常就是建立了異常物件並丟擲一個異常物件.
- 誤區: 異常並不是語法錯誤,語法錯誤會直接導致程式無法編譯成功,不會產生.class檔案,而異常是在程式執行時發生的.
異常的分類
在java中,異常被當成一個物件來看待,其根類是java.lang.Throwable
,Throwable類又分為Error
和Exception
- Error:不能處理的異常,這是系統內部異常,執行時報錯,屬於系統問題.一般發生Error異常,JVM會選擇終止程式,開發人員需提前避免該異常
- Exception:可以處理的異常,這是比較常見的異常,開發人員可以根據java中異常得類來處理.
Error
- 對於嚴重的Error,沒有辦法處理,只能做到提起規避.常見的Error有:
(棧溢位錯誤)和 (記憶體溢位錯誤)。 - Java中堆是用來儲存物件例項的,因此如果我們不斷地建立物件, 並且物件沒有被垃圾回收, 那麼當建立的物件過多時, 會導致記憶體不足, 進而引發
異常。
Exception
我們一般說的異常都是指的是Exception異常,這種異常一旦出現我們就要堆程式碼進行更正修復程式.
- 根據編譯時期還是執行時期可以將異常分為一下兩類
編譯時異常(checked Exception)
- 即在編譯時期就會檢查程式是否有的異常,如何沒有處理異常,則會編譯失敗,如檔案無法找到等異常,異常對於編譯時異常我們必須處理否則無法編譯透過.
執行時異常(runtime Exception)
- 即在執行時就對程式檢查的異常,在編譯時期執行異常不會被編譯器檢測(開發工具不會提示,只有執行時才會報的異常),如空指標異常,型別轉化異常,數字操作異常.
public class Test01 {
public static void main(String[] args) {
//FileInputStream inputStream = new FileInputStream("java筆記.txt");//會報編譯時異常
String str=null;
System.out.println(str.length());//執行時異常->空指標異常
Object o = new Object();
String s=(String) o;//執行時異常->型別轉換異常
int i=10/0;//執行時異常->數字操作異常
}
}
異常的傳遞
在java程式
try
語塊中丟擲一個異常,若這個異常沒有被catch
語句塊捕獲,這個異常會沿著呼叫棧向上傳遞,直到它被某個catch
語句捕獲,或者若直到到達程式頂層(main
方法或程式入口)仍沒有被捕獲,那麼程式就會終止,並列印出異常堆疊的資訊.該過程就是異常傳遞
異常的處理
異常的處理有三種,throw(丟擲),throws(宣告,向上拋),try-catch(捕獲)程式碼塊
throw
java程式出現異常時會建立一個異常物件,該物件將會被提交到java執行系統,並將控制權轉移給能夠處理該異常的程式碼,這個過程被稱為丟擲→丟擲一個異常物件
- 自動生成:程式執行時,JVM檢測到程式發生了問題,如果在當前程式碼中沒有找到相應的處理程式,就會在後臺自動建立一個對應的異常類的例項物件並丟擲,即自動丟擲
- 手動生成:
Expectation expectaion=new Expectation();
這樣建立的異常物件若不丟擲則對程式沒有任何影響,即和建立一個普通物件一樣,但是一旦丟擲,就會對程式執行產生影響,會導致程式終止.
//自動丟擲異常例子
public class Test01 {
public static void main(String[] args) {
int arr[]={1,2,3,4};
int index=4;
//我們寫相應的異常處理程式程式碼,由虛擬機器自動檢測建立異常物件自動丟擲。
System.out.println(getElements(arr, index));
}
public static int getElements(int arr[],int index)
{
return arr[index];//下標越界之後JVM會自動識別異常並建立一個異常物件並丟擲
}
}
//手動丟擲異常
public class Test01 {
public static void main(String[] args) {
int arr[]={1,2,3,4};
int index=4;
System.out.println(getElements(arr, index));
}
public static int getElements(int arr[],int index)
{
if (arr==null) throw new NullPointerException("陣列為空");//手動丟擲異常
if(index<0||index>arr.length-1) throw new IndexOutOfBoundsException("下標越界");
return arr[index];
}
}
throws
與
throw
不同相同的是,throws
,只是在可能出現異常的方法宣告異常,throws
不會丟擲一個異常物件,作用的物件是方法
- 不處理異常:與
try-catch
不同,throws
並不會在方法內部處理異常,只是宣告告訴呼叫者”該方法可能會丟擲這些異常,你需要處理它”若呼叫者不處理可接著throws
接著甩鍋 - 向上傳遞:若一個地方使用了
throws
宣告瞭可能會丟擲的異常,但方法內部沒有將其捕獲,那麼這些異常會向上傳遞到呼叫該方法的程式碼中. - 受檢查異常與非受檢查異常:
public class Test01 {
public static void main(String[] args) //在主函式也可以向上丟擲FileNotFoundException
{
//呼叫該方法時會報錯,以為getFile的異常被拋到呼叫其方法的程式碼
getFile();
}
public static void getFile () throws FileNotFoundException//向上丟擲異常,但不處理,交給呼叫者處理
{
FileInputStream inputStream = new FileInputStream("java開發");
}
}
try-catch
try-catch
的核心思想是將程式中可能會出現異常的程式碼塊(try
塊)與處理異常程式碼(catch
塊)分離,從而提高程式處理異常的能力
- 異常捕獲:
try
塊中包含了可能丟擲異常的程式碼.當執行這些程式碼發生異常,java會立即停止try
塊中的剩餘程式碼,建立一個異常物件並丟擲,並查詢與之匹配的catch
塊. - 異常處理:
catch
用於處理try
塊中
public class Test02 {
public static void main(String[] args) {
int result;
try{
result=10/0; //此時有除0異常
System.out.println("result:" + result);//若沒有異常此語句將會被執行
}
catch (ArithmeticException e){
System.err.println("An arithmeticException happened here"+e.getMessage());
}
System.out.println("try-catch語句之後保持順序執行");
}
}
finally
finally
是java異常處理中一個重要的部分,它與try
語句和catch
語句共同使用,以確保無論是否發生異常,某些程式碼都是能執行的 ,即finally
塊用於包含那些無論是否發生異常都必須執行的程式碼
執行時機
- 正常完成:如果
try
塊中的程式碼正常執行,沒有發生異常,則finally
塊在try
塊之後執行 - 異常發生:如果
try
塊中的的程式碼丟擲異常,並且這個異常被相應的catch
塊捕獲(或者沒有被catch
捕獲但異常被傳遞出去),則finally
塊會在catch
塊之後執行,或者是在try
塊之後異常傳遞之前執行 - Return:若在
try
塊和catch
塊中帶有return
關鍵字,則finally
塊會在return
之前執行,但finally
塊不會改變return
的返回值,除非finally
塊中也包含return
語句,則返回finally
塊中的值
public class Test01 {
public static void main(String[] args)throws
{
getFile();
}
public static void getFile ()
{
try {
//可能會找不到檔案
FileInputStream inputStream = new FileInputStream("java開發");
}
catch (FileNotFoundException e){
//若找不到檔案者丟擲notFound異常
System.out.println(e.getMessage());
}
finally {
//無論如何都會執行關閉檔案語句
System.out.println("關閉檔案");
}
}
}
自定義異常
透過繼承java中
Exception
類或其他子類(如RuntimeException
)來建立異常類,該方式可以讓開發者自定義特定的錯誤異常,可以更靈活和準確處理程式中的錯誤異常
自定義異常的建立
- 確定基類(父類)
- 自定義異常應該繼承相應的Exception或其子類,如希望檢驗受檢異常則應該繼承
Exception
,如果希望執行時異常,則應該繼承RuntimeException
因此在每次自定義異常時要準確的繼承相應基類
- 編寫自定義異常類
- 可以建立一個新的類,並使其繼承所選擇的異常父類
- 可新增自定義屬性和方法
- 可重寫相應的的
toString() getMessage()
方法
public class LessThan0Exception extends RuntimeException{
//自定義一個異常類LessThan0Exception 繼承了RuntimeException類
public LessThan0Exception() { //無參構造方法
super("該數為負數!");//呼叫父類丟擲資訊方法
}
}
public static void main(String[] args) {
int age=-10;//主函式中
if(age<10)
{ //若age為負數則會丟擲自定義異常
throw new LessThan0Exception();
}
}
隨機數
隨機數在日常開發中也是十分常見的,例如抽獎,隨機點名等等,所有隨機數都是
Random
類的使用
基本用法
public static void main(String[] args) {
Random random = new Random();//例項化一個random物件
System.out.println(random.nextInt());//隨機產生一個int型別的數
System.out.println(random.nextInt(10));//隨機產生一個[0,10)的數
System.out.println(random.nextDouble());//隨機產生一個雙精度的浮點數
System.out.println(random.nextDouble(10));//隨機產生一個[0,9)的浮點數
}
日期類
在開發工作中日期類也是十分常見的
Date
作為一個最基礎的日期類,是我們學習的必要
public static void main(String[] args) {
Date date = new Date();
//Date重寫了toString()方法
System.out.println(date);//以美國國家習慣列印出該天的日期格式符
System.out.println(date.getTime());//列印從1970.01.01到目前為止的毫秒數
}
日曆類
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();//獲取日曆類物件
System.out.println(calendar.get(Calendar.YEAR));//獲取當前年份
System.out.println((calendar.get(Calendar.MONTH) + 1));//獲取當前月份
//由於java月份範圍是[0,11] 因此要加上一個1
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//顯示今天是該月的第幾天
System.out.println(calendar.get(Calendar.HOUR));//獲取當前小時數(12進位制)
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));//獲取當前小時(24進位制)
System.out.println(calendar.get(Calendar.MINUTE));//獲取分鐘數
System.out.println(calendar.get(Calendar.SECOND));//獲取秒數
System.out.println(calendar.get(Calendar.MILLISECOND));//獲取毫秒數
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//獲取該填是該年的第幾天
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));//獲取該天是該周的第幾天即 星期幾
calendar.set(Calendar.MONTH,6);//設定當前月份為6月
System.out.println(calendar.get(Calendar.MONTH));
calendar.add(Calendar.YEAR,1);//add(),將y,m,d或加上一定的數
System.out.println(calendar.get(Calendar.YEAR));
calendar.getTime();
}
SimpleDataFormat
該類是用於格式化和解析日期的具體類,允許進行格式化(日期→文字),解析(文字→日期)和規範化
- 構造方法:
SimpleDataFormat(String pattern)
指定日期格式 其中y:年,MM:月,dd日 HH:小時,mm:分鐘,ss秒;例如”yyyy-MM-dd HH:mm:ss”
則表示為 2024-07-03 22:42:15 - 日期格式化:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//將當前的日期格式化為yyyy-MM-dd HH:mm:ss
String date=simpleDateFormat.format(new Date());//傳入一個Date型別的引數
System.out.println(date);
結果為:2024-07-03 23:26:58
- 日期解析:在日期解析中會用到
SimpleDateFormat
類中的Parse
函式,其主要的作用是資料型別的轉換(包括將字串轉化為程式所需的型別,int,float,date,time等等),複雜資料結構的生成(主要包括引用資料型別,陣列,列表,等等),
String dateString="2000-01-01 12:00:00";
try{ //可能會出現解析錯誤異常因此要做好異常處理
Date date1 = simpleDateFormat.parse(dateString);
System.out.println(date1);
}
catch (ParseException e)
{ //當出現解析錯誤異常則向控制檯列印出相應的錯誤
e.printStackTrace();
}
結果為:Sat Jan 01 12:00:00 CST 2000
包裝類
包裝類,是基本資料型別的封裝形式,它允許將基本資料型別當作物件來處理,從而可以呼叫相應的方法.
基本資料型別與包裝類對應關係
基本資料型別 | 包裝類 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
包裝類的作用
- 提供物件化的方法:包裝類提供了一系列的方法,允許對基本資料型別進行跟複雜的操作,如型別轉化,解析字串等等
- 自動裝箱與拆箱:允許包裝類與基本資料型別可以相互轉化,簡化了程式設計工作
自動裝箱與拆箱
自動裝箱
自動裝箱指的是java編譯器在編譯階段自動將基本資料型別轉化為包裝類,該過程是透明的,無需手動編寫程式碼
int num=10;
Integer integer = num; //相當於Integer integer= Integer.valueof(num);
System.out.println(integer);
解釋:num
是基本資料型別int
,integer
是一個Integer型別變數,當把num
的值賦給integer
時,編譯器會自動執行裝箱操作,將int
型別的num
轉化為Integer
型別
底層實現原理
- 自動裝箱實際是呼叫包裝類
valueOf()
方法實現的,編譯器會自動將裝箱操作轉化為對valueOf()
方法的呼叫,例如上述例子就是自動呼叫了
Integer integer=Integer.valueOf(num);
- 注意!!
每次JVM執行時,對於Integer
,Short
,Byte
,Character
,Boolean
會自動快取-128到127之間的整數(Byte,Short
)以及字元值和布林值.這意味著在給範圍內的裝箱操作會返回快取中的物件引用,而不是新建立物件
自動拆箱
自動拆箱是裝箱的逆過程,指的是在編譯階段自動將包裝類轉化為基本資料型別的過程,該操作也是透明的,無需手寫轉化程式碼
Integer integer=10;
int i=integer;// int num=integer.intValue(i);
System.out.println(i);
解釋:num
是基本資料型別int
,integer
是一個Integer型別變數,當把num
的值賦給integer
時,編譯器會自動執行拆箱操作,將Integer
型別的num
轉化為int
型別
底層實現原理
自動拆箱的過程實際上是呼叫了包裝類中的xxxValue()
方法(如 doubleValue()
,intValue()
),編譯器會自動將拆箱操作轉化為相對應的xxxValue()
操作,上述例子實際被編譯器轉為