Java基礎知識干貨2

Java基礎知識干貨1傳送門->http://www.lxweimin.com/p/78fdfacf1868

內部類

  1. 常規內部類


    常規內部類的組成
  • 只有內部類可以聲明為 private,常規類只可以被聲明為 public和protected
  • 內部類可以訪問外部類私有域的成員變量
  • 內部類不允許有靜態方法和變量
  1. 靜態內部類
  • 靜態內部類不允許訪問外圍類對象
  • 靜態內部類可以有靜態方法和靜態變量
  • 聲明在接口中的內部類自動成為static和public類

Java異常層次

Java異常層次

超類和子類的異常

  • 如果在子類中覆蓋了超類的一個方法,子類方法中聲明的受查異常不能比超類中聲明的異常更通用(應該拋出更具體的異常)
  • 如果超類方法沒有拋出任何受查異常,子類也不能拋出

建議同時捕獲多個異常

catch(FileNotFoundException | UnkownHostException e) {

}

可以在catch字句中再拋出一個異常,這樣做的目的是改變異常的類型

下列代碼表明執行sevlet的代碼可能不想知道發生錯誤的具體類型,但是希望明確知道servlet是否有問題。

try {
  //access the database
}
catch(SQLException e) {
  //這樣做可以讓用戶拋出多個異常,而又不會丟失原始異常的細節
  Throwable se = new ServletException("db error");
  se.init(e);
  throw se;
}

泛型的聲明及調用

  • 泛型類: 位于類名的后面
public class Pair<T> {
  ...
}
  • 泛型方法: 位于修飾符的后面,返回類型的前面
public static <T> T getMiddle(T...a) {
  ...
}

在調用的泛型方法的時候,可以指定泛型具體參數,也可以不指定泛型具體參數.

  • 在不指定泛型的情況下,泛型變量的類型為該方法的幾種類型的同一個父類的最小級,直到Object.
//Integer和Float最小父類是Number
Number num = Test.getMiddle(1,1.2);
//Integer和String最下父類是Object
Object o = Test.getMiddle(1,"aaa");
  • 在指定泛型的情況下,會發生編譯錯誤
int a = Test.<Integer>getMiddle(1,1.2);

泛型類型變量的限定

下例將T限制為實現了Comparable接口的類。
一個類型變量或通配符可以有多個限定

T extends Comparable & Serializable
public static <T extends Comparable> T min(T[] a) {
  ...
}

運行時類型查詢只適用于原始數據類型

由于類型擦出機制的存在,虛擬機中的對象總有一個特定的非泛型類型,因此所有的類型查詢只產生原始類型

if(a instanceof Pair<String>) //error
if(a instanceof) Pair<T>)     //error

getClass()方法總是返回原始類型

Pair<String> a1 = ...;
Pair<Emploee> a2 = ...;
if(a1.getClass() == a2.getClass())   //true

不能創建參數化類型的數組

Pair<String>[] table = new Pair<String>[10];  //error

因為類型擦除機制使得table能夠存放任意類型的對象,所以必須使用 ArrayList 對象。

泛型類中的靜態方法和靜態變量不可以使用泛型類所聲明的泛型類型參數

public class Singleton<T> {
  private static T singleInstance;         //error
  public static T getSingleInstance() {    //error
    ...
  }
}

因為泛型類中的泛型參數的實例化是在定義對象的時候制定的,而靜態變量和靜態方法不需要對象來調用,所以是錯誤的。

注意擦除后的沖突

public class Pair<T> {
  public boolean equals(T ob) {
    return first.equals(ob);
  }
}

類型擦除后,類中存在兩個 boolean equals(Object o) 方法,引起沖突,補救措施->rename

要想支持擦除的轉化,就需要強行限制一個類或類型變量不能同時成為兩個接口類型的子類,且這兩個接口是同一接口的不同參數化

class Emploee implments Comparable<Emploee> {...}
class Manager extends Emploee implments Comparable<Manager> {...}  //error

隊列(queue)

隊尾添加元素,頭部刪除元素,并且可以查找隊列中元素的個數
規則: 先進先出(FIFO)
實現方式: 循環數組,鏈表
兩種實現方式的比較: 循環數組比鏈表更高效,但容量也有限,刪除(或插入)中間元素時效率很差(因為需要一個一個進行移位操作)

public interface Queue<E> {
  void add(E e);
  E remove();
  int size();
}

Collection接口

在Java類庫中,集合類的基本接口是Collection接口

