----------- 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 和 2 的情況
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);
}
1,foreach循環簡化了迭代器。迭代器還用嗎?用,因爲迭代過程中還可以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學習型技術博客、期待與您交流! ------------