黑馬程序員_day019_泛型

----------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

一、泛型:jdk1.5版本,出現的技術。是一個安全機制。 

泛型技術的由來:

集合中可以存儲任意類型對象,但是在取出時,如果要使用具體對象的特有方法時,需要進行向下轉型,如果存儲的對象類型不一致,在轉型過程中就會出現ClassCastException異常。

這樣就給程序帶來了不安全性。

jdk1.5以後就有了解決方案。就是泛型技術。

解決方案就是,在存儲元素時,就不允許存儲不同類型的元素。

存儲了就編譯失敗。 所以就需要在存儲元素時,在容器上明確具體的元素類型。這其實和數組定義很像。

好處:

1,將運行時期的ClassCastException異常轉移到了編譯事情,進行檢 查,並以編譯失敗來體現。 這樣有利於程序員儘早解決問題。 

2,避免了向下轉型(強轉)的麻煩

什麼時候寫泛型呢?

先簡單理解:只要在使用類或者接口時,該類或者接口在api文當描述時都帶着<>就需要在使用時,定義泛型。

其實,泛型無非就是通過<>定義了一個形式參數。專門用於接收具體的引用類型。在使用時,一定要傳遞對應的實際參數類型。

集合中泛型的應用特別多見。

泛型的擦除:

泛型技術是用於編譯時期的技術,編譯器會按照<>中的指定類型對元素進行檢查,檢查不匹配,就編譯失敗,匹配,就編譯通過,通過後,生產的class文件中是沒有泛型的。這就成爲泛型的擦除。

泛型的補償:

運行時,可以根據具體的元素對象獲取其具體的類型。並用該類型對元素進行自動轉換。

二、泛型對對於程序的設計也有一定優化動作。例如下:

定義一個工具類對worker對象也可以對Student對象進行操作,設置和獲取。甚至於任意對象。

之所以定義Object類型的對象是因爲不能確定要操作什麼類型的具體對象。

弊端是要使用對象的特有方法需要向下轉型,並且問題發生,會出現的運行時期,而不是編譯時期,不能解決。 

