------- android教育訓練、java教育訓練、期待與您交流! ----------
一、異常處理:
在程式中,錯誤可能産生于程式員沒有預料到的各種情況,或是因為超出了程式員控制之外的環境因素,如使用者的壞資料、試圖打開一個根本不存在的檔案等。在java中這種在程式運作時可能出現的一些錯誤稱為異常。異常是一個在程式執行期間發生的事件,它中斷了正在執行的程式的正常指令流。
異常由來:問題也是現實生活中一個具體的事物,也可以通過java的類的形式進行描述。并封裝成對象。其實就是java對不正常情況進行描述後的對象展現。
對于問題的劃分:兩種:一種是嚴重的問題,一種非嚴重的問題。
對于嚴重的,java通過Error類進行描述。對于Error一般不編寫針對性的代碼對其進行處理。
對于非嚴重的,java通過Exception類進行描述。 對于Exception可以使用針對性的處理方式進行處理。
無論Error或者Exception都具有一些共性内容。比如:不正常情況的資訊,引發原因等。
Throwable
|--Error
|--Exception
2,異常的處理
java 提供了特有的語句進行處理。
try
{
需要被檢測的代碼;
}
catch(異常類 變量)
{
處理異常的代碼;(處理方式)
}
finally
{
一定會執行的語句;
}
3,對捕獲到的異常對象進行常見方法操作。
String getMessage():擷取異常資訊。
class Demo
{
int div(int a,int b)throws Exception//在功能上通過throws的關鍵字聲明了該能
//有可能會出現問題。
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,1);
System.out.println("x="+x);
}
catch (Exception e)//Exception e = new ArithmeticException();
{
System.out.println("除零啦");
System.out.println(e.getMessage());// / by zero;
System.out.println(e.toString());// 異常名稱 : 異常資訊。
e.printStackTrace();//異常名稱,異常資訊,異常出現的位置。
//其實jvm預設的異常處理機制,就是在調用
//printStackTrace方法。
//列印異常的堆棧的跟蹤資訊。
}
System.out.println("over");
}
}
二、自定義異常:
因為項目中會出現特有的問題,而這些問題并未被java所描述并封裝對象。是以對于這些特有的問題可以按照java的對問題封裝的思想,将特有的問題進行自定義的異常封裝。
當在函數内部出現了throw抛出異常對象,那麼就必須要給對應的處理動作。
要麼在内部try catch處理。
要麼在函數上聲明讓調用者處理。
一般情況下,函數内出現異常,函數上需要聲明。
如何定義異常資訊呢?
因為父類中已經把異常資訊的操作都完成了。是以子類隻要在構造時,将異常資訊傳遞給父類通過super語句。那麼就可以直接通過getMessage方法擷取自定義的異常資訊。
自定義異常:必須是自定義類繼承Exception。
繼承Exception原因:
異常體系有一個特點:因為異常類和異常對象都被抛出。他們都具備可抛性。這個可抛性是Throwable這個體系中獨有特點。隻有這個體系中的類和對象才可以被throws和throw操作。
throws和throw的差別:
throws使用在函數上。後面跟的異常類,可以跟多個,用逗号隔開。
throw使用在函數内。後跟的是異常對象。
class FuShuException extends Exception //getMessage();
{
private int value;
FuShuException()
{
super();
}
FuShuException(String msg,int value)
{
super(msg);
this.value = value;
}
public int getValue()
{
return value;
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
//手動通過throw關鍵字抛出一個自定義異常對象。
throw new FuShuException("出現了除數是負數的情況------“,b);
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-9);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
//System.out.println("除數出現負數了");
System.out.println("錯誤的負數是:"+e.getValue());
}
System.out.println("over");
}
}
Exceptoin中有一個特殊的子類異常RuntimeException 運作時異常。
如果在函數内容抛出該異常,函數上可以不用聲明,編譯一樣通過。
如果在函數上聲明了該異常。調用者可以不用進行處理。編譯一樣通過;
之是以不用在函數聲明,是因為不需要讓調用者處理。
當該異常發生,希望程式停止。因為在運作時,出現了無法繼續運算的情況,希望停止程式後,對代碼進行修正。
自定義異常時:如果該異常的發生,無法在繼續進行運算,就讓自定義異常繼承RuntimeException。
對于異常分兩種:
1,編譯時被檢測的異常。
2,編譯時不被檢測的異常(運作時異常。RuntimeException以及其子類)
class FuShuException extends RuntimeException
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws Exception//throws ArithmeticException
{
if(b<0)
throw new Exception("出現了除數為負數了");
if(b==0)
throw new ArithmeticException("被零除啦");
return a/b;
}
}
class ExceptionDemo4
{
public static void main(String[] args)
{
Demo d = new Demo();
int x = d.div(4,-9);//被除數為負數,出現異常,停止運作
System.out.println("x="+x);
System.out.println("over");
}
}
三、異常部分總結:
是什麼?是對問題的描述。将問題進行對象的封裝。
---------------------------------------------------------------------------------------------------
異常體系:
Throwable
|--Error
|--Exception
|--RuntimeException
異常體系的特點:異常體系中的所有類以及建立的對象都具備可抛性。
也就是說可以被throw和throws關鍵字所操作。
隻有異常體系具備這個特點。
----------------------------------------------------------------------------------------------------
throw和throws的用法:
throw定義在函數内,用于抛出異常對象。
throws定義在函數上,用于抛出異常類,可以抛出多個用逗号隔開。
當函數内容有throw抛出異常對象,并未進行try處理。必須要在函數上聲明,否則編譯失敗。
注意,RuntimeException除外。也就說,函數内如果抛出的RuntimeExcpetion異常,函數上可以不用聲明。
---------------------------------------------------------------------------------------------------------
如果函數聲明了異常,調用者需要進行處理。處理方法可以throws可以try。
異常有兩種:
編譯時被檢測異常
該異常在編譯時,如果沒有處理(沒有抛也沒有try),編譯失敗。
該異常被辨別,代表這可以被處理。
運作時異常(編譯時不檢測)
在編譯時,不需要處理,編譯器不檢查。
該異常的發生,建議不處理,讓程式停止。需要對代碼進行修正。
---------------------------------------------------------------------------------------------------------
異常處理語句:
try
{
需要被檢測的代碼;
}
catch ()
{
處理異常的代碼;
}
finally
{
一定會執行的代碼;
}
有三個結合格式:
1. try
{
}
catch ()
{
}
2. try
{
}
finally
{
}
3. try
{
}
catch ()
{
}
finally
{
}
注意:
1,finally中定義的通常是 關閉資源代碼。因為資源必須釋放。
2,finally隻有一種情況不會執行。當執行到System.exit(0);fianlly不會執行。
3,catch是用于處理異常。如果沒有catch就代表異常沒有被處理過,如果該異常是檢測時異常。那麼必須聲明。
--------------------------------------------------------------------------------------------------------
自定義異常:
定義類繼承Exception或者RuntimeException
1,為了讓該自定義類具備可抛性。
2,讓該類具備操作異常的共性方法。
當要定義自定義異常的資訊時,可以使用父類已經定義好的功能。
異常異常資訊傳遞給父類的構造函數。
class MyException extends Exception
{
MyException(String message)
{
super(message);
}
}
自定義異常:按照java的面向對象思想,将程式中出現的特有問題進行封裝。
-----------------------------------------------------------------------------------------------------
異常的好處:
1,将問題進行封裝。
2,将正常流程代碼和問題處理代碼相分離,友善于閱讀。
異常的處理原則:
1,處理方式有兩種:try 或者 throws。
2,調用到抛出異常的功能時,抛出幾個,就處理幾個。
一個try對應多個catch。
3,多個catch,父類的catch放到最下面。
4,catch内,需要定義針對性的處理方式。不要簡單的定義printStackTrace,輸出語句。也不要不寫。
當捕獲到的異常,本功能處理不了時,可以繼續在catch中抛出。
try
{
throw new AException();
}
catch (AException e)
{
throw e;
}
如果該異常處理不了,但并不屬于該功能出現的異常。可以将異常轉換後,在抛出和該功能相關的異常。或者異常可以處理,當需要将異常産生的和本功能相關的問題提供出去,讓調用者知道,并處理。也可以将捕獲異常處理後,轉換新的異常。
try
{
throw new AException();
}
catch (AException e)
{
// 對AException處理。
throw new BException();
}
異常的注意事項:
1,子類在覆寫父類時,如果父類的方法抛出異常,那麼子類的覆寫方法,隻能抛出父類的異常或者該異常的子類。
2,如果父類方法抛出多個異常,那麼子類在覆寫該方法時,隻能抛出父類異常的子集。
3,如果父類或者接口的方法中沒有異常抛出,那麼子類在覆寫方法時,也不可以抛出異常。如果子類方法發生了異常。就必須要進行try處理。絕對不能抛。
四、多線程
如果一次隻完成一件事情,很容易實作,但事實上現實生活中很多事情都是同時進行的,是以在java中為了模拟這種狀态,引入了線程機制。簡單地說,當程式同時完成多件事情時,就是所謂的多線程程式。多線程應用相當廣泛,使用多線程可以建立視窗程式、網絡程式等。
在Windows作業系統是多任務作業系統,它以程序為機關。一個程序是一個包含有自身位址的程式,每個獨立執行的程式都成為程序,也就是正在執行的程式。系統可以配置設定給每個程序一段有限的使用CPU的時間(也可以稱為CPU時間片),CPU在這段時間中執行某個程序,然後下一個時間片又跳至另一個程序中去執行。由于CPU轉換較快,是以使得每個程序好像是同時執行一樣。
建立線程的第一種方式:繼承Thread類
步驟:
1,定義類繼承Thread。
2,複寫Thread類中的run方法。目的:将自定義代碼存儲在run方法。讓線程運作。
3,調用線程的start方法。該方法兩個作用:啟動線程,調用run方法。
為什麼要覆寫run方法呢?
Thread類用于描述線程。該類就定義了一個功能,用于存儲線程要運作的代碼。該存儲功能就是run方法。也就是說Thread類中的run方法,用于存儲線程要運作的代碼。
例如:建立兩個線程,和主線程交替運作。
class Test extends Thread
{
//private String name;
Test(String name)
{
//this.name = name;
super(name);
}
public void run()
{
for(int x=0; x<60; x++)
{
System.out.println(Thread.currentThread().getName()+" run..."+x);// static Thread currentThread():擷取目前線程對象。getName(): 擷取線程名稱。
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one---");
Test t2 = new Test("two+++");
t1.start();
t2.start();
for(int x=0; x<60; x++)
{
System.out.println("main....."+x);
}
}
}
建立線程的第二種方式:實作Runable接口
步驟:
1,定義類實作Runnable接口
2,覆寫Runnable接口中的run方法。将線程要運作的代碼存放在該run方法中。
3,通過Thread類建立線程對象。
4,将Runnable接口的子類對象作為實際參數傳遞給Thread類的構造函數。
為什麼要将Runnable接口的子類對象傳遞給Thread的構造函數。因為,自定義的run方法所屬的對象是Runnable接口的子類對象。是以要讓線程去指定指定對象的run方法。就必須明确該run方法所屬對象。
5,調用Thread類的start方法開啟線程并調用Runnable接口子類的run方法。
實作Runnable方式的好處是:避免了單繼承的局限性。在定義線程時建議使用實作Runnable方式。
例如:
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//建立了一個線程;
Thread t2 = new Thread(t);//建立了一個線程;
Thread t3 = new Thread(t);//建立了一個線程;
Thread t4 = new Thread(t);//建立了一個線程;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
在實作Runnable方式的例子中我們發現了一些問題,通過分析,發現列印出了0,-1,-2等錯票。多線程的運作出現了安全問題。
問題的原因:
當多條語句在操作同一個線程共享資料時,一個線程對多條語句隻執行了一部分,還沒有執行完,另一個線程參與進來執行。導緻共享資料的錯誤。
解決辦法:
對多條操作共享資料的語句,隻能讓一個線程都執行完。在執行過程中,其他線程不可以參與執行。
Java對于多線程的安全問題提供了專業的解決方式。就是同步代碼塊。
synchronized(對象)
{
需要被同步的代碼
}
其中對象就如同鎖,持有鎖的線程可以在同步中執行。沒有持有鎖的線程即使擷取cpu的執行權,也進不去,因為沒有擷取鎖。
同步的前提:
1,必須要有兩個或者兩個以上的線程。
2,必須是多個線程使用同一個鎖。保證了同步中隻能有一個線程在運作。
好處:解決了多線程的安全問題。
弊端:多個線程需要判斷鎖,較為消耗資源。
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
//try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步函數的話也是可以的,要注意的是,同步函數用的鎖是this。
如果同步函數被靜态修飾後,通過驗證,發現不在是this,因為靜态方法中也不可以定義this。靜态進記憶體時,記憶體中沒有本類對象,但是一定有該類對應的位元組碼檔案對象。
類名.class 該對象的類型是Class
是以,靜态的同步方法,使用的鎖是該方法所在類的位元組碼檔案對象。 類名.class
在同步代碼塊中如何找問題:
1,明确哪些代碼是多線程運作代碼。
2,明确共享資料。
3,明确多線程運作代碼中哪些語句是操作共享資料的。
死鎖:同步中嵌套同步就會造成死鎖。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)//同步,鎖為MyLock.locka
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
synchronized(MyLock.lockb)//同步,鎖為MyLock.lockb
{
System.out.println(Thread.currentThread().getName()+"..if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)//同步,鎖為MyLock.lockb
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
synchronized(MyLock.locka)//同步,鎖為MyLock.locka
{
System.out.println(Thread.currentThread().getName()+".....else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
單例模式中應用同步代碼塊:
//餓漢式。
/*
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
*/
//懶漢式(此為考點,要掌握)
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}