泛型简介:
在泛型没有出来之前,编写存储对象的数据结构是很不方便的。如果要针对每类型的对象写一个数据结构,
则当需要将其应用到其他对象上时,还需要重写这个数据结构。如果使用了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.(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*/ public class UsedStack { private LinkedList list = new LinkedList (); /** * 入栈 向栈添加元素 * * @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 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 = 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数组类型 */ public class GenericsArray{ private T[] array; private int size; /** * 泛型化数组构造函数 * * @param type * 数组类型 * @param size * 数组长度 */ @SuppressWarnings("unchecked") public GenericsArray(Class 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 gStrArray = new GenericsArray ( String.class, 3); String[] strs = { "您好!", "我叫和佑!", "我喜欢Java!" }; gStrArray.printService(strs); System.out.println("\n向泛型化数组添加Integer元素"); GenericsArray gIntArray = new GenericsArray ( 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.使用格式来表示泛型类型参数,参数个数可多个;
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 staticList query(String sql, Class type) { QueryRunner qr = new QueryRunner(); List list = null; try { list = (List ) 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) { Listlist = 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接口即:
3.当泛型参数类型被限制为接口的子类型时,也使用extends关键字。
代码实例:
package com.xhj.generics.used; /** * 利用泛型比较类对象实例大小 * * @author XIEHEJUN * */ public class GenericComparable { /** * 比较并获取最小类对象实例 * * @param array * @return */ public staticextends Comparable > 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就是泛型类型参数,可以是多个。
3.在实现此接口时要注意,实现类的泛型参数和接口的泛型参数要相匹配。
代码实例:
泛型接口
package com.xhj.generics.used.ginterface; /** * 定义一个泛型接口 * * @author XIEHEJUN * */ public interface GenericComparableInterface { publicextends Comparable > T getMax(T[] array); } 实现泛型接口 package com.xhj.generics.used.ginterface; public class GenericComparableImp implements GenericComparableInterface { @Override public extends Comparable > 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.通配符可以设置取值下限,如:,参数类型要求是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) { Listlist = new ArrayList (); list.add(182); list.add(0115); list.add(8); list.add(502); list.add(233); System.out.println(getMiddle(list).toString()); List liststr = new ArrayList (); 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 staticextends 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)); } }