泛型簡介:
在泛型沒有出來之前,編寫存儲對象的數據結構是很不方便的。如果要針對每類型的對象寫一個數據結構,
則當需要將其應用到其他對象上時,還需要重寫這個數據結構。如果使用了Object類型,編寫的數據結構雖然
通用性很好,但是不能保證存入的對象的安全性。
--支持知識共享,轉載請標注地址"http://www.cnblogs.com/XHJT/p/3958036.html "——和佑博客園,謝謝~~--
代碼實例1:
不用泛型實現棧結構
1.用Object和自定義棧類實現的一個入棧和出棧的小case;
2.理解棧:棧是算法世界中經常要用到的一種數據結構,它可以實現元素的先進后出。常用於實現字符串反轉,
· 四則運算等。
package com.xhj.generics.unused; /** * 自定義Stack類 * * @author XIEHEJUN * */ public class UsedStack { private Object[] os = new Object[10]; private int index = 0; /** * 將一個元素入棧 * * @param o * 要添加的元素對象 */ public void push(Object o) { if (index != os.length) { os[index++] = o; } } /** * 元素出棧,刪除一個元素對象 * * @return 返回出棧的元素對象 */ public Object pop() { if (index != -1) { return os[--index]; } return null; } public boolean isEmty() { if (index == 0) { return true; } else { return false; } } /** * 輸出棧中所有元素 */ public String toString() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < os.length; i++) { if (os[i] != null) sb.append(os[i]); } return sb.toString(); } public static void main(String[] args) { UsedStack us = new UsedStack(); System.out.println("===================元素入棧===================="); System.out.println("向棧添加三個元素"); String[] strs = { "您好!", "我叫和佑!", "我喜歡Java!" }; us.push(strs[0]); System.out.println("添加元素后的棧為:" + us); us.push(strs[1]); System.out.println("添加元素后的棧為:" + us); us.push(strs[2]); System.out.println("添加元素后的棧為:" + us); System.out.println("===================元素出棧===================="); for (int i = 0; i < us.os.length; i++) { if (!us.isEmty()) { System.out.println("刪除元素"); System.out.println(us.pop()); } else { System.out.println(); } } } }
注:從本實例可以看出:1.要想獲取到適當的值,需要對類型進行強制轉換,而本實例則通過重定義toString方法實現。
2.在本實例任何類型都可入棧,這意味着若是InputStream等類型入棧,在調用toString方法時將會拋出異常。
如:將上面的代碼:us.push(strs[0]);
轉換成:us.push(new InputStreamReader(System.in, "xhj"));
將會拋出:Exception in thread "main" java.io.UnsupportedEncodingException: xhj
at sun.nio.cs.StreamDecoder.forInputStreamReader(StreamDecoder.java:52)
at java.io.InputStreamReader.<init>(InputStreamReader.java:83)
at com.xhj.generics.unused.UsedStack.main(UsedStack.java:67)
使用泛型實現棧結構:
1.泛型是Java中的一個重要特性,使用泛型編程可以使代碼獲得最大的重要。
2.在使用泛型時要指明泛型的具體類型,這樣可以避免類型轉換。
3.泛型類是一個參數類型可變的類;固泛型參數只能是類類型。
代碼實例:
package com.xhj.generics.used; import java.util.LinkedList; /** * 使用泛型實現棧的使用 * * @author XIEHEJUN * * @param <T> */ public class UsedStack<T> { private LinkedList<T> list = new LinkedList<T>(); /** * 入棧 向棧添加元素 * * @param */ public void push(T t) { list.addFirst(t); } /** * 出棧 刪除元素 * * @return */ public T pop() { return list.removeFirst(); } /** * 判斷棧是否為空 * * @return */ public boolean isEmty() { if (list.size() == 0) { return true; } else { return false; } } /** * 重寫toString方法 */ public String toString() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < list.size(); i++) { if (list.get(i) != null) sb.append(list.get(i)); } return sb.toString(); } public static void main(String[] args) { UsedStack<String> us = new UsedStack<String>(); System.out.println("===================元素入棧===================="); System.out.println("向棧添加三個元素"); String[] strs = { "您好!", "我叫和佑!", "我喜歡Java!" }; us.push(strs[0]); System.out.println("添加元素后的棧為:" + us); us.push(strs[1]); System.out.println("添加元素后的棧為:" + us); us.push(strs[2]); System.out.println("添加元素后的棧為:" + us); System.out.println("===================元素出棧===================="); for (int i = us.list.size() - 1; i >= 0; i--) { if (!us.isEmty()) { System.out.println("刪除元素"); System.out.println(us.pop()); System.out.println("棧中元素還有個數為:" + i); } else { System.out.println("棧內已沒有元素"); } } } }
注:泛型可以很好的解決上面出現的那兩個問題,他的主要魅力就在於讓程序有更好的可讀性和安全性。
自定義泛型化數組類:
1.在Java虛擬機中並沒有泛型類型的對象,所有有關泛型的信息都被擦除了。這雖然可以避免C++語言
的模版代碼膨脹問題,但是也引起了其他問題。如:不能直接創建泛型數組等。
2.Java中的泛型不支持實例化類型變量。
3.通過Java的反射機制創建一個泛型化數組
newInstance(Class<?> componentType,int length)
代碼實例:
package com.xhj.generics.used; import java.lang.reflect.Array; /** * 利用Java反射機制泛型化數組 * * @author XIEHEJUN * * @param <T>數組類型 */ public class GenericsArray<T> { private T[] array; private int size; /** * 泛型化數組構造函數 * * @param type * 數組類型 * @param size * 數組長度 */ @SuppressWarnings("unchecked") public GenericsArray(Class<T> type, int size) { array = (T[]) Array.newInstance(type, size); this.size = size; } /** * 向泛型化數組添加元素 * * @param index * @param item */ public void put(int index, T item) { if (index >= 0 && index < size) { array[index] = item; } } /** * 根據數組下標獲取相應值 * * @param index * @return */ public T get(int index) { if (index >= 0 && index < size) { return array[index]; } return null; } /** * 將泛型化數組打印輸出 * * @param t */ public void printService(T[] t) { put(0, t[0]); System.out.println("添加的元素為:" + get(0)); put(1, t[1]); System.out.println("添加的元素為:" + get(1)); put(2, t[2]); System.out.println("添加的元素為:" + get(2)); } public static void main(String[] args) { System.out.println("向泛型化數組添加String元素"); GenericsArray<String> gStrArray = new GenericsArray<String>( String.class, 3); String[] strs = { "您好!", "我叫和佑!", "我喜歡Java!" }; gStrArray.printService(strs); System.out.println("\n向泛型化數組添加Integer元素"); GenericsArray<Integer> gIntArray = new GenericsArray<Integer>( Integer.class, 3); Integer[] arrays = { 10, 52, 32 }; gIntArray.printService(arrays); } }
總結:
Java泛型的局限性
1.不能使用基本類型作為其類型參數;
2.不能拋出或捕獲泛型類型的實例、
3.不能直接使用泛型數組、
4.不能實例化類型變量
5.對於某些不足,可以通過Java的反射機制進行彌補。
泛型方法與數據查詢
眾所周知在使用JDBC查詢數據庫中數據的時候,返回的結果是ResultSet對象,這種機制,
我們在實際使用過程中是很不方便的。當然Java還提供了Commons DbUtils組件來將ResultSet
轉化為Bean列表的方法,但是該在使用的時候是需要根據不同的Bean對象創建不同的查詢方法的。
下面我將在此方法基礎上使用泛型進行包裝,以便提高它的通用性。
在Java中,不僅可以聲明泛型類,還可以聲明泛型方法:
1.使用<T>格式來表示泛型類型參數,參數個數可多個;
2.類型參數列表要放在訪問權限修飾符、static和final之后;
3.類型參數列表要放在返回值類型、方法名稱、方法參數之前。
代碼實例:
對象實體類
package com.xhj.generics.used.entity; /** * 用戶實體類 * * @author XIEHEJUN * */ public class User { private String userName; private String userId; private int userAge; private String userAddress; private String gende; private long userTell; public User() { super(); } public User(String userName, String userId, int userAge, String userAddress, String gende, long userTell) { this.userName = userName; this.userId = userId; this.userAge = userAge; this.userAddress = userAddress; this.gende = gende; this.userTell = userTell; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserId() { return userId; } public int getUserAge() { return userAge; } public void setUserAge(int userAge) { this.userAge = userAge; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } public String getGende() { return gende; } public void setGende(String gende) { this.gende = gende; } public long getUserTell() { return userTell; } public void setUserTell(long userTell) { this.userTell = userTell; } @Override public String toString() { return "User{" + "\n\tuserId =" +userId+ "\n\tuserName =" + userName + "\n\tuserAge =" + userAge + "\n\tgende =" + gende + "\n\tuserAddress =" + userAddress + "\n\tuserTell =" + userTell + "\n\t}"; } }
泛型數據訪問操作類
package com.xhj.generics.used.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.List; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; /** * 數據庫操作類,定義增刪改查等操作方法 * * @author XIEHEJUN * */ public class GenericQuery { private static String URL = "jdbc:oracle:thin:@192.168.100.13:1521:SIGMA"; private static String DRIVRR = "ojdbc6"; private static String USER = "PCD_Online_V2"; private static String PASSWORD = "password"; private static Connection con; /** * 獲取數據庫連接 * * @return */ public static Connection getConnecton() { DbUtils.loadDriver(DRIVRR); try { con = DriverManager.getConnection(URL, USER, PASSWORD); } catch (SQLException e) { System.out.println("連接失敗"); } return con; } /** * 查詢數據 * * @param sql * SQL語句 * @param type * 實體類類型 * @return */ @SuppressWarnings("unchecked") public static <T> List<T> query(String sql, Class<T> type) { QueryRunner qr = new QueryRunner(); List<T> list = null; try { list = (List<T>) qr.query(getConnecton(), sql, new BeanListHandler( type)); } catch (SQLException e) { System.out.println("SQL語句不正確"); e.printStackTrace(); }finally{ DbUtils.closeQuietly(con); } return list; } /** * 更新數據--增/刪/改 */ public static void queryUpdate(String sql) { QueryRunner qr = new QueryRunner(); try { qr.update(getConnecton(), sql); } catch (SQLException e) { e.printStackTrace(); }finally{ DbUtils.closeQuietly(con); } } }
業務操作類
package com.xhj.generics.used.service; import java.util.List; import com.xhj.generics.used.dao.GenericQuery; import com.xhj.generics.used.entity.User; /** * 調用數據庫操作方法,對數據進行增刪改查等操作 * * @author XIEHEJUN * */ public class Service { /** * 插入數據 * * @param user */ public static void update(User user) { String sql = "insert into XHJUSER values('" + user.getUserId() + "','" + user.getUserName() + "','" + user.getUserAge() + "','" + user.getGende() + "','" + user.getUserAddress() + "','" + user.getUserTell() + "')"; GenericQuery.queryUpdate(sql); } /** * 修改數據 * * @param sql */ public static void update(String sql) { GenericQuery.queryUpdate(sql); } /** * 查詢數據 * * @param user * @param sql */ public static void select(User user, String sql) { List<User> list = GenericQuery.query(sql, User.class); System.out.println("表中數據有:"); for (int i = 0; i < list.size(); i++) { System.out.println(i + "號對象屬性值為:" + list.get(i)); } } /** * 刪除數據 * * @param user */ public static void delete(String sql) { GenericQuery.queryUpdate(sql); } }
測試類:
package com.xhj.generics.used.main; import com.xhj.generics.used.entity.User; import com.xhj.generics.used.service.Service; /** * 測試類 * * @author XIEHEJUN * */ public class Test { public static void main(String[] args) { User user = new User("B", java.util.UUID.randomUUID().toString(), 12, "湖南", "女", 1213344455); Service.update(user); String sql = "select * from XHJUser where username = 'B'"; Service.select(user, sql); sql = "update XHJuser set username = 'D' where userid ='1ab3ee1b-1c52-43d2-8df5-ffefb92c9c5c'"; Service.update(sql); sql = "delete from XHJuser where username = 'C'"; Service.delete(sql); } }
注:泛型類與泛型方法的重要區別
1.在使用泛型類時,需要注意不能將泛型參數類型用於靜態域和靜態方法中,而對於泛型方法則可以是靜態的。
2.這種區別主要是"擦除"產生的。由於在泛型方法中已經指明了參數的具體類型,故即使發生擦除,也不會丟失。
泛型化方法與最小值
1.在Java中除了數值可以比較大小外,任何實現了Comparable接口的類的實例,都可以比較大小。
2.在比較類的對象是,需要限制比較的對象實現Comparable接口即:<T extends Comparable>
3.當泛型參數類型被限制為接口的子類型時,也使用extends關鍵字。
代碼實例:
package com.xhj.generics.used; /** * 利用泛型比較類對象實例大小 * * @author XIEHEJUN * */ public class GenericComparable { /** * 比較並獲取最小類對象實例 * * @param array * @return */ public static <T extends Comparable<T>> T getMin(T[] array) { if (array.length == 0 || array == null) { return null; } else { T min = array[0]; for (int i = 0; i < array.length; i++) { if (min.compareTo(array[i]) > 0) { min = array[i]; } } return min; } } public static void main(String[] args) { String[] strs = { "您好!我是和佑b,來自和佑博客園", "您好!我是和佑a,來自和佑博客園", "您好!我是和佑c,來自和佑博客園" }; System.out.println("最小的類對象實例為:" + getMin(strs)); } }
注:1.compareTo()方法先是逐步比較ASCII碼,若是此時仍無法得出結果,再比較其長度
2.泛型類型參數的限定一般有兩種情況:
a.小於某一個"范圍"
b.大於某一個"范圍"
范圍即可以是一個類,也可以是一個接口,還可以是類和接口的組合,對於組合來說,需要將類放在第一位,並且用&分隔。
泛型化接口與最大值
1.在Java中除了可以定義類和方法,還可以定義泛型接口。泛型接口的作用和普通接口一樣,只是它的實用性更強。
對於很多具體類型通用的方法,可以將其提取到一個泛型接口中,再編寫一個泛型類實現這個接口即可。
2.定義泛型接口和定義泛型類是相似的,直接在接口名稱后面加上<T>即可。T就是泛型類型參數,可以是多個。
3.在實現此接口時要注意,實現類的泛型參數和接口的泛型參數要相匹配。
代碼實例:
泛型接口
package com.xhj.generics.used.ginterface; /** * 定義一個泛型接口 * * @author XIEHEJUN * */ public interface GenericComparableInterface { public <T extends Comparable<T>> T getMax(T[] array); } 實現泛型接口 package com.xhj.generics.used.ginterface; public class GenericComparableImp implements GenericComparableInterface { @Override public <T extends Comparable<T>> T getMax(T[] array) { if(array==null||array.length==0){ return null; }else{ T max = array[0]; for (int i = 0; i < array.length; i++) { if(max.compareTo(array[i])<0){ max = array[i]; } } return max; } } public static void main(String[] args) { GenericComparableImp gci = new GenericComparableImp(); String[] strs = { "您好!我是和佑b,來自和佑博客園", "您好!我是和佑a,來自和佑博客園", "您好!我是和佑c,來自和佑博客園" }; System.out.println("最小的類對象實例為:" +gci.getMax(strs)); } }
注:泛型接口的應用
一個大型網站的后台往往使用多個數據表,可以將一些公共的操作如數據的增刪改以及保存等放在一個泛型的DAO接口中定義,
在針對使用的持久層技術,編寫此DAO的實現類,這些對於每一個持久化的對象,直接繼承這個實現類,再去實現特有 方法即可。
使用通配符增強泛型
1.Java中的數組支持協變類型,即如果方法參數是數組T,而S是T的子類,則方法也可以使用參數S。對於泛型類則沒有這個特性。
為了彌補這個不足,Java推出了通配符類型參數。
2.使用通配符"?"可以讓泛型在實際應用當中更加的靈活
3.通配符可以利用"extends"關鍵字來設置取值上限,如:<? extends Number>,參數類型要求繼承Number
4.通配符可以設置取值下限,如:<?super Number>,參數類型要求是Number的父類
5.通配符可有多個"界限",如:實現多個接口,在接口間用&分隔。
代碼實例:
package com.xhj.generics.used; import java.util.ArrayList; import java.util.List; /** * 通配符在泛型中作用 * * @author XIEHEJUN * */ public class Wildcard { /** * 獲取基於Number父類下的list的中間數 * * @param list * @return */ public static Object getMiddle(List<? extends Number> list) { return list.get(list.size() / 2); } /** * 獲取任何繼承Object的List下的中間值 * * @param list * @return */ public static Object getMiddles(List<? extends Object> list) { return list.get(list.size() / 2); } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(182); list.add(0115); list.add(8); list.add(502); list.add(233); System.out.println(getMiddle(list).toString()); List<String> liststr = new ArrayList<String>(); liststr.add("您好!"); liststr.add("吃飯了嗎!"); liststr.add("美女!"); liststr.add("下午有空嗎!"); liststr.add("看電影去吧!"); System.out.println(getMiddles(liststr).toString()); } }
泛型化的折半查找法
1.查找就是在一組給定的數據集合中找出滿足條件的數據。
2.折半查找要求數據集合中的元素必須可比較,且各元素按升序或者降序排列:
取集合的中間元素作為比較對象,則:
a.如果給定的值與比較對象相等,則查找成功,並返回中間元素的序號。
b.若大於比較對象,則在中間元素的右半段進行查找
c.若小於比較對象,則在中間元素的左半段進行查找
3.循環執行上述過程,直至查找成功,此時折半查找的平均時間復雜度是log2n
代碼實例:
package com.xhj.generics.used; /** * 泛型化折半查找算法 * * @author XIEHEJUN * */ public class HalfSearch { /** * 折半查找的實現方法 * * @param key * @param array * @return */ public static <T extends Comparable<? super T>> int search(T key, T[] array) { int low = 0; int high = array.length - 1; int mid = 0; while (low <= high) { mid = (low + high) / 2; if (key.compareTo(array[mid]) == 0) { return mid; } else if (key.compareTo(array[mid]) > 0) { low += 1; } else { high -= 1; } } return -1; } public static void main(String[] args) { Integer[] array = { 12, 3, 8, 45, 26, 68 }; String str = "GFISUDGHUIW"; String[] arraystr = { str, "GYFSGHFUIH", "CD" }; System.out.println("在String數組中'GFISUDGHUIW'的索引為:" + search(str, arraystr)); System.out.println("在整型數組中'3'的索引為:" + search(3, array)); } }