class Tool{

private Object obj;

public void setObject(Object obj){

this.obj = obj;

}

public Object getObject(){

return obj;

}

主函數中對該工具類調用時

// Tool t = new Tool();

// t.setObject(new Student2());編譯時竟沒有報錯而運行時報錯

// Worker w = (Worker)t.getObject();需要類型強轉,否則編譯失敗

怎麼解決呢?泛型來了

class Util<QQ>{

private QQ obj;

public void setObject(QQ obj){

this.obj = obj;

}

public QQ getObject(){

return obj;

}

}

主函數中對泛型工具類的調用:

Util<Worker> u = new Util<Worker>();

// u.setObject(new Student2());//類型不匹配,直接編譯失敗。

// Worker w = u.getObject();//不需要向下轉型了。

三、泛型類的誕生和使用條件

當一個類要操作的引用數據類型不確定的時候,可以將該類型定義一個形參。用到的這類時,有使用者來通過傳遞類型參數的形式,來確定要操作的具體的對象類型。 

意味着在定義這個類時(自定義類),需要在類上定義形參。用於接收具體的類型實參。

這就是將泛型定義在類上。這就是泛型類。

什麼時候使用泛型類呢?只要類中操作的引用數據類型不確定的時候,就可以定義泛型類。 

有了泛型類,省去了曾經的強轉和類型轉換異常的麻煩。 

泛型定義在方法上

這是一個泛型類。class Tool2<W>

class Tool2<W>{

public void show(W w){

System.out.println("show:"+w.toString());

}

public  void myprint(W w){

System.out.println("myprint:"+a.toString());

}

主函數中創建該泛型類對象,並調用該泛型類中的方法

Tool2<String> t = new Tool2<String>();

t.show("abc");

t.show(new Integer(4));這兒會編譯失敗。因爲只能是String

t.myprint("haha");

t.myprint(new Integer(4));同上。

}而我對這個myprint方法,想要操作的類型是不確定的,但是不一定和調用該方法的對象指定的類型一致。

*這時 可以將泛型定義在方法上。 

public <A> void myprint(A a){

System.out.println("myprint:"+a.toString());

}此時,上面的t.myprint(new Integer(4));就不會再編譯報錯了

另外,靜態方法不能訪問類上定義的泛型,如果需要泛型,該泛型只能定義在方法上。即如果void前沒有 <Y> ,會編譯失敗的。

public   static <Y> void method(Y w){

System.out.println("method:"+w);

}

泛型接口的應用

interface Inter<V>{

public abstract void show(V v);

}這是一個泛型接口

1//實現接口時,明確具體的類型。 

class InterImpl implements Inter<String>{

public void show(String s){

System.out.println("show :"+s);

}

}

2/實現接口時,也不明確具體類型。 

class InterImpl2<C> implements Inter<C>{

public void show(C c){

System.out.println("show :"+c);

}

}

主函數中分別對1  和  的情況

new InterImpl().show("abc");實現接口時明確了String

new InterImpl2<Integer>().show(new Integer(5));

實現接口時沒有明確具體類型。

四、泛型的通配符:?

ArrayList<Integer> al3 = new ArrayList<Object>();//錯誤

ArrayList<Object> al4 = new ArrayList<Integer>();//錯誤。

簡單的定義方式就是保證兩邊的類型一致。但是也有其他情況。

所以通配符應運而生了。

當操作的不同容器中的類型都不確定的時候,而且使用的都是元素從Object類中繼承的方法。

這時泛型就用通配符?來表示即可。

應用示例:創建一個用於迭代集合的功能。

public static void printColl(Collection<?> coll){

Iterator<?> it = coll.iterator();

while(it.hasNext()){

System.out.println(it.next().toString());

}

}

Collection<?> coll表示無論是ArrayList 集合還是HashSet集合,也無論他們的ArrayList<String>還是ArryList<Integer>都可以使用該功能。

五、泛型的限定

明確具體類型代表一個類型。

明確?代表所有類型。?相當於? extends Object

能不能對操作的類型限制在一個範圍之內呢?

比如:定義一個功能,只操作Person類型或者Person的子類型。 

這時可以用 ? extends E:接收E類型或者E的子類型。這就是上限。 

下限:? super E: 接收E類型或者E的父類型。

六、foreach語句:增強for循環

jdk1.5以後 出現的新方式。這種升級就是簡化書寫。

Collection有了一個父接口,Iterable

該接口封裝了iterator方法,同時提供了一個新的語句。foreach語句。

格式:

for(變量 Collection集合or數組)

{

 }

Iterator it = al.iterator();

while(it.hasNext()){

System.out.println(it.next());

}foreach語句如下:

for(String s : al){

System.out.println(s);

}

int[] arr = {7,23,1,67,90,8};

for(int i : arr){//只爲遍歷元素,無法操作角標。 

System.out.println("i="+i);

}

1foreach循環簡化了迭代器。迭代器還用嗎?用,因爲迭代過程中還可以remove().

一般只對基本遍歷簡化使用。

2,和傳統for循環有什麼區別呢?

foreach循環特點:必須明確被遍歷的目標。沒有目標沒用。目標只能是數組或者Collection集合。

如果要對數組的中的元素進行特定操作時,建議傳統for循環,通過角標完成。 

七、TreeSet排序的兩種方式

 TreeSet:可以給其中的元素進行指定方式排序,使用的自然順序。

自然順序:就是元素自身的具備的比較性實現了Comparable接口的compareTo方法

TreeSet排序的方式一:讓元素自身具備比較性,需要實現Comparable接口,覆蓋compareTo方法。這種比較方式成爲自然順序排序。

如果元素自身不具備比較性或者具備的比較性(自然順序)不是所需要的。這時只能用第二種方式 。

TreeSet排序的方式二:讓容器自身具備比較性。容器一初始化就具備了比較功能。

因爲容器時在對象構造時完成的。通過查閱,有一個構造方法TreeSet(Comparator).

在容器初始化時可以指定一個比較器。 

需要實現Comparator接口,覆蓋compare方法即可。

所以這種方式成爲比較器排序。 

TreeSet練習

練習:對字符串長度進行排序。

 思路:

1,對字符串對象進行排序是很簡單的。

因爲字符串對象本身就具備着自然順序(字典順序),因爲它實現Comparable接口。覆蓋了compareTo方法。 

2, 要求是對字符串進行長度的排序。

發現字符串具備的自然順序不是所需要的。這時就只能使用比較 器的方式。