public interface Collection<E> {
  boolean add(E e);
  int size();
  boolean isEmpty();
  boolean contains(Object e);
  Iterator<E> iterator();
  boolean remove(Object e);
  boolean containsAll(Collection<?> c);
  boolean addAll(Collection<? extends E> c);
  boolean removeAll(Collection<?> c);
  boolean retainAll(Collection<?> c);
  void clear();
  boolean equals(Object o);
  int hasCode();
}

迭代器

Iterator接口包含4個方法

public interface Iterator<E> {
  E next();
  boolean hasNext();
  //刪除上次訪問對象
  void remove();
  default void forEachRemaining(Consumer<? super E> action);
}

通過反復調用next方法,可以逐個訪問集合中的每個元素,如果到達了集合的末尾,next()方法將拋出 NoSuchElementException. 因此需在調next()方法之前調用hasNext();
如果想要查看集合中的所有元素,就請求一個迭代器

Collection<String> collection = new ArrayList<>();
Iterator<String> iterator = collection.iterator();
while(iterator.hasNext()) {
  ...
}

ListIterator可以實現雙向遍歷

集合框架中的接口

集合框架中的接口

List是一個有序集合,元素會增加到容器的特定位置。
List的訪問方式:

  1. 使用迭代器訪問(必須順序訪問)
  2. 使用整數索引來訪問

Java具體集合

Java具體集合

鏈表

鏈表

Vector: Vector之所以沒在圖中體現是因為這是Java集合中的遺留類,但是Vector所有方法都是同步的,我們平時使用單線程應用時,應該優先選擇使用ArrayList.
ArrayList: 使用數組實現,從中間插入/刪除元素很困難,但是按索引訪問效率高。
LinkedList: 按索引訪問效率很差,需要一個一個向下面遍歷知道找到要訪問的元素。

使用ListIterator向LinkedList中添加元素:
Java語言中的所有鏈表都是雙向鏈表,鏈表是一個有序集合,每個對象的位置十分重要,由于迭代器是描述集合中位置的,只有通過迭代器對有序集合添加元素才更有意義,但Iterator接口中沒有add(),其子接口ListIterator中包含add().

ListIterator<String> iter = staff.listIterator();
//add()在迭代器位置之前添加一個元素
iter.next();
iter.add("tom");

如果多次調用add(),將依次把各元素添加至當前迭代器位置之前。

散列集

散列表不在意元素的順序,但其可以實現快速的查找所需要的對象。
在Java中,散列表使用鏈表數組實現,要想要查找表中對象的位置,就要先計算他的散列碼,然后與桶(bucket)的總數取余。

散列表的邏輯結構

桶滿的情況下,需要將新對象逐個與鏈表中的對象進行比較,查看這個對象是否已經存在(Java SE8中,桶滿是會從鏈表變為平衡二叉樹).
如果想控制散列表的運行性能,就要指定一個初始的桶數,通常將桶數設置在預計元素的75%~150%。
當然,并不是總能預先知道需要存的元素的個數,如果散列表太滿,就需要進行再散列(rehashed),即創建一個桶數更多的表,并將所有元素插入到新表中,然后丟棄原來的表,何時丟棄由裝填因子(load factor)決定,默認為0.75

樹集

TreeSet類(使用紅黑樹實現)與散列集十分類似,不過它比散列集有所改進,它是一個有序集合,可以以任意的順序將元素插入到集合中,在對集合進行遍歷時,每個值將自動的按照排序后的順序出現。

隊列

雙端隊列: Java SE6引入了Deque接口,并由ArrayDeque和LinkedList類實現,這兩個類都提供雙端隊列,雙端隊列即可以在頭部和尾部同時添加或刪除元素。

優先級隊列(priority queue): 可以按照任意的順序插入,卻總是按照排序的順序進行檢索(使用堆(heap)來實現)

映射

Java類庫為映射提供了兩個通用的實現:HashMap和TreeMap,這兩個類都實現了Map接口

  • HashMap: 使用散列表實現,可以快速的查找鍵/值
  • LinkedHashMap: 迭代遍歷時,按插入次序遍歷,迭代時也可以按照LRU(最近最少使用算法,長時間未使用的在前)次序迭代,迭代訪問比HashMap快,因為它使用鏈表維護內部次序。
  • WeakHashMap: 允許釋放映射所指的對象
  • ConcurrentHashMap: 線程安全的Map
  • IdentityHashMap: 使用==代替equals()對鍵進行比較

線程安全的容器(簡記為“喂,SHE”)

