"TOC" 異常 基本概念 異常 是在運行時期發生的不正常情況。 異常類 在java中用類的形式對不正常情況進行了描述和封裝對象,描述不正常的情況的類。 異常就是java通過面向對象的思想將問題封裝成了對象.用異常類對其進行描述. 異常體系 不同的問題用不同的類進行具體的描述。 比如角標越界。空指針 ...
目錄
異常
基本概念
- 異常
是在運行時期發生的不正常情況。
- 異常類
在java中用類的形式對不正常情況進行了描述和封裝對象,描述不正常的情況的類。
異常就是java通過面向對象的思想將問題封裝成了對象.用異常類對其進行描述.
- 異常體系
不同的問題用不同的類進行具體的描述。 比如角標越界。空指針等等。問題很多,意味著描述的類也很多,將其共性進行向上抽取
不正常情況就分為了兩大類。
- Throwable: 無論是error,還是異常,問題,問題發生就應該可以拋出,讓調用者知道並處理。
- throws throw ,凡是可以被這兩個關鍵字所操作的類和對象都具備可拋性.
一般不可處理的. Error是由jvm拋出的嚴重性的問題
可以處理的.Exception
- 自定義異常:自定義的問題描述
異常的分類
- 編譯時被檢測異常:
- 只要是Exception和其子類都是(除了特殊子類RuntimeException體系)
- 這種問題一旦出現,希望在編譯時就進行檢測,讓這種問題有對應的處理方式。
- 這樣的問題都可以針對性的處理。
- 編譯時不檢測異常(運行時異常)
- 就是Exception中的RuntimeException和其子類。
- 這種問題的發生,無法讓功能繼續,運算無法進行,更多是因為調用者的原因導致的而或者引發了內部狀態的改變導致的。
- 那麼這種問題一般不處理,直接編譯通過,在運行時,讓調用者調用時的程式強制停止,讓調用者對代碼進行修正。
所以自定義異常時,要麼繼承Exception。要麼繼承RuntimeException。
throws 和throw的區別:
- throws使用在函數上.
throw使用在函數內.- throws拋出的是異常類,可以拋出多個,用逗號隔開。
throw拋出的是異常對象。
異常處理的捕捉形式
這是可以對異常進行針對性處理的方式。
//具體格式是:
try
{
//需要被檢測異常的代碼。
}
catch(異常類 變數)//該變數用於接收發生的異常對象
{
//處理異常的代碼。
}
finally
{
//一定會被執行的代碼。
}
異常的註意事項
- 子類在覆蓋父類方法時,父類的方法如果拋出了異常, 那麼子類的方法只能拋出父類的異常或者該異常的子類。
- 如果父類拋出多個異常,那麼子類只能拋出父類異常的子集。
簡單說:子類覆蓋父類只能拋出父類的異常或者子類或者子集。
註意:如果父類的方法沒有拋出異常,那麼子類覆蓋時絕對不能拋,就只能try .
包
對類文件進行分類管理;給類提供多層命名(名稱)空間; 寫在程式文件的第一行;
- 類名的全稱是:
包名.類名
包也是一種封裝形式;
//package
//protected必須是成為其子類,才能繼承
//import導入指定包中的類用的
- 導包的原則:用到哪個導哪個;
- jar包:Java的壓縮包;
多線程
- 進程和線程
- 進程:正在進行中的程式;
- 線程:就是進程中一個負責程式執行的控制單元(執行路徑) 一個進程中可以多執行路徑,稱之為多線程;
- 一個進程中至少有一個線程;
- 開啟多個線程是為了同時運行多個代碼;
- 每一個線程都有運行的內容,這個內容稱為這個線程執行的任務;
- 多線程好處:解決了多部分同時運行的問題;
- 多線程弊端:線程太多回到的效率降低;
其實應用的執行都是CPU進行著高速的轉換,這個切換是隨機的;
- 如:JVM啟動時就啟動了多個線程,至少有兩個線程分析的出來;
- 執行main函數的線程; 該線程的代碼都在主函數中;
- 負責垃圾回收的線程; 垃圾回收器中;
finalize清除垃圾,重新分配資源
System.gc()啟動垃圾回收器
//主線程運行示例:
class Demo extends Thread
{
private String name;
Demo (String name)
{
this.name=name;
}
public void run()
{
for(int x=0;x<10;x++)
{
for(int y=-99999;y<999999999;y++){}
System.out.println(name+“....x”+x);
}
}
}
class Threaddemo
{
Demo d1=new Demo(“旺財”);
Demo d2=new Demo(“xiaoqiang”);
d1.start();
d2.start(); //開啟線程,運行run方法
}
如何創建一個線程呢?
* 創建線程方式一:繼承Thread類。
步驟:
- 定義一個類繼承Thread類。
- 覆蓋Thread類中的run方法。
- 直接創建Thread的子類對象創建線程。
- 調用start方法開啟線程並調用線程的任務run方法執行。
可以通過Thread的getName獲取線程的名稱 Thread-編號(從0開始)
- 主線程的名字就是main
- 創建線程的目的是為了開啟一條執行路徑,去運行指定的代碼和其他代碼實現同時運行。
- 而運行的指定代碼就是這個執行路徑的任務。
- jvm創建的主線程的任務都定義在了主函數中。
- 而自定義的線程它的任務在哪兒呢?
Thread類用於描述線程,線程是需要任務的。所以Thread類也對任務的描述。
這個任務就通過Thread類中的run方法來體現。也就是說,run方法就是封裝自定義線程運行任務的函數。
run方法中定義就是線程要運行的任務代碼。
開啟線程是為了運行指定代碼,所以只有繼承Thread類,並覆寫run方法。
將運行的代碼定義在run方法中即可。
- 通過Thread的getName()方法來獲取該線程的名字
currentThread()正在運行的線程名字 - 調用start和run的區別
被創建
start() 運行
sleep(time)
wait() 凍結
Sleep(time)時間到
notify()
Run()方法結束,線程的任務結束
Stop()
消亡
- 線程的狀態:
- CPU的執行資格:等待執行;
- CPU的執行權:正在執行
- 臨時阻塞狀態:具備執行資格,沒有執行權,只有正在運行的有執行權;
- 只有一個有執行權;
- 創建線程的第二種方式:實現Runnable介面
- 定義類實現Runnable介面。
- 覆蓋介面中的run方法,將線程的任務代碼封裝到run方法中。
- 通過Thread類創建線程對象,並將Runnable介面的子類對象作為Thread類的構造函數的參數進行傳遞。
- 為什麼?
- 因為線程的任務都封裝在Runnable介面子類對象的run方法中。 所以要線上程對象創建時就必須明確要運行的任務。
- 調用線程對象的start方法開啟線程。
- 實現Runnable介面的好處:
- 將線程的任務從線程的子類中分離出來,進行了單獨的封裝。 按照面向對象的思想將任務的封裝成對象。
- 避免了java單繼承的局限性。
所以,創建線程的第二種方式較為常用。
- 線程安全問題解決思路
將多條操作共用數據的線程代碼封裝起來,當有線程執行代碼時,其他不能參與運算;
同步代碼塊 :synchronized(對象)
{
需要被同步的代碼;
}
- 同步的好處:
解決了線程的安全問題;
- 同步的弊端:
相對降低了效率,因為同步外的線程都會判斷同步鎖
- 線程間通信(多個線程在處理同一資源,但是任務卻不同):
- 這些方法存在於同步中;
- 使用這些方法時必須要標識所屬的同步的鎖;
- 等待、喚醒機制:
- 涉及的方法:
1. wait() //讓線程處於凍結狀態,被wait的線程會被存儲線程池
2. notify() //喚醒線程池中任意一個線程
3. notifyAll() //喚醒線程池中所有線程
這些方法都必須定義在同步中,因為這些方法是用於操作線程狀態的方法
- 必須要明確到底操作的是哪個鎖上的線程
- 為什麼這些方法定義在object類中?
因為這些方法是監視器的方法,監視器本身就是一個鎖
- Wait和sleep的方法:
- wait可以指定時間也可以不指定
Sleep必須指定時間- 在同步中,對CPU的執行權和鎖的處理不同
Wait:釋放執行權,釋放鎖
Sleep:釋放執行權,不釋放鎖
- 停止線程
- stop方法(已過時)
- Run方法結束
- Interrupt()將線程從凍結狀態恢復到運行狀態,讓線程具備CPU的執行資格
- 守護線程setDaemon(true)將該線程標記為守護線程,當所有線程被守護時,CPU退出
集合類
- 集合類的由來
對象用於封裝特有數據,對象多了需要存儲.
如果對象的個數不確定,就使用集合容器進行存儲。
- 集合特點
- 用於存儲對象的容器。
- 集合的長度是可變的。
- 集合中不可以存儲基本數據類型值。
集合容器因為內部的數據結構不同,有多種具體容器。
Collection介面
- 不斷的向上抽取,就形成了集合框架,框架的頂層Collection介面:
- Collection
- 添加
boolean add(Object obj):
boolean addAll(Collection coll):
- 刪除
boolean remove(object obj):
boolean removeAll(Collection coll);
void clear();
- 判斷
boolean contains(object obj):
boolean containsAll(Colllection coll);
boolean isEmpty():判斷集合中是否有元素。
- 獲取
int size():
Iterator iterator():
- 取出元素的方式:迭代器。 該對象必須依賴於具體容器,因為每一個容器的數據結構都不同。
- 所以該迭代器對象是在容器中進行內部實現的。
- 對於使用容器者而言,具體的實現不重要,只要通過容器獲取到該實現的迭代器的對象即可,
- 也就是iterator方法。
- Iterator介面就是對所有的Collection容器進行元素取出的公共介面。
- 好比就是抓娃娃游戲機中的夾子!
- 其他
boolean retainAll(Collection coll) //取交集。
Object[] toArray() //將集合轉成數組。
- Collection方法
- List:有序(存入和取出的順序一致),元素都有索引(角標),元素可以重覆。
- Set:元素不能重覆,無序。
- List特有的常見方法:有一個共性特點就是都可以操作角標。
- 添加
void add(index,element)
void add(index,collection)
- 刪除
Object remove(index):
- 修改
Object set(index,element)
- 獲取
Object get(index);
int indexOf(object);
int lastIndexOf(object);
List subList(from,to);
//list集合是可以完成對元素的增刪改查.
List:
- Vector:內部是數組數據結構,是同步的。增刪,查詢都很慢!
- ArrayList:內部是數組數據結構,是不同步的。替代了Vector。查詢的速度快。
- LinkedList:內部是鏈表數據結構,是不同步的。增刪元素的速度很快。
LinkedList:
addFirst();
addLast():
//jdk1.6
offerFirst();
offetLast();
getFirst();.//獲取但不移除,如果鏈表為空,拋出NoSuchElementException.
getLast();
//jdk1.6
peekFirst();//獲取但不移除,如果鏈表為空,返回null.
peekLast():
removeFirst();//獲取並移除,如果鏈表為空,拋出NoSuchElementException.
removeLast();
//jdk1.6
pollFirst();//獲取並移除,如果鏈表為空,返回null.
pollLast();
- Set:元素不可以重覆,是無序。
Set介面中的方法和Collection一致。
- HashSet: 內部數據結構是哈希表 ,是不同步的。
- 如何保證該集合的元素唯一性呢?
- 是通過對象的hashCode和equals方法來完成對象唯一性的。
- 如果對象的hashCode值不同,那麼不用判斷equals方法,就直接存儲到哈希表中。
- 如果對象的hashCode值相同,那麼要再次判斷對象的equals方法是否為true。
- 如果為true,視為相同元素,不存。如果為false,那麼視為不同元素,就進行存儲。
記住:如果元素要存儲到HashSet集合中,必須覆蓋hashCode方法和equals方法。
-
TreeSet:可以對Set集合中的元素進行排序。是不同步的。
-
判斷元素唯一性的方式
就是根據比較方法的返回結果是否是0,是0,就是相同元素,不存。
- TreeSet對元素進行排序的方式一:
讓元素自身具備比較功能,就需要實現Comparable介面。覆蓋compareTo方法。
- 如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。怎麼辦?
- 可以使用TreeSet集合第二種排序方式二:
讓集合自身具備比較功能,定義一個類實現Comparator介面,覆蓋compare方法。將該類對象作為參數傳遞給TreeSet集合的構造函數。
if(this.hashCode()== obj.hashCode() && this.equals(obj))
- 哈希表確定元素是否相同
- 判斷的是兩個元素的哈希值是否相同。 如果相同,在判斷兩個對象的內容是否相同。
- 判斷哈希值相同,其實判斷的是對象的hashCode的方法。判斷內容相同,用的是equals方法。
註意:如果哈希值不同,是不需要判斷equals。
集合的一些技巧
- 需要唯一嗎?
需要:Set- 需要制定順序:
需要: TreeSet
不需要:HashSet- 但是想要一個和存儲一致的順序(有序):
LinkedHashSet
不需要:List- 需要頻繁增刪嗎?
需要:LinkedList
不需要:ArrayList- 如何記錄每一個容器的結構和所屬體系呢?
- List
|--ArrayList
|--LinkedList- Set
|--HashSet
|--TreeSet- 尾碼名就是該集合所屬的體系。
首碼名就是該集合的數據結構。- 看到array:就要想到數組,就要想到查詢快,有角標.
- 看到link:就要想到鏈表,就要想到增刪快,就要想要 add get remove+frist last的方法
- 看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆蓋hashcode方法和equals方法。
- 看到tree:就要想到二叉樹,就要想要排序,就要想到兩個介面Comparable,Comparator 。
- 通常這些常用的集合容器都是不同步的。
Map
- Map:一次添加一對元素。Collection 一次添加一個元素。
- Map也稱為雙列集合,Collection集合稱為單列集合。
- 其實map集合中存儲的就是鍵值對。
- map集合中必須保證鍵的唯一性。
- 常用方法
- 添加
value put(key,value)
//返回前一個和key關聯的值,如果沒有返回null.
- 刪除
void clear()
//清空map集合。
value remove(key)
//根據指定的key翻出這個鍵值對。
- 判斷
boolean containsKey(key)
boolean containsValue(value)
boolean isEmpty()
- 獲取
value get(key)
//通過鍵獲取值,如果沒有該鍵返回null。
//當然可以通過返回null,來判斷是否包含指定鍵。
int size()
//獲取鍵值對的個數。
- Map介面常用的子類
- Hashtable :內部結構是哈希表,是同步的。不允許null作為鍵,null作為值。
- Properties:用來存儲鍵值對型的配置文件的信息,可以和IO技術相結合。
- HashMap : 內部結構是哈希表,不是同步的。允許null作為鍵,null作為值。
- TreeMap : 內部結構是二叉樹,不是同步的。可以對Map集合中的鍵進行排序。
泛型
jdk1.5出現的安全機制。
泛型技術是給編譯器使用的技術,用於編譯時期。確保了類型的安全。
運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱為泛型的擦除。
為什麼擦除呢?
因為為了相容運行的類載入器。
- 好處
- 將運行時期的問題ClassCastException轉到了編譯時期。
- 避免了強制轉換的麻煩。
- <>
- 當操作的引用數據類型不確定的時候。就使用<>。將要操作的引用數據類型傳入即可.
- 其實<>就是一個用於接收具體引用數據類型的參數範圍。
- 在程式中,只要用到了帶有<>的類或者介面,就要明確傳入的具體引用數據類型 。
- 泛型的補償
在運行時,通過獲取元素的類型進行轉換動作。不用使用者再強制轉換了。
- 泛型的通配符:
? 未知類型
- 泛型的限定:
- extends E: 接收E類型或者E的子類型對象。上限
一般存儲對象的時候用。比如 添加元素 addAll.- super E: 接收E類型或者E的父類型對象。 下限。
一般取出對象的時候用。比如比較器。