• Java笔记--泛型总结与详解


    泛型简介:

        在泛型没有出来之前,编写存储对象的数据结构是很不方便的。如果要针对每类型的对象写一个数据结构,
        则当需要将其应用到其他对象上时,还需要重写这个数据结构。如果使用了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("
    向泛型化数组添加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{" + "
    	userId =" +userId+ "
    	userName =" + userName
                    + "
    	userAge =" + userAge + "
    	gende =" + gende
                    + "
    	userAddress =" + userAddress + "
    	userTell =" + userTell
                    + "
    	}";
        }
    }

    泛型数据访问操作类

    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));
        }
    
    }
    知识重在总结和梳理,只有不断地去学习并运用,才能化为自己的东西。由于本人进阶猿类时间尚短,故此博客即是我学习,工作的笔记,也是和大家交流,相互提升技术的平台~希望大家不吝赐教~~ --但管努力,莫问前程,事在人为,功不唐捐。--和佑博客园
  • 相关阅读:
    分布式系统笔记
    Paxos算法细节详解(一)
    Java G1学习笔记
    Spring Boot 的 10 个核心模块
    k8s 重点
    毕玄:阿里十年,从分布式到云时代的架构演进之路
    netty原理解析
    JVM调优总结(一):基本概念
    《快学 Go 语言》第 16 课 —— 包管理 GOPATH 和 Vendor
    Oracle 检查表空间使用情况
  • 原文地址:https://www.cnblogs.com/xhjt/p/3958036.html
Copyright © 2020-2023  润新知