 3, 需要定義個比較器。 

TreeSet ts = new TreeSet(new ComparatorByLength());

ts.add("hahah");

ts.add("nba");

ts.add("aaaa");

ts.add("zzz");

ts.add("abc");

Iterator it = ts.iterator();

while(it.hasNext()){

System.out.println(it.next());

}

}

定義比較器ComparatorByLength

public class ComparatorByLength implements Comparator {

public int compare(Object o1, Object o2) {

String s1 = (String)o1;

String s2 = (String)o2;

int temp = s1.length() - s2.length();

return temp==0?s1.compareTo(s2):temp;

}

}

八、有序集合LinkedHashSet

Set集合中,其實有一個有序集合。就是HashSet的子類

順便提一下LinkedList中特有的操作方法:

 * addFirst()

 * addLast();

 * jdk1.6以後,變成。

 * offerFirst();

 * offerLast();

 * 

 * getFirst():獲取元素,但不刪除,如果列表爲空,拋出異常NoSuchElementException

 * getLast();

 * jdk1.6以後。

 * peekFirst();獲取元素,但不刪除,如果列表爲空,返回null

 * peekLast();

 * 

 * removeFirst():獲取元素,並會刪除,如果列表爲空,拋出異常NoSuchElementException

 * removeLast();

 * jdk1.6以後。

 * pollFirst():獲取元素,並會刪除,如果列表爲空,返回null

 * pollLast();

九、集合的技巧掌握

明確具體集合對象名稱的後綴:

如果後綴是List,都所屬於List體系。通常都是非同步的。

如果後綴是Set,都屬於Set體系,通常也是非同步的。

這些體系中的其他子類對象,後綴不是所屬接口名的,一般都 是同步的。比如Vector.

這在常用子類對象中通用。 

明確數據結構:

對於jdk1.2版本的子類對象。

後綴名是所屬的體系。

前綴名是就是數據結構的名稱。

比如:

ArrayList: 看到Array,就要明確是數組結構。查詢快。

LinkedList:看到Link,就要明確鏈表結構,就要想到 add get  remove first last結合的方法.增刪快。

HashSet:看到hash,就要明確是哈希表。查詢巨快,而且唯 一性。

就要想到元素必須覆蓋 hashCode方法和equals方法。 

TreeSet:看到Tree,就要明確是二叉樹,可以對元素排序。

就要想到兩種排序方式:

自然順序:Comparable接口,覆蓋compareTo(一個參 數 )java.lang

比較器:Comparator接口,覆蓋compare(兩個參);java.util

判斷元素唯一性的依據就是比較方法的返回結果return 0;

十、編程題訓練

對一個字符串數組(其中包含着重複的元素)進行長度由小到大的排序。

想要按照字符串的長度排序。

說明字符串對象所具備的自然順序不是所需要的。

只好使用另一種對象比較的方式。就是比較器。 

在排序的時候傳入一個指定的比較器進來。 按照指定的方式進行比較排序。

String[]arr= {"abcd","zz","hahaah","xixix","qq","mm","abcd","hahaah"};

sortStringArray2(arr,new ComparatorByStringLen());

String str = toString(arr);

System.out.println(str);

傳進字符串數組,並對其排序。

public static void sortStringArray2(String[] arr,Comparator<String> comp){

for (int i = 0; i < arr.length-1; i++) {

for (int j = i+1; j < arr.length; j++) {

if(comp.compare(arr[i], arr[j])>0){

swap(arr,i,j);

}

}

}

將字符串數組通過字符串緩存區轉成字符串並返回

private static String toString(String[] arr) {

StringBuilder sb = new StringBuilder();

sb.append("[");

for (int i = 0; i < arr.length; i++) {

if(i!=arr.length-1)

sb.append(arr[i]+", ");

else

sb.append(arr[i]+"]");

}

return sb.toString();

}

創建比較器ComparatorByStringLen()

public class ComparatorByStringLen implements Comparator<String> {

public int compare(String o1, String o2) {

int temp = o1.length() - o2.length();

return temp==0?o1.compareTo(o2):temp;

}

}

----------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章