java基礎複習05
java基礎複習
面對物件
4.1、Debug追蹤
Debug除錯程式:
- 可以讓程式碼逐行執行,檢視程式碼執行的過程,除錯程式中出現的bug
使用方式:
- 在行號的右邊,滑鼠左鍵單擊,新增斷點(每個方法的第一行,哪裡有bug新增到哪裡)右鍵,選擇Debug執行程式
- 程式就會停留在新增的第一個斷點處
執行程式:
-
f8: 逐行執行程式
-
f7: 進入到方法中
-
shift+f8: 跳出方法
-
f9: 跳到下一個斷點,如果沒有下一個斷點,那麼就結束程式
-
ctrl+f2:退出debug模式,停止程式
-
Console:切換到控制檯
4.2、異常
異常: 指的是程式在執行過程中,出現的非正常的情況,最終會導致JVM的非正常停止。
在 java等面向物件的程式語言中,異常本身是一個類,產生異常就是建立異常物件並丟擲了一個異常物件。Java處理異常的方式是中斷處理。
throw關鍵字作用:
- 可以使用throw關鍵字在指定的方法中丟擲指定的異常
使用格式:
if(判斷語句){
throw new xxException(“異常產生的原因"");
}
注意:
-
throw關鍵字必須寫在方法的內部
-
throw關鍵字後邊new的物件必須是Exception或者Exception的子類物件
-
throwv關鍵字丟擲指定的異常物件,我們就必須處理這個異常物件
-
throw關鍵字後邊建立的是RuntimeException或者是RuntimeException的子類物件,我們可以不處理,預設交給JW處理(列印異常物件,中斷程式)
-
throw關鍵字後邊建立的是編譯異常(寫程式碼的時候報錯),我們就必須處理這個異常,要麼throws,要麼try...catch
throws關鍵字
- 異常處理的第一種方式,交給別人處理
作用:
-
當方法內部丟擲異常物件的時候,那麼我們就必須處理這個異常物件
-
可以使用throws關鍵字處理異常物件,會把異常物件宣告批出給方法的呼叫者處理(自己不處理,給別人處理),最終交給JWw處理-->中斷處理
使用格式:在方法宣告時使用
修飾符 返回值型別 方法名(引數列表) throws AAAExcepiton,BBBExcepiton. . .{ }
注意:
-
throws關鍵字必須寫在方法宣告處
-
throws關鍵字後邊宣告的異常必須是Exception或者是Exception的子類
-
方法內部如果丟擲了多個異常物件,那麼throws後邊必須也宣告多個異常
- 如果丟擲的多個異常物件有子父類關係,那麼直接宣告父類異常即可
-
呼叫了一個宣告丟擲異常的方法,我們就必須的處理宣告的異常
- 要麼繼續使用throws宣告丟擲,交給方法的呼叫者處理,最終交給JVM要麼try...catch自己處理異常
try catch
格式:
try{
可能產生異常的程式碼
}catch(定義一個異常的變數,用來接收try中丟擲的異常物件){
異常的處理邏輯,異常物件之後,怎麼處理異常物件
一般在工作中,會把異常的資訊記錄到一個日誌中
}
catch(異常類名變數名){}
注意:
-
try中可能繪丟擲多個異常物件,那麼就可以使用多個catch來處理這些異常物件
-
如果try中產生了異常,那麼就會執行catch中的異常處理邏輯,執行完畢catch中的處理邏輯,繼續執行try...catch之後的程式碼如果try中沒有產生異常,那麼就不會執行catch中異常的處理邏輯,執行完try中的程式碼,繼續執行try. ..catch之後的程式碼
Finally程式碼塊
格式:
try{
可能產生異常的程式碼
}catch(定義一個異常的變數,用來接收try中丟擲的異常物件){
異常的處理邏輯,異常物件之後,怎麼處理異常物件
一般在工作中,會把異常的資訊記錄到一個日誌中
}
catch(異常類名變數名){
}finally{
無論是否出現異常都會執行
}
注意:
-
finally 不能單獨使用,必須和try—起使用
-
finally 一般用於資源釋放(資源回收),無論程式是否出現異常,最後都要資源釋放(IO)
-
如果finally有return語句,永遠返回finally中的結果,應該避免該情況。
4.3、多執行緒
併發:指兩個或多個事件在同一個時問段內發生。(交替執行)
並行:指兩個或多個事件在同一時刻發生(同時發生).(同時執行)
程序:
執行緒︰執行緒是程序中的一個執行單元,負責當前程序中程式的執行,一個程序中至少有一個執行緒。一個程序中是可以有多個執行緒的,這個應用程式也可以稱之為多執行緒程式。(簡而言之:一個程式執行後至少有一個程序,—個程序中可以包含多個執行緒)
執行緒排程
-
分時排程:
- 所有執行緒輪流使用CPU的使用權,平均分配每個執行緒佔用CPU的時間。
-
搶佔式排程:
- 優先讓優先順序高的執行緒使用CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個(執行緒隨機性),Java使用的為搶佔式排程。
主執行緒:執行主(main)方法的執行緒
單執行緒程式: java程式中只有一個執行緒執行從main方法開始,從上到下依次執行
多執行緒程式
建立多執行緒程式的第一種方式:建立Thread類的子類
java.lang. Thread類:是描述執行緒的類,我們想要實現多執行緒程式,就必須繼承Thread類
實現步驟:
-
建立一個Thread類的子類
-
在Thread類的子類中重寫Thread類中的run方法,設定執行緒任務(開啟執行緒要做什麼?)
-
建立Thread類的子類物件
-
呼叫Thread類中的方法start方法,開啟新的執行緒,執行run方法
- void start()使該執行緒開始執行;Java虛擬機器呼叫該執行緒的run方法。
- 結果是兩個執行緒併發地執行;當前執行緒(main執行緒)和另一個執行緒(執行其 run方法)。
- 多次啟動一個執行緒是非法的。特別是當執行緒已經結束執行後,不能再重新啟動。
public class MyThread extends Thread{ //1.建立一個Thread類的子類
@Override
public void run() { //2.在Thread類的子類中重寫Thread類中的run方法,設定執行緒任務(開啟執行緒要做什麼?)
for (int i = 0; i < 20; i++) {
System.out.println("run:"+i);
}
}
}
public class DemoThread {
public static void main(String[] args) {
MyThread mt = new MyThread();//3.建立Thread類的子類物件
mt.start();//4.呼叫Thread類中的方法start方法,開啟新的執行緒,執行run方法
for (int i = 0; i < 20; i++) {
System.out.println("main:"+i);
}
}
}
建立多執行緒程式的第二種方式:實現Runnable介面 java.Lang . Runnable
實現步驟:
1、建立一個Runnable介面的實現類
2、在實現類中重寫Runnable介面的run方法,設定執行緒任務
3、建立一個Runnable介面的實現類物件
4、建立Thread類物件,構造方法中傳遞Runnable介面的實現類物件
5、呼叫Thread類中的start方法,開啟新的執行緒執行run方法
public class RunnableImpl implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
public class DemoRunnable {
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
RunnableImpl ra = new RunnableImpl();
Thread th = new Thread(ra);
th.start();
}
}
Thread常用方法
public static void sleep(Long millis):使當前正在執行的執行緒以指定的毫秒數暫停〈暫時停止執行)。
public class DemoThread01 {
public static void main(String[] args) {
for (int i = 0; i <20 ; i++) {///模擬秒錶
System.out.println(i);
try {
Thread.sleep(1000);//使用Thread類的sleep方法讓程式睡眠1秒鐘
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
匿名內部類方式實現執行緒的建立
匿名內部類作用:簡化程式碼
- 把子類繼承父類,重寫父類的方法,建立子類物件合一步完成
把實現類實現類介面,重寫介面中的方法,建立實現類物件合成一步完成
匿名內部類的最終產物:子類/實現類物件,而這個類沒有名字
格式:
new 父類/介面(){
重複父類介面中的方法
};
public class Demo01 {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"+"+i);
}
}
}.start();
}
}
public class Demo02 {
public static void main(String[] args) {//用介面的方式
Runnable r = new Runnable(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"+程式設計師");
}
}
};
new Thread(r).start();
}
}
4.4、執行緒安全
多執行緒在用到相同資源時會產生安全問題
解決方法
-
同步程式碼塊。
-
同步方法。
-
鎖機制。
同步程式碼塊:synchronized關鍵字可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。
格式:
synchronized(同步鎖){
//需要操作的程式碼
}
注意:
-
通過程式碼塊中的鎖物件,可以使用任意的物件
-
但是必須保證多個執行緒使用的鎖物件是同一個
-
鎖物件作用:
- 把同步程式碼塊鎖住,只讓一個執行緒在同步程式碼塊中執行
public class RunnableImpl implements Runnable{
private int ticket = 100;
Object object = new Object();
@Override
public void run() {
while (true) {
synchronized (object) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在賣第:" + ticket + "張票");
ticket--;
}
}
}
}
}
public class DemoMain {
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
Thread t3 = new Thread(runnable);
t1.start();
t2.start();
t3.start();
}
}
同步方法
-
把訪問了共享資料的程式碼抽取出來,放到一個方法中
-
在方法上新增synchronized修飾符
格式:
修飾符 synchronized 返回值型別 方法名(引數列表){
可能會出現執行緒安全問題的程式碼(訪問了共享資料的程式碼)
}
public class RunnableImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
payTicket();
}
}
public synchronized void payTicket() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在賣第:" + ticket + "張票");
ticket--;
}
}
}
鎖機制
使用Lock鎖 java.util.concurrent.tocks.Lock介面
Lock實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作。
Lock介面中的方法:
-
void lock() 獲取鎖。
-
void unlock() 釋放鎖。
public class RunnableImpl implements Runnable {
private int ticket = 100;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "--->正在賣第:" + ticket + "張票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
}
}
}
}
}
4.5、等待喚醒機制
等待喚醒案例:執行緒之間的通訊
建立一個顧客執行緒(消費者):告知老闆要的包子的種類和數量,呼叫wait方法,放棄cpu的執行,進入到waiting狀態(無限等待)
建立一個老闆執行緒(生產者):花了5秒做包子,做好包子之後,呼叫notify方法,喚醒顧客吃包子
注意:
-
顧客和老闆執行緒必須使用同步程式碼塊包裹起來,保證等待和喚醒只能有一個在執行
-
同步使用的鎖物件必須保證唯一
-
只有鎖物件才能呼叫wait和notify方法
public class DemoWaitAndNotify {
public static void main(String[] args) {
Object o = new Object();
new Thread() {
@Override
public void run() {
synchronized (o){
System.out.println("告訴老闆要的包子的數量和種類");
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子已經做好了 ,開吃");
}
}
}.start();
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o){
System.out.println("老闆五秒後做好包子,告訴顧客可以吃了");
o.notify();
}
}
}.start();
}
}
進入到Timewaiting(計時等待)有兩種方式
-
使用sleep(Long m)方法,在毫秒值結束之後,執行緒睡醒進入到Runnable/BLocked狀態
-
使用wait(Long m)方法, wait方法如果在毫秒值結束之後,還沒有被notify響醒,就會自動醒來,執行緒睡醒進入到RunnabLe/BLoched狀態
喚醒的方法:
-
void notify()喚醒在此物件監視器上等待的單個執行緒。
-
void notifyAll()喚醒在此物件監視器上等待的所有執行緒。
吃包子案例
public class BaoZi {
String pi;
String xian;
Boolean flat =false;
}
public class BaoZiPu extends Thread{
private BaoZi bz;
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
if (bz.flat == false) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃貨正在吃:" + bz.pi + "、" + bz.xian + "的包子");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bz.flat = false;
bz.notify();
System.out.println("已經把:" + bz.pi + "、" + bz.xian + "的包子吃完了,包子鋪快繼續生產");
System.out.println("----------------------------------");
}
}
}
}
public class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
int count = 0;
while (true) {
synchronized (bz) {
if (bz.flat == true) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count % 2 == 0) {
bz.pi = "薄皮";
bz.xian = "豬肉";
} else {
bz.pi = "冰皮";
bz.xian = "牛肉";
}
count++;
System.out.println("包子鋪正在生產:" + bz.pi + "、" + bz.xian + "的包子");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bz.flat = true;
bz.notify();
System.out.println("包子鋪已經生產好了:" + bz.pi + "、" + bz.xian + "的包子,可以開吃了!");
}
}
}
}
public class Demo {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
new BaoZiPu(bz).start();
new ChiHuo(bz).start();
}
}