一、異常
1、異常介紹
異常:程式在運作過程中出現的一些非正常現象。在開發中需要針對不同的異常給出解決方案。
在Java中使用Exception表示異常。
程式運作過程中出現的問題Java将其分類兩大類:
- Error:如果程式中出現的XxxxYyyyError,那麼表示程式出現重大錯誤,是需要修改源代碼,沒有補救的機會。
- Exception:表示程式中出現一些可以解決的問題,在程式設計時可以提前給出一些解決的方案。
2、異常的展現

3、異常的解決
在進行異常預先處理的時候,需要确定兩個角色問題:
- 方法的定義者:在定義方法的時候,如果方法中的确有異常會出現,這時異常不需要調用者知道,這時需要手動在方法中将異常給捕獲住(内部消化異常),如果異常必須抛給調用者,一定要在方法内部使用throw 關鍵字将異常對象抛出。
- 方法的調用者:被動調用别的方法,如果被調用的方法中抛出了異常,自己屬于被動的在接受異常, 這時處理方案也有兩個:在調用的代碼地方捕獲,或者自己繼續抛出。
異常的解決方案:
-
捕獲異常:
try{
可能出現異常的代碼
}catch( 異常類名 變量名 ){
處理異常
}
-
抛出異常:
在判斷的地方發現可能有異常,使用throw關鍵字抛出異常,在方法定義的位置需要使用throws來聲明方法中抛出的某些異常。
4、異常處理舉例
4.1、方法的定義者将異常抛出
public class Circle {
private double radius;
private static final double PI = 3.14;
// 圓需要構造方法,在建立的時候給半徑進行初始化
public Circle( double radius ) {
// 對半徑進行判斷
if( radius <= 0 ) {
// 手動抛出異常
throw new IllegalArgumentException("圓的半徑不能小于等于零");
}
this.radius = radius;
}
// 擷取圓面積
public double getArea() {
return this.radius * this.radius * PI;
}
}
4.2、方法調用者捕獲異常
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
// 建立圓的對象,但是傳遞的半徑大于零
Circle c = new Circle( -3 );
double area = c.getArea();
System.out.println(area);
}catch( IllegalArgumentException e ) {
//IllegalArgumentException e = new IllegalArgumentException("圓的半徑不能小于等于零");
//e.printStackTrace();
// 在項目中異常被抓住之後,需要在catch代碼塊中通過IO技術,将異常的資訊寫到檔案中(記錄異常日志)
// 後期項目的維護人員,通過擷取異常日志檔案,查閱項目運作過程中的一些問題,進而給出解決方案
}
}
}
5、Java中的異常體系
在Java中已經将程式可能出現的常見的異常現象都已經安裝面向對象的思想,将異常封裝成不同的類。
如果在開發中遇到了類似的問題,需要抛出異常,這時可以借助Java中相關的異常類名,将這個異常抛出。
在JDK中衆多的異常形成一個異常的繼承體系:
在JDK中異常的頂層父類Throwable,它下面有2個子類:
- Error:錯誤
- Exception:異常
-
Exception:Exception或Exception下非RuntimeException的子類都稱為編譯時期異常,
在程式中如果用到别的方法上通過throws關鍵字顯示聲明了編譯時期的異常,這時需要在自己調用的代碼中給出解決方案(1、在方法上使用throws繼續聲明,2、在方法内部使用try - catch捕獲)、
- RuntimeException:運作時期異常,可以在編寫代碼的時候不管。
-
6、自定義異常
在項目中會遇到一些異常問題,在JDK找不到一個正确的異常類描述,這時一般需要在項目中自定義一些異常類,然後表示項目中某些特殊的異常資訊。
通過觀察JDK中已經存在某些異常類,發現從Exception類往下的所有異常類他們中隻提供的一些構造方法,沒有其他額外方法,在構造方法中都是通過super語句将參數等資訊交給父類處理。最終所有異常都是交給Throwable類進行處理。
自定義異常類:
1、定義類,提供相關的構造方法
2、類需要找到JDK中某個異常類作為父類
3、在構造方法中通過super語句将參數交給父類處理
/*
* 如果自定義異常屬于編譯時期需要處理的,繼承Exception,
* 如果屬于運作時期的,繼承RuntimeException
*/
public class RadiusException extends Exception {
public RadiusException() {
super();
}
public RadiusException(String msg) {
super(msg);
}
}
一般項目中自定義異常的包名: com.neusoft.exce
7、異常的其他組合
關于try-catch的組合還有别的寫法:
// try-catch組合捕獲異常
try{
可能有異常的代碼
}catch( 異常類名 變量名 )
{
處理異常的代碼
}
// 可能程式中會出現多種異常, 每個異常都需要單獨捕獲進行異常的處理
try{
可能有異常的代碼
}
catch( 異常類名 變量名 )
{
處理異常的代碼
}
catch( 異常類名 變量名 )
{
處理異常的代碼
}
catch( 異常類名 變量名 )
{
處理異常的代碼
}
// 上面這個多catch捕獲異常的時候,一定要從小的異常往大的異常捕獲
// try-catch-finally
try{
可能出現異常的代碼
}catch( 異常類型 變量名 ){
}
........
finally{
書寫的必須要執行的代碼
}
public class ExceptionDemo4 {
public static void main(String[] args) {
try {
int s = div(1,0);
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("必須執行的代碼");
}
}
// 除法運算
public static int div(int a , int b) {
return a / b;
}
}
// try - finally : 組合異常是沒有被捕獲的,程式中有某些代碼是必須執行的,但不管有沒有異常,也不進行異常的處理
try{
}finally{
}
面試題:final(修飾符)、finally(異常中必須要執行代碼)、finalize(通知垃圾回收器) 差別?
8、方法複寫中異常問題
方法重載:在一個類中方法名相同,參數清單不同(個數、類型、順序)
方法重寫:在子父類之間子類中出現了和父類一模一樣的方法(傳回值類型、方法名、參數清單一緻)。
重寫異常的細節:
- 當父類的方法沒有聲明(throws)異常,子類複寫父類的方法時也不能聲明異常。
- 當父類的方法聲明異常,子類複寫的時候可以聲明相同的異常或者異常的子異常。
- 當父類的方法聲明多個異常(throws 異常1,異常2,異常3),子類複寫父類的方法時可以聲明這些異常中部分。
二、日期
1、日期介紹
在項目中日期資料經常被使用的,但是存在問題,Java中的日期預設的格式不是中國人經常使用的格式,需要手動的進行轉換。
Date:表示的日期資料(年月日時分秒),主要是以毫秒進行日期操作。
Calendar:表示月曆,對日期進行運算操作,或者日期中的各種資料。
DateFormat:日期資料進行格式化操作(它中提供的方法格式固定)。
SimpleDateFormat:日期資料進行格式化操作(日期的格式由程式員自己指定)。
2、Date類
2.1、Date介紹
Date講解的是java.util包在的,在程式中需要導包。
如果項目中使用的java.lang包在的類,不需要導包,在程式中已經預設導入。
public class DateDemo {
public static void main(String[] args) {
Date d = new Date();
/*
* 在使用輸出語句輸出某個對象的引用變量的時候,
* 輸出語句是在通過引用變量調用的Object類中的toString方法
* System.out.println(d);
* System.out.println(d.toString());
*/
System.out.println(d);
Demo demo = new Demo();
System.out.println(demo);
}
}
2.2、Date中的毫秒值
// 關于Date中的毫秒值問題
public static void demo2() {
// 建立Date類的時候,可以指定一個毫秒值,date對象就表示具體的某個時間點
Date d = new Date(19999889991232L);
System.out.println(d);
Date d2 = new Date();
// 擷取Date表示的毫秒值 getTime
System.out.println( d2.getTime() );
// 給Date設定毫秒值 setTime
d2.setTime(19999889991232L);
}
3、Calendar類
3.1、Calendar類介紹
Calendar類是抽象類,但是其中提供了靜态的方法傳回了本類對象(傳回的Calendar類的子類對象)。
3.2、get擷取時間資料
// 測試Calendar的基本使用
public static void demo1() {
Calendar c = Calendar.getInstance();
System.out.println(c);
// Calendar中提供get、set方法可以擷取其中儲存的與時間有關系的資料
int year = c.get( Calendar.YEAR );
System.out.println(year);
System.out.println( c.get(Calendar.MONTH) + 1 );
System.out.println( c.get(Calendar.DAY_OF_MONTH) );
System.out.println( c.get(Calendar.HOUR) );
System.out.println( c.get(Calendar.MINUTE) );
System.out.println( c.get(Calendar.SECOND) );
}
3.3、add和set改變時間資料
// set方法 直接修改時間資料
private static void demo3() {
Calendar c = Calendar.getInstance();
c.set(2100, 1, 1);
System.out.println( c.get( Calendar.YEAR ) );
System.out.println( c.get(Calendar.MONTH) + 1 );
System.out.println( c.get(Calendar.DAY_OF_MONTH) );
}
// add方法對時間進行運算(增加和減少)
private static void demo2() {
Calendar c = Calendar.getInstance();
// 在目前的時間年份上+3
//c.add(Calendar.YEAR, 3);
c.add(Calendar.DAY_OF_MONTH, 1000);
System.out.println( c.get( Calendar.YEAR ) );
System.out.println( c.get(Calendar.MONTH) + 1 );
System.out.println( c.get(Calendar.DAY_OF_MONTH) );
}
3.4、任意一年2月有多少天
// 任意一年2月有多少天
private static void demo4() {
// 将Calendar表示的時間設定為當年的3月1日,
// 然後使用add方法将月中的天數-1,為2月的最後一天
Calendar c = Calendar.getInstance();
for( int year = 2000 ; year < 2100 ; year++ ) {
c.set(year, 2 , 1);
c.add(Calendar.DAY_OF_MONTH, -1);
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "年的2月有" + day + "天");
}
}
4、SimpleDateFormat類
4.1、SimpleDateFormat介紹
SimpleDateFormat
是一個以與語言環境有關的方式來格式化和解析日期的具體類。
它允許進行格式化(日期 -> 文本)、解析(文本 -> 日期)和規範化。
4.2、日期格式化和解析
public class SimpleDateFormatDemo {
public static void main(String[] args) throws ParseException {
demo3();
}
private static void demo3() throws ParseException {
String s = "2020年07-29日 15:53:40";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM-dd日 HH:mm:ss");
Date date = sdf.parse(s);
System.out.println(date);
}
// 使用SimpleDateFormat類的同時指定轉換格式
private static void demo2() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d = new Date();
// 轉換
String s = sdf.format(d);
System.out.println(s);
}
// 示範SimpleDateFormat
private static void demo1() {
// 建立SimpleDateFormat對象
SimpleDateFormat sdf = new SimpleDateFormat();
// 将Date轉成String
Date d = new Date();
// 轉換
String s = sdf.format(d);
System.out.println(s);
}
}
三、基本類型包裝類
1、基本類型包裝類介紹
Java認為所有的資料、事物、個體等等都應該先封裝類,然後通過類或者對象去調用其中的屬性或者行為。
8種基本類型它們不屬于類類型(不屬于對象),更沒有具體的屬性和方法可以調用。
針對每一個基本類型都提供一個對應的類類型,稱為基本類型的包裝類。
基本類型 | 包裝類型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
隻需要注意int和char對應的包裝類型即可。
8個基本類型對應的包裝類它們的類都在java.lang包下。
2、Double類
public static void demo1() {
// 基本類型
double d = 3.14;
int x = (int) d;
// 将整個基本類型包裝成對應的類類型
Double d2 = new Double( d );
int x2 = d2.intValue();
Double d3 = Double.valueOf( d );
}
3、自動裝箱和拆箱
從JDK5版本開始,為基本類型和對應的包裝類之間提供便捷的轉換方式:
// 自動裝箱和拆箱
private static void demo2() {
// 這樣的操作比較麻煩
Float f = new Float( 123F );
// 自動裝箱
// 允許直接将基本類型資料指派給對應的包裝類型
// 底層将将1.23F 小數先轉成Float對象,然後将對象的位址指派給f2
Float f2 = 1.23F;
// 自動拆箱:可以将包裝類型的引用變量直接指派給對應的基本類型
// 底層會先從對象中擷取到包裝的數值,然後将這個值指派給基本類型
float f3 = f2;
int x = 12;
Integer y = 23;
y+=x; // y = y + x
}
4、面試題
private static void demo3() {
Integer x = 127;
Integer y = 127;
Integer i = 128;
Integer j = 128;
System.out.println( x == y ); // true
System.out.println( i == j ); // false
}
關于上面的結果分析:查閱Integer的源碼,發現在其中有個成員内部類
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
在Integer中定義的成員内部類IntegerCache,将 -128 到 127 之間的每個數字先提前封裝成Integer對象,并且緩存起來。
當在程式中通過下面的代碼:
Integer x = 127;
Integer y = 127;
Integer i = 128;
Integer j = 128;
将int類型的資料指派給Integer引用的時候,會發生自動裝箱,這時不是将每個int值都包裝成Integer對象,而是先判斷目前需要包裝的int值在不在IntegerCache緩存的數組中,如果有直接将緩存數組中的對象的位址指派給程式中的x和y引用。但是128并不再緩存中,是以每次裝箱都會重新封裝128為一個新的對象。