泛型(Generic type 或者 generics)是對(duì) java 語言的類型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類型進(jìn)行參數(shù)化的類。可以把類型參數(shù)看作是使用參數(shù)化類型時(shí)指定的類型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的值的占位符一樣。
泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。
在Java SE 1.5之前,沒有泛型的情況的下,通過對(duì)類型Object的引用來實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點(diǎn)是要做顯式的強(qiáng)制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對(duì)實(shí)際參數(shù)類型可以預(yù)知的情況下進(jìn)行的。對(duì)于強(qiáng)制類型轉(zhuǎn)換錯(cuò)誤的情況,編譯器可能不提示錯(cuò)誤,在運(yùn)行的時(shí)候才出現(xiàn)異常,這是一個(gè)安全隱患。
泛型的好處是在編譯的時(shí)候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的,以提高代碼的重用率。
可以在集合框架(Collection framework)中看到泛型的動(dòng)機(jī)。例如,Map 類允許您向一個(gè) Map添加任意類的對(duì)象,即使最常見的情況是在給定映射(map)中保存某個(gè)特定類型(比如 String)的對(duì)象。
因?yàn)?Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為期望的類型,如下面的代碼所示:
Map m = new HashMap(); m.put("key", "blarg"); String s = (String) m.get("key");要讓程序通過編譯,必須將 get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為 String,并且希望結(jié)果真的是一個(gè) String。但是有可能某人已經(jīng)在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會(huì)拋出 ClassCastException。
理想情況下,您可能會(huì)得出這樣一個(gè)觀點(diǎn),即 m 是一個(gè) Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強(qiáng)制類型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類型檢查層,該檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中。這就是泛型所做的工作。
package cn.itcast_01;import java.util.ArrayList;import java.util.Iterator;/* * ArrayList存儲(chǔ)字符串并遍歷 * * 我們按照正常的寫法來寫這個(gè)程序, 結(jié)果確出錯(cuò)了。 * 為什么呢? * 因?yàn)槲覀冮_始存儲(chǔ)的時(shí)候,存儲(chǔ)了String和Integer兩種類型的數(shù)據(jù)。 * 而在遍歷的時(shí)候,我們把它們都當(dāng)作String類型處理的,做了轉(zhuǎn)換,所以就報(bào)錯(cuò)了。 * 但是呢,它在編譯期間卻沒有告訴我們。 * 所以,我就覺得這個(gè)設(shè)計(jì)的不好。 * 回想一下,我們的數(shù)組 * String[] strArray = new String[3]; * strArray[0] = "hello"; * strArray[1] = "world"; * strArray[2] = 10; * 集合也模仿著數(shù)組的這種做法,在創(chuàng)建對(duì)象的時(shí)候明確元素的數(shù)據(jù)類型。這樣就不會(huì)在有問題了。 * 而這種技術(shù)被稱為:泛型。 * * 泛型:是一種把類型明確的工作推遲到創(chuàng)建對(duì)象或者調(diào)用方法的時(shí)候才去明確的特殊的類型。參數(shù)化類型,把類型當(dāng)作參數(shù)一樣的傳遞。 * 格式: * <數(shù)據(jù)類型> * 此處的數(shù)據(jù)類型只能是引用類型。 * 好處: * A:把運(yùn)行時(shí)期的問題提前到了編譯期間 * B:避免了強(qiáng)制類型轉(zhuǎn)換 * C:優(yōu)化了程序設(shè)計(jì),解決了黃色警告線 */public class GenericDemo { public static void main(String[] args) { // 創(chuàng)建 ArrayList<String> array = new ArrayList<String>(); // 添加元素 array.add("hello"); array.add("world"); array.add("java"); // array.add(new Integer(100)); //array.add(10); // JDK5以后的自動(dòng)裝箱 // 等價(jià)于:array.add(Integer.valueOf(10)); // 遍歷 Iterator<String> it = array.iterator(); while (it.hasNext()) { // ClassCastException // String s = (String) it.next(); String s = it.next(); System.out.PRintln(s); } // 看下面這個(gè)代碼 // String[] strArray = new String[3]; // strArray[0] = "hello"; // strArray[1] = "world"; // strArray[2] = 10; }}Java 語言中引入泛型是一個(gè)較大的功能增強(qiáng)。不僅語言、類型系統(tǒng)和編譯器有了較大的變化,以支持泛型,而且類庫也進(jìn)行了大翻修,所以許多重要的類,比如集合框架,都已經(jīng)成為泛型化的了。這帶來了很多好處:
泛型的主要目標(biāo)是提高 Java 程序的類型安全。通過知道使用泛型定義的變量的類型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類型假設(shè)。沒有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話,還存在于代碼注釋中)。
Java 程序中的一種流行技術(shù)是定義這樣的集合,即它的元素或鍵是公共類型的,比如“String 列表”或者“String 到 String 的映射”。通過在變量聲明中捕獲這一附加的類型信息,泛型允許編譯器實(shí)施這些附加的類型約束。類型錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕獲了,而不是在運(yùn)行時(shí)當(dāng)作 ClassCastException 展示出來。將類型檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于您更容易找到錯(cuò)誤,并可提高程序的可靠性。
泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換。這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。 盡管減少強(qiáng)制類型轉(zhuǎn)換可以降低使用泛型類的代碼的羅嗦程度,但是聲明泛型變量會(huì)帶來相應(yīng)的羅嗦。
3、優(yōu)化了程序設(shè)計(jì),解決了黃色警告線
泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入。但是,編譯器編譯帶類型說明的集合時(shí)會(huì)去除掉“類型”信息,目的就是使程序運(yùn)行效率不受影響。因此,對(duì)于參數(shù)化的泛型類型,getClass()方法的返回值和原始類型完全一樣。
package com.itheima.day2;import java.util.ArrayList;public class GenericTest { public static void main(String[] args) { ArrayList<String> collection1 = new ArrayList<String>(); ArrayList collection2 = new ArrayList(); System. out.println(collection1.getClass() == collection2.getClass()); //結(jié)果:true }}由于編譯生成的字節(jié)碼會(huì)去掉泛型的類型信息,只要能跳過編譯器,就可以往某個(gè)泛型集合中加入其它類型的數(shù)據(jù),例如,用反射得到集合,再調(diào)用其add方法即可。
package com.itheima.day2;import java.util.ArrayList;public class GenericTest { public static void main(String[] args) throws Exception { ArrayList<Integer> collection1 = new ArrayList<Integer>(); collection1.getClass().getMethod( "add",Object.class).invoke(collection1, "abc"); System. out.println(collection1.get(0)); }}ArrayList類定義和ArrayList類引用中涉及如下術(shù)語:
整個(gè)稱為ArrayList<E>泛型類型ArrayList<E>中的E稱為類型變量或類型參數(shù)整個(gè)ArrayList<Integer>稱為參數(shù)化的類型ArrayList<Integer>中的Integer稱為類型參數(shù)的實(shí)例或?qū)嶋H類型參數(shù)ArrayList<Integer>中的<>念著typeofArrayList稱為原始類型參數(shù)化類型與原始類型的兼容性:參數(shù)化類型可以引用一個(gè)原始類型的對(duì)象,編譯報(bào)告警告,例如
Collection<String> c = new Vector();//考慮到對(duì)以前代碼的兼容性,編譯器是可以通過的原始類型可以引用一個(gè)參數(shù)化類型的對(duì)象,編譯報(bào)告警告,例如
Collection c = new Vector<String>();//原來的方法接受一個(gè)集合參數(shù),新的類型也要能傳進(jìn)去參數(shù)化類型不考慮類型參數(shù)的繼承關(guān)系:
Vector<String> v = new Vector<Object>(); //錯(cuò)誤!不寫<Object>沒錯(cuò),寫了就是明知故犯Vector<Object> v = new Vector<String>(); //也錯(cuò)誤!注意:
假設(shè)Vector<String> v = new Vector<Object>();可以的話,那么以后從v中取出的對(duì)象當(dāng)作String用,而v實(shí)際指向的對(duì)象中可以加入任意的類型對(duì)象;
假設(shè)Vector<Object> v = new Vector<String>();可以的話,那么以后可以向v中加入任意的類型對(duì)象,而v實(shí)際指向的集合中只能裝String類型的對(duì)象。
編譯器不允許創(chuàng)建泛型變量的數(shù)組。即在創(chuàng)建數(shù)組實(shí)例時(shí),數(shù)組的元素不能使用參數(shù)化的類型。
例如,下面語句有錯(cuò)誤:
Vector<Integer> vectorList[] = new Vector<Integer>[10];思考題:
下面的代碼會(huì)報(bào)錯(cuò)誤嗎?
Vector v1 = new Vector<String>();Vector<Object> v = v1;答案:編譯的時(shí)候是不會(huì)報(bào)錯(cuò)的,因?yàn)榫幾g器是一行一行按照語法檢查代碼的,因此不會(huì)出錯(cuò)。
把泛型定義在類上,格式:public class 類名<泛型類型1,…>,注意:泛型類型必須是引用類型
package cn.itcast_04;/* * 泛型類的測(cè)試 */public class ObjectToolDemo { public static void main(String[] args) { // ObjectTool ot = new ObjectTool(); // // ot.setObj(new String("風(fēng)清揚(yáng)")); // String s = (String) ot.getObj(); // System.out.println("姓名是:" + s); // // ot.setObj(new Integer(30)); // Integer i = (Integer) ot.getObj(); // System.out.println("年齡是:" + i); // ot.setObj(new String("林青霞")); // // ClassCastException // Integer ii = (Integer) ot.getObj(); // System.out.println("姓名是:" + ii); System.out.println("-------------"); ObjectTool<String> ot = new ObjectTool<String>(); // ot.setObj(new Integer(27)); //這個(gè)時(shí)候編譯期間就過不去 ot.setObj(new String("林青霞")); String s = ot.getObj(); System.out.println("姓名是:" + s); ObjectTool<Integer> ot2 = new ObjectTool<Integer>(); // ot2.setObj(new String("風(fēng)清揚(yáng)"));//這個(gè)時(shí)候編譯期間就過不去 ot2.setObj(new Integer(27)); Integer i = ot2.getObj(); System.out.println("年齡是:" + i); }}//泛型類:把泛型定義在類上class ObjectTool<T> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; }}把泛型定義在方法上,格式:public <泛型類型> 返回類型 方法名(泛型類型 .)
package cn.itcast_05;public class ObjectToolDemo { public static void main(String[] args) { // ObjectTool ot = new ObjectTool(); // ot.show("hello"); // ot.show(100); // ot.show(true); // ObjectTool<String> ot = new ObjectTool<String>(); // ot.show("hello"); // // ObjectTool<Integer> ot2 = new ObjectTool<Integer>(); // ot2.show(100); // // ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>(); // ot3.show(true); // 定義泛型方法后 ObjectTool ot = new ObjectTool(); ot.show("hello"); ot.show(100); ot.show(true); }}//泛型方法:把泛型定義在方法上class ObjectTool { public <T> void show(T t) { System.out.println(t); }}把泛型定義在接口上,格式:public interface 接口名<泛型類型1…>
package cn.itcast_06;public class InterDemo { public static void main(String[] args) { // 第一種情況的測(cè)試 // Inter<String> i = new InterImpl(); // i.show("hello"); // // 第二種情況的測(cè)試 Inter<String> i = new InterImpl<String>(); i.show("hello"); Inter<Integer> ii = new InterImpl<Integer>(); ii.show(100); }}//泛型接口:把泛型定義在接口上 interface Inter<T> { public abstract void show(T t);}/實(shí)現(xiàn)類在實(shí)現(xiàn)接口的時(shí)候//第一種情況:已經(jīng)知道該是什么類型的了//public class InterImpl implements Inter<String> {//// @Override// public void show(String t) {// System.out.println(t);// }// }//第二種情況:還不知道是什么類型的class InterImpl<T> implements Inter<T> { @Override public void show(T t) { System.out.println(t); }}為了解決類型被限制死了不能動(dòng)態(tài)根據(jù)實(shí)例來確定的缺點(diǎn),引入了“通配符泛型”,針對(duì)上面的例子,使用通配泛型格式為
package cn.itcast_07;import java.util.ArrayList;import java.util.Collection;/* * 泛型高級(jí)(通配符) * ?:任意類型,如果沒有明確,那么就是Object以及任意的Java類了 * ? extends E:向下限定,E及其子類 * ? super E:向上限定,E極其父類 */public class GenericDemo { public static void main(String[] args) { // 泛型如果明確的寫的時(shí)候,前后必須一致 Collection<Object> c1 = new ArrayList<Object>(); // Collection<Object> c2 = new ArrayList<Animal>(); // Collection<Object> c3 = new ArrayList<Dog>(); // Collection<Object> c4 = new ArrayList<Cat>(); // ?表示任意的類型都是可以的 Collection<?> c5 = new ArrayList<Object>(); Collection<?> c6 = new ArrayList<Animal>(); Collection<?> c7 = new ArrayList<Dog>(); Collection<?> c8 = new ArrayList<Cat>(); // ? extends E:向下限定,E及其子類 // Collection<? extends Animal> c9 = new ArrayList<Object>(); Collection<? extends Animal> c10 = new ArrayList<Animal>(); Collection<? extends Animal> c11 = new ArrayList<Dog>(); Collection<? extends Animal> c12 = new ArrayList<Cat>(); // ? super E:向上限定,E極其父類 Collection<? super Animal> c13 = new ArrayList<Object>(); Collection<? super Animal> c14 = new ArrayList<Animal>(); // Collection<? super Animal> c15 = new ArrayList<Dog>(); // Collection<? super Animal> c16 = new ArrayList<Cat>(); }}class Animal {}class Dog extends Animal {}class Cat extends Animal {}針對(duì)集合操作 的工具類,里面的方法都是靜態(tài)的,可以對(duì)集合進(jìn)行排序、二分查找、反轉(zhuǎn)、混排等。
Collection:是單列集合的頂層接口,有子接口List和Set。Collections:是針對(duì)集合操作的工具類,有對(duì)集合進(jìn)行排序和二分查找等方法
1、public static <T> void sort(List<T> list)
使用sort方法可以根據(jù)元素的自然順序 對(duì)指定列表按升序進(jìn)行排序。列表中的所有元素都必須實(shí)現(xiàn) Comparable 接口。此列表內(nèi)的所有元素都必須是使用指定比較器可相互比較的
2、public static <T> int binarySearch(List<?> list,T key)
使用二分搜索法搜索指定列表,以獲得指定對(duì)象。
3、public static <T> T max(Collection<?> coll)
根據(jù)元素的自然順序,返回給定 collection 的最大元素。
4、public static void reverse(List<?> list)
反轉(zhuǎn)指定列表中元素的順序。
5、public static void shuffle(List<?> list)
混排算法所做的正好與 sort 相反: 它打亂在一個(gè) List 中可能有的任何排列的蹤跡。也就是說,基于隨機(jī)源的輸入重排該 List, 這樣的排列具有相同的可能性(假設(shè)隨機(jī)源是公正的)。這個(gè)算法在實(shí)現(xiàn)一個(gè)碰運(yùn)氣的游戲中是非常有用的。例如,它可被用來混排代表一副牌的 Card 對(duì)象的一個(gè) List 。另外,在生成測(cè)試案例時(shí),它也是十分有用的。
6、fill(List<? super T> list, T obj)
使用指定元素替換指定列表中的所有元素。
7、copy(List<? super T> dest, List<? extends T> src)
將所有元素從一個(gè)列表復(fù)制到另一個(gè)列表。用兩個(gè)參數(shù),一個(gè)目標(biāo) List 和一個(gè)源 List, 將源的元素拷貝到目標(biāo),并覆蓋它的內(nèi)容。目標(biāo) List 至少與源一樣長。如果它更長,則在目標(biāo) List 中的剩余元素不受影響。
8、集合線程安全化
List<T> synchronizedList(List<T> list);//返回支持的同步(線程安全的)List集合
Map<K,V> synchronizedMap(Map<K,V> m):返回支持的同步(線程安全的)Map集合
package cn.itcast_01;import java.util.Collections;import java.util.List;import java.util.ArrayList;/* * Collections:是針對(duì)集合進(jìn)行操作的工具類,都是靜態(tài)方法。 * * 面試題: * Collection和Collections的區(qū)別? * Collection:是單列集合的頂層接口,有子接口List和Set。 * Collections:是針對(duì)集合操作的工具類,有對(duì)集合進(jìn)行排序和二分查找的方法 * * 要知道的方法 * public static <T> void sort(List<T> list):排序 默認(rèn)情況下是自然順序。 * public static <T> int binarySearch(List<?> list,T key):二分查找 * public static <T> T max(Collection<?> coll):最大值 * public static void reverse(List<?> list):反轉(zhuǎn) * public static void shuffle(List<?> list):隨機(jī)置換 */public class CollectionsDemo { public static void main(String[] args) { // 創(chuàng)建集合對(duì)象 List<Integer> list = new ArrayList<Integer>(); // 添加元素 list.add(30); list.add(20); list.add(50); list.add(10); list.add(40); System.out.println("list:" + list); // public static <T> void sort(List<T> list):排序 默認(rèn)情況下是自然順序。 // Collections.sort(list); // System.out.println("list:" + list); // [10, 20, 30, 40, 50] // public static <T> int binarySearch(List<?> list,T key):二分查找 // System.out // .println("binarySearch:" + Collections.binarySearch(list, 30)); // System.out.println("binarySearch:" // + Collections.binarySearch(list, 300)); // public static <T> T max(Collection<?> coll):最大值 // System.out.println("max:"+Collections.max(list)); // public static void reverse(List<?> list):反轉(zhuǎn) // Collections.reverse(list); // System.out.println("list:" + list); //public static void shuffle(List<?> list):隨機(jī)置換 Collections.shuffle(list); System.out.println("list:" + list); }}運(yùn)行結(jié)果:

一、概述
此類包含用來操作數(shù)組(比如排序和搜索)的各種方法。此類還包含一個(gè)允許將數(shù)組作為列表來查看的靜態(tài)工廠。 二、常用方法
1、集合與數(shù)組的轉(zhuǎn)換
(1) 將數(shù)組轉(zhuǎn)換為集合
Lsit<T> asList(T… a);//返回一個(gè)受指定數(shù)組支持的固定大小的列表。
把數(shù)組變成List集合的好處:可以使用集合的思想和方法來操作數(shù)組中的元素。如:contains,get,indexOf,subList等方法。
PS:
將數(shù)組轉(zhuǎn)換成集合,不可使用集合的增刪方法,因?yàn)閿?shù)組的長度是固定的。如果進(jìn)行增刪操作,則會(huì)產(chǎn)生UnsupportedOperationException的編譯異常。如果數(shù)組中的元素都是對(duì)象,則變成集合時(shí),數(shù)組中的元素就直接轉(zhuǎn)為集合中的元素。如果數(shù)組中的元素都是基本數(shù)據(jù)類型,那么會(huì)將該數(shù)組作為集合中的元素存在。(2) 集合轉(zhuǎn)數(shù)組
2、查找
int binarySearch():使用二分搜索法來搜索指定的 byte 型數(shù)組,以獲得指定的值
3、判斷 boolean equals(Object[] a, Object[] a2):判斷指定的兩個(gè)數(shù)組是否相等
4、排序 void sort():對(duì)指定的數(shù)組按數(shù)字升序進(jìn)行排序。
5、復(fù)制 <T> T[] copyOf(T[] original, int newLength) 復(fù)制指定的數(shù)組,截取或用 null 填充(如有必要),以使副本具有指定的長度。
6、填充 void fill(Object[] a, Object val) 將指定的 Object 引用分配給指定 Object 數(shù)組的每個(gè)元素。
7、其他方法 (1) toString(Object[] a):返回指定數(shù)組內(nèi)容的字符串表示形式。 (2) int hashCode(Object[] a) 基于指定數(shù)組的內(nèi)容返回哈希碼。
package com.heima.test;import java.util.ArrayList;import java.util.Arrays;import java.util.List;public class Test { public static void main(String[] args) { System.out.println("1、將數(shù)組轉(zhuǎn)成集合"); System.out.println("-------------------"); // asList()將數(shù)組轉(zhuǎn)換為集合 List<Integer> list = Arrays.asList(87, 67, 65, 544); for (Integer i : list) { System.out.println(i); } System.out.println("2、二分查找"); System.out.println("-------------------"); // binarySearch()二分查找 int[] a = { 23, 45, 67, 8, 32, 45, 6, 7, 85, 54, 3, 432 }; int index = Arrays.binarySearch(a, 45); System.out.println(index); System.out.println("3、排序"); System.out.println("-------------------"); // sort()排序 System.out.println("排序前:" + Arrays.toString(a)); Arrays.sort(a); System.out.println("排序后:" + Arrays.toString(a)); System.out.println("4、集合轉(zhuǎn)成數(shù)組"); System.out.println("-------------------"); // toArray()集合轉(zhuǎn)成數(shù)組 ArrayList<String> al = new ArrayList<String>(); al.add("java"); al.add("javase"); al.add("php"); al.add("ruby"); al.add("android"); String[] str = al.toArray(new String[al.size()]); System.out.println(Arrays.toString(str)); }}運(yùn)行結(jié)果: 
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注