Vector: 效率低,但是比ArrayList多了個同步化機制,在web應用中特別是前臺頁面,往往效率優先,故不常使用。
Stack:
hashtable: 比hashMap多了個線程安全
enumeration:

枚舉類型(不能被繼承)

枚舉是一組含有有限個具名值的集合,使用關鍵字enum聲明枚舉類型

enum shrubbery {GROUND,CRAWLING,HANGING}

除了不能繼承一個enum外,基本上可以將enum看做一個常規的類,甚至可以有main()方法

public enum EnumTest{
  WOLF("This is a wolf"),
  TIGET("This is a tiger"),
  SNAKE("This is a snake");

  //必須在實例之后定義方法和變量
  private String desc;
  public String getDesc() {
    return desc;
  }

  //構造函數聲明為private
  private EnumTest(String desc) {
    this.desc = desc;
  }
}

遍歷

for(EnumTest item : EnumTest.values()) {
  System.out.println(item + item.getDesc());
}

枚舉中的values():

我們原本聲明的枚舉類Explore

enum Explore{
  HERE,THERE
}

經過編譯后變為

final class Explore extends java.lang.Enun{
  public static final Explore HERE;
  public static final Explore THERE;
  //自動添加
  public static final Explorep[] values();
  //自動添加
  public static final Explore valueOf(java.lang.String);
  static{};
}

任務的定義

線程可以驅動任務,而任務的實現必須實現Runnable接口并編寫run()方法。
任務的run()方法通常會有某種形式的循環,使得任務一直運行下去直到不再需要。
在run()方法中對靜態方法Thread.yield()的調用是對線程調度器的一種建議,建議它切換至另一個任務執行。

Thread類

將Runnable對象轉變為工作任務的傳統方式是把它提交給一個Thread構造器。

public class BasicThread {
  public static void main(String[] args) {
    Thread t = new Thread(new LiftOff());
    t.start();
  }
}

使用Executor

Executor在客戶端和任務執行之間提供了一個間接層,與客戶端直接執行任務不同,這個中介對象將執行任務。Executor允許你管理異步任務的執行,而無需顯式的管理線程的聲明周期

ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new LiftOff());
//防止新任務被提交給exec
exec.shutdown();
  • newCachedThreadPool(): 運行時為每個線程分派一個線程。
  • newFixedThreadPool(): 一次性執行代價高昂的線程分配。
  • newSingleThreadExecutor(): 一次只運行一個線程,多個需要排隊.

從任務中產生返回值

Runnable是執行工作中的獨立任務,不產生任何返回值,如果希望任務完成時能夠返回一個值,那么可以實現Callable接口。

public TaskWithResult implements Callable<String> {
  private int id;
  public TaskWithResult(int id) {
    this.id = id;
  }

  public String call() {
    return "result is " + id;
  }
}

調用

ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> array = new ArrayList<>();
for(int i = 0;i < 5;i++) {
  //必須通過submit()來調用,調用之后返回Future對象
  array.add(exec.submit());
}

for(Future<String> fu : array) {
  //使用get()獲取結果,獲取結果前可以使用isDone()來檢查是否完成
  System.out.println(fu.get());
}

休眠

try {
  TimeUnit.MILLSECONDS.sleep(100);
} catch (InterruptedException e) {
  System.err.println("interrupted");
}

優先級

public void run() {
  //在run()方法開頭設置優先級
  Thread.currentThread().setPriority(10);
}

一般調整優先級時,只是用MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY這三個級別.

后臺線程(daemon)

后臺線程不屬于程序中不可或缺的一部分,當所有的非后臺程序全部執行完畢時,程序也就終止了,同時會殺死進程中的所有后臺線程,反過來說,只要有任何非后臺線程在運行,程序就不會終止。

Thread t = new  Thread(new LiftOff());
t.setDaemon(true);
t.start();

可以通過調用isDaemon()來判斷一個線程是否是一個后臺線程,如果是一個后臺線程,那么它創建的所有后臺線程也都是后臺線程。
通過編寫定制的ThreadFactory()可以定制由Executor創建的線程的屬性.

public class DaemonThreadFactory implements ThreadFactory {
  public Thread newThread(Runnable r) {
    Thread t = new Thread(r);
    t.setDaemon(true);
    return t;
  }
}

調用

ExecutorService exec = Exectors.newCachedThreadPool(new DaemonThreadFactory());

另外,后臺線程在不執行finally語句的情況下就會終止run()方法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容