黑马程序员——Java之基本数据类型包装类及泛型

来源:互联网 发布:java命令行执行junit 编辑:程序博客网 时间:2024/06/06 23:18
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
内容提要:
        基本数据类型对象包装类
        基本数据类型对象包装类新特性
        泛型概述
        泛型使用
        泛型类
        泛型方法
        静态方法泛型
        泛型接口
        泛型限定
        泛型应用

基本数据类型包装类
        Java提供的八种基本数据类型,但不支持面向对象的编程机制,基本数据类型的数据也不具备“对象”的特性:没有成员变量、方法可以被调用。因此,Java为八种基本数据类型分别定义了相应的引用类型(包装类类型)。
        基本数据类型对象的包装类以:基本数据类型:引用数据类型(基本数据类型包装类)给出。
        例如:byte对应Byte,short对应Short,int对应Integer,long对应Long,boolean对应Boolean,float对应Float,double对应Double,char对应Character。
        Java之所以提供这八种基本数据类型,主要是为了照顾程序员的传统习惯;实际需要解决八种基本数据类型不能被当做Object类型变量被使用的问题,由此引出了包装类的概念。
        基本数据类型对象包装类的最常用作用,就是用于基本数据类型和字符串类型之间做转换。
        也就是:基本数据类型转成字符串 int->String,方法有:基本数据类型+""(空的字符串),或者是(静态方法)Integer.toString(int value),或者是int->Integer->String然后使用Integer的toString方法,此外还有String类的静态方法valueOf()。



        字符串转成基本数据类型(包装类的静态方法)parseInt(“123”);还可使用包装类的静态方式:intValue()方法(String->Integer->int);还可以使用构造方法,同样是String->Integer->int单词parse具有解析的含义。
        转换成包装类的优点(封装性),可以使用Java平台封装好的包装类类方法,比如Integer类中包含的十进制转成其他进制方法、其他进制转成十进制方法等等。
public class IntegerDemo2 {public static void sop(String str) {System.out.println(str);}public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println("String: " + Integer.toString(34)); // 基本数据类型转成字符串int num1 = Integer.parseInt("123"); // 字符串类型转成基本数据类型sop("num1 = " + (num1 + 2));Integer num2 = new Integer("124");sop("num2 = " + num2.intValue());boolean x_boolean = Boolean.parseBoolean("true");double x_double = Double.parseDouble("12.3");sop("x_boolean = " + x_boolean);sop("x_long = " + x_double);sop(Integer.toBinaryString(-6)); // 十进制转换成二进制等其他进制sop(Integer.toHexString(-6)); // 十进制转换成十六进制sop(Integer.toOctalString(-6)); // 十进制转换成八进制sop("" + Integer.parseInt("3c", 16)); // 按照特定进制将字符串转换成十进制表示结果}}
基本数据类型对象包装类新特性
        自动装箱和自动拆箱功能:JDK 1.5之后,实现了自动装箱和拆箱功能。
        自动装箱,就是可以把一个基本类型变量直接赋给对应的包装类变量,或者赋给Object变量;
        自动拆箱则与之相反,允许直接把包装类对象直接赋给一个对应的基本类型变量。
        包装类型变量和基本数据类型的一大不同点:前者可赋值为null。
        系统把byte类型-128~127之间的整数自动装箱成Integer实例,并放入一个名为cache的数组中缓存起来。如果以后把一个该范围之间的整数自动装箱成一个Integer实例时,实际上是直接指向对应的数组元素。不在该范围内的值,系统将新建一个对象用于存放该变量。(Java将一些创建成本大、需要频繁使用的对象缓存起来,从而提高程序的运行特性)
/* * 测试Integer包装类型的新 特性:自动装箱和拆箱操作 * */public class IntegerDemo {public static void main(String[] args) {// TODO Auto-generated method stubmethod1();System.out.println("<------------------------>");method2();System.out.println("<------------------------>");method3();}public static void method3() {// 以下两种方式都不是自动装箱操作Integer x = new Integer("123"); // 使用字符串创建Integer类型变量,默认使用十进制编码方式Integer y = new Integer(123); // 使用int类型的值创建Integer类型变量sop("x == y?--->" + (x == y)); // false巩固操作符"=="sop("x equals y--->" + x.equals(y)); // trueInteger number = 2; // 自动装箱操作,实际上调用了调用了Integer.valueOf(2)number = number + 2; // 运算x+2操作时,x对象调用了intValue()方法,变成了int类型,再和2进行加法运算;最后进行装箱操作sop(number.toString()); // 4Integer n1 = 127;Integer n2 = 127;sop("n1 == n2?--->" + (n1 == n2)); // true 包装类的新特性n1 = 128;n2 = 128;sop("n1 == n2?--->" + (n1 == n2)); // false 该数值范围不在-128~127之间,将新建对象Integer i1 = 1;Integer i2 = new Integer(1);Integer i3 = Integer.valueOf(1);sop("i1==i2?--->" + (i1 == i2)); // falsesop("i1==i3?--->" + (i1 == i3)); // truesop("i2==i3?--->" + (i2 == i3)); // false}public static void method2() {Integer x = 123;Integer y = 123; // JDK1.5之后出现的新特性:自动装箱Integer z = 127;Integer m = 128;sop("x==y? " + (x == y)); // truesop("x equals y? " + (x.equals(y))); // truex = x + 4; // 先进行自动拆箱,变成int类型,和4进行加法运算后,再自动装箱sop("x==y? " + (x == y)); // falsesop("x==z? " + (x == z)); // true 自动装箱后,仍然使用的是同一个对象x = x + 1;sop("x==m? " + (x == m)); // false 超出了-128~127的范围,将新建一个对象}public static void method1() {Integer x = new Integer("123");Integer y = new Integer("123");sop("x==y? " + (x == y)); // falsesop("x equals y? " + (x.equals(y))); // true}public static void sop(String str) {System.out.println(str);}}
运算结果:


        运算结果分析:对于method1()方法的运算结果比较易懂;但method()方法中就说明了包装类的新特性:自动装箱和拆箱。
        缓存:Integer类型中使用缓存的实例:Integer i1 = 1; Integer i2 = new Integer(1); Integer i3 = Integer.valueOf(1); 第一种方式中,新建一个Integer对象,自动装箱转换并缓存;第二种方式中,新类一个Integer对象,未缓存;第三种方式中,新建一个Integer对象,并缓存。因为第一种方式已经缓存了,第三个语句将直接使用该实例对象,不在新建对象。
    public static Integer valueOf(int i) {        if (i >= IntegerCache.low && i <= IntegerCache.high)            return IntegerCache.cache[i + (-IntegerCache.low)];        return new Integer(i);    }
        代码分析:以上代码是Integer类的源码,在语句Integer i1 = 1; 中,编译器实际上执行的是Integer i1 = Integer.valueOf(1); 该方法会判断是否在cache[]数组中。 这也是为什么会有新特性的原因。
        不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。
        Java提供的8个包装类和java.lang.String类都是不可变类,当创建他们的实例后,其实例的实例变量不可改变。
        如果需要设计一个不可变类,尤其要注意其引用类型的成员变量,如果引用类型的成员变量的类是可变的,就必须采取必要的措施来保护该成员变量所引用的对象不会被改变。
        练习:
/* * 需求:对字符串"12 0 99 -7 30 4 -100 23"中的数值进行排序,生成一个 * 数值从小到大的新字符串 *  * 思路: * 1.将字符串切割为字符串数组 * 2.将字符串数组转化为整数类型数组 * 3.对整数类型数组进行冒泡排序 * 4.将排序后的整数类型数组转化为字符串 * */public class IntegerDemo3 {public static void main(String[] args) {// TODO Auto-generated method stubString s = "12 0 99 -7 30 4 -100 23";sop(s);sop(stringSort(s));/* * 测试结果 *  * 12 0 99 -7 30 4 -100 23 <-----------> 12 0 99 -7 30 4 -100 23 * <-----------> -100 -7 0 4 12 23 30 99 */}// 字符串分隔public static String stringSort(String s) {String[] str = s.split(" "); // 对指定字符串用空格符分隔int[] arr = stringToInt(str); // 字符串数组转化为整型数组System.out.println("<----------->");sop(arr);System.out.println("<----------->");bubbleSort(arr);return intToString(arr).toString();}// 将字符串数组转化为整数类型数组public static int[] stringToInt(String[] str) {int[] arr = new int[str.length];for (int x = 0; x < str.length; x++) {arr[x] = Integer.parseInt(str[x]); // 将字符串转化为整型数值}return arr;}// 对数组中的元素进行冒泡排序public static void bubbleSort(int[] arr) {for (int x = 0; x < arr.length; x++) {for (int y = 0; y < arr.length - x - 1; y++) {if (arr[y] > arr[y + 1])swap(arr, y, y + 1);}}}// 交换数组中的元素public static void swap(int[] arr, int x, int y) {arr[x] = arr[x] ^ arr[y];arr[y] = arr[x] ^ arr[y];arr[x] = arr[x] ^ arr[y];}// 将整数类型数组存放到一个StringBuilder容器中public static StringBuilder intToString(int[] arr) {StringBuilder sb = new StringBuilder();for (int index = 0; index < arr.length - 1; index++) {sb.append(arr[index] + " ");}sb.append(arr[arr.length - 1] + "");return sb;}public static void sop(String str) {System.out.println(str);}// 遍历数组public static void sop(int[] arr) {for (int index = 0; index < arr.length; index++) {System.out.print(arr[index] + "\t");}System.out.println();}}
泛型概述
        泛型Generic,是JDK1.5版本以后出现新特性,用于解决安全问题,是一个安全机制。
        解决类似于:在定义集合的时候,就明确规定了该集合中应该存入元素的类型,无法加入指定类型以外的数据,可以在编译时期指出存入元素所发现的类型转换问题,这就是泛型的由来。
        工作原理:提供给javac编译器使用的,可以限定集合中的输入类型;但编译后生成的字节码会去掉泛型的类型信息,使程序运行效率不受影响。
        没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用了泛型的集合,可以将一个集合中的元素限定为一个特点类型,集合中只能存储同一个类型的对象;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换。
        所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参),Java的参数化类型被称为泛型(Generic)
        在使用Java提供的对象时,什么时候写泛型呢?
        “参数化类型”,允许程序在创建集合时指定集合元素的类型。创建这种特殊集合的方法是:在集合接口、类后增加尖括号,尖括号里放一个数据类型,即表明这个集合接口、集合类只能保存特定类型的对象。如:List<String>称List是带一个类型参数String的泛型接口。
        定义泛型好处:
        其一、将运行时期出现问题ClassCastException,转移到了编译时期,方便于程序员解决问题,让运行时期问题减少,增加安全性。
        其二、避免了强制转换的麻烦行为(之前的程序员需要主观地判断容器中存入的是什么元素,并进行主观限定存入的元素类型;而现在是由JVM客观地限定存入元素类型)。
        泛型在程序编译期指出了集合支持的类型,用类型参数指定,避免了产生编译期的不检查类型的异常。
public class GenericDemo1 {public void func() {System.out.println("func running");}public static void main(String[] args) throws Exception {ArrayList<String> al = new ArrayList<String>();// ArrayList al = new ArrayList();al.add("zhang");al.add("feng");// 编译期会报错// al.add(4); //假若定义时并写出泛型,则无法通过编译// 指出迭代器指针类型Iterator<String> it = al.iterator();while (it.hasNext()) {String str = it.next();System.out.println("str length = " + str.length());}Object obj = new GenericDemo1();((GenericDemo1) obj).getClass().newInstance().func(); // 需要进行强制类型转换,编译器将func()方法被当做是GenericDemo1类对象的方法/* * Class c = ((GenericDemo1)obj).getClass(); c.newInstance().func(); * //编译无法通过,原因:编译器将c当做了Object类型字节码;The method func() is undefined for * the type Object */// 将以上两条语句改写成以下形式即可编译通过Class<? extends GenericDemo1> c = ((GenericDemo1) obj).getClass(); // 使用泛型,限定c的类型字节码c.newInstance().func();}}
       代码分析:在定义ArrayList集合时,使用了泛型用以限定集合中存入的元素类型:String类型。但假若在定义时不写泛型,对于al.add(4);则会编译时期并不报错,但会在运行时发生错误;但假若写了泛型,规定了集合存入的元素类型,针对于al.add(4)会在编译时期直接指出错误,导致编译无法通过。
泛型使用
        泛型格式说明:通过<>来定义要操作的引用数据类型,通常在集合框架中很常见,只要见到<>就要定义泛型,其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可,如同函数中传递参数。必须指出的是,传递的数据类型必须是引用数据类型。
        如:ArrayList<E>类和ArrayList<Integer>类;
        其中:ArrayList<E>整个称为是泛型类型;ArrayList<E>中的E称为类型变量或类型参数;整个ArrayList<Integer>称为参数化(已经将参数变为实际类型的状态)类型;ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数;ArrayList为原始类型。
关于参数化类型的几点说明:
        其一、参数的类型不考虑类型参数的继承关系;
        其二、编译器不允许创建泛型变量的数组,即在创建数组实例时,数组的元素不能使用参数化的类型。
public class Test {public static void main(String[] args) {// TODO Auto-generated method stubVector<String> v1 = new Vector<Object>(); // cannot convert from Vector<Object> to Vector<String>Vector<Object> v2 = new Vector<String>(); // cannot convert from Vector<String> to Vector<Object>Vector<Integer>[] vectorList = new Vector<Integer>[10]; //Cannot create a generic array of Vector<Integer>}}
        从Java7开始,Java允许在构造器后不需要带完整的泛型信息,只需要给出一对尖括号(<>)即可,Java可以(根据定义时类型)推断尖括号里应该是什么泛型信息。
        包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。
        当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。
//泛型在比较器中的使用import java.util.*;public class GenericDemo2 {public static void main(String[] args) {// 指定传入TreeMap中的元素类型是StringTreeSet<String> ts = new TreeSet<String>(new SLComparator());ts.add("zd");ts.add("asc");ts.add("az");ts.add("a");ts.add("az");Iterator<String> it = ts.iterator();while (it.hasNext()) {String str = it.next();System.out.println(str);}}}// 指定比较元素比较类型是Stringclass SLComparator implements Comparator<String> {// 因为已经指定比较元素的类型了,无须强制转换public int compare(String s1, String s2) {if (s1.length() > s2.length())return 1;else if (s1.length() < s2.length())return -1;elsereturn s1.compareTo(s2);}}
       代码分析:在定义比较器时,写了泛型<String>,也就是明确规定了传入的参数只能是String类型;在覆写compare()方法时,避免了对两个比较参数的类型装换。
泛型类
        若类实例对象中要使用到同一泛型参数,即这些地方引用类型要保持同一实际类型时,这时候就要采用泛型类的方式进行定义,也就是类级别的泛型。
        什么时候应该定义泛型类?当类中要操作的引用数据类型不确定的时候,早期要定义Object来完成扩展,现在用泛型类完成。
        泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,要操作的类型就已经确定下来了。类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的。
        从泛型类派生子类:当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含类型形参。
        当使用一个泛型类时(包括声明变量和创建对象两种情况),都应该为这个泛型类传入一个类型实参。如果没有传入类型实际参数,编译器就会提出泛型警告。
        实例演示:
public class GenericDemo3 {public static void main(String[] args) {Tool t = new Tool();t.setObject(new Work()); //向Tool类对象中传入Work类对象t.getObject();Utils<Work> utils = new Utils<Work>(); //参数化泛型,为泛型类传入类型实参utils.setObject(new Work()); //向Utils类对象中传入Work类对象// 不再需要强制转换,(Work)utils.getObject();Work w = utils.getObject();}}//泛型类class Utils<QQ> { //创建泛型类,private QQ q;public void setObject(QQ q) {this.q = q;}public QQ getObject() {return q;}}// 泛型前的做法class Tool {private Object obj;public void setObject(Object obj) {this.obj = obj;}public Object getObject() {return obj;}}class Work {}class Stu {}
        代码分析:程序在自定义类中使用了泛型,在工具类Util<>集合中限定了装入Work类型的对象。当类中要操作的引用数据类型不确定的时候,早期定义Object来完成功能扩展,现在定义泛型来完成扩展。
泛型方法
        定义类、接口时没有使用类型形参,但定义方法时定义类型形参也是可以的,即泛型方法。为了让不同方法可以操作不同类型,而且类型还不确定,可以将泛型定义在方法上。
        泛型方法就是在声明方法时定义一个或多个类型形参。
       需要注意的是:泛型方法和泛型类的书写结构不同,泛型方法的泛型参数写在访问修饰符之后。
public class GenericDemo4 {public static void main(String[] args) {Demo d = new Demo(); //定义泛型类对象时,不给定实际类型参数d.show(new Integer(4)); //对象d可以操作多种数据类型d.print("print方法被调用");Demo<String> ds = new Demo<String>(); //定义泛型类对象时,给定实际类型参数ds.show("string");ds.print(4); // 方法上的泛型和对象的泛型无关// ds.show(4); //出现编译错误,需要String类型d.method("method");ds.method("method");}}// 泛型定义在方法上,比定义泛型类更加方便class Demo<T> { // 定义在类上的泛型public void show(T t) // 定义在方法上的泛型,其中T和定义在类上的T相同{System.out.println("show: " + t);}public <Q> void print(Q q) // 定义在方法上的类型Q和T不相同{System.out.println("print: " + q);}public static <W> void method(W w) {System.out.println("method: " + w);}}
        代码分析:可以看到泛型定义在方法上时,调用方法传入的参数是什么类型,在运行时即可表现出什么类型的特征。这种泛型定义比定义泛型类广泛,而且好用。实现了不同方法可以操作不同类型,而且类型还不确定。
       需要注意的是:此外既可以出现定义在类上的泛型,也有定义在方法上的泛型,两者不重复,但书写的格式需要留意。
        与接口、类声明中定义的类型形参不同的是,方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。此外,方法中的泛型参数无须显式地传入实际类型参数,系统依然可以知道类型形参的数据类型,因为编译器根据实参推断类型形参的值,他通常推断出最直接的类型参数。
静态泛型方法
        特殊之处在于,静态方法不可以访问类上定义的泛型,因为只有在类运行时编译器才会确定要运行的是什么类型,也就是“动态的”。对于静态环境而言,不能访问动态变量,这是之前就意识到的问题。
        解决办法:如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。定义在静态方法上的泛型,是可以。
        不管泛型的实际类型参数是什么,他们在运行时总有相同的类(class文件),在内存中只占用一块内存空间。因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。
        泛型定义在方法上时,泛型类型需要写在函数返回值类型的前面,静态修饰符的后面。
        public static <W> void method(W w) {System.out.println("method: " + w);}
       静态泛型方法,就如同上面的method(W w)方法。需要指出的是泛型<W>需要放在static修饰符之后。
泛型接口
        实现接口时,无法知道需要使用的是什么类型,需要在实现类上定义泛型。
//泛型定义在接口上public class GenericDemo5 {public static void main(String[] args) {InterImpl i1 = new InterImpl();i1.show("zhangfeng");InterImpl2<String> i2 = new InterImpl2<String>();i2.show("zhangfeng");InterImpl2<Integer> i3 = new InterImpl2<Integer>();i3.show(4);}}interface Inter<T> {void show(T t);}class InterImpl2<T> implements Inter<T> { // 实现类也使用了泛型public void show(T t) {System.out.println("show2: " + t);}}class InterImpl implements Inter<String> { // 在实现接口时,指定泛型类型public void show(String t) {System.out.println("show1: " + t);}}
        代码分析:在定义接口时,实现了在接口中的泛型;在实际的Java开发中,比较常见的是将泛型定义在类和方法上,定义在接口上的泛型不常见。
泛型限定
       当传入的类型不确定时,可以使用通配符?。使用通配符的好处是可以不同明确传入的类型,这样在使用泛型类或者泛型方法时,提高了扩展性。Collection<?> a 可以与任意参数化的类型匹配,但到底匹配的是什么类型,只有以后才知道。
import java.util.*;public class GenericDemo6 {public static void main(String[] args) {ArrayList<String> al = new ArrayList<String>();al.add("zhang1");al.add("zhang2");al.add("zhang3");show(al);ArrayList<Integer> newal = new ArrayList<Integer>();newal.add(1);newal.add(2);newal.add(3);show2(newal);}// 第二种形式public static void show(ArrayList<?> al) // ?表示类型占位符{Iterator<?> it = al.iterator();while (it.hasNext())System.out.println(it.next());}public static <T> void show2(ArrayList<T> al) // 可以使用类型T{Iterator<T> it = al.iterator();while (it.hasNext())System.out.println(it.next());}}
       代码分析:使用类型占位符?,表示传入的什么类型,使用的就是什么类型。show()和show2()方法在计算结果上,都能够得到正确的结果。区别在于:show2()方法中定义了一个类型T,也就可以在之后的代码中使用到该类型T。相对于类型占位符?,就没有该用途。
        ?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,但弊端在于:不能调用与参数化有关的方法。
        此外还有子类和父类定义泛型的问题:
import java.util.*;public class GenericDemo7 {public static void main(String[] args) {ArrayList<Person> al_person = new ArrayList<Person>();al_person.add(new Person("zhang"));al_person.add(new Person("li"));al_person.add(new Person("zhao"));printEle(al_person);ArrayList<Student> al_student = new ArrayList<Student>();al_student.add(new Student("zhang_stu"));al_student.add(new Student("li_stu"));al_student.add(new Student("zhao_stu"));// printEle(al_student); //编译出现异常,ArrayList<Person> <-- new// ArrayList<Student>()printEle2(al_student); // 能够接受Person类型和其子类型}// 定义泛型,接受Person类型和Person类型的子类型public static void printEle2(ArrayList<? extends Person> list) {Iterator<? extends Person> it = list.iterator();while (it.hasNext()) {System.out.println(it.next().getName());}}public static void printEle(ArrayList<Person> list) {Iterator<Person> it = list.iterator();while (it.hasNext()) {System.out.println(it.next().getName());}}}class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return name;}public String toString() {return "person + " + name;}}class Student extends Person { //Student继承了Person类public Student(String name) {super(name);}}
        代码分析:比较难以理解的是:printEle(al_student); 语句为什么会在编译时出现异常,毕老师在视频中这么说的:ArrayList<Person> <-- newArrayList<Student>()。或者可以这么进行归纳:如果Foo是Bar的一个子类型,而G是具有泛型声明的类或接口,G<Foo>并不是G<Bar>的子类型!比如:List<String>对象并不能被当做List<Object>对象使用,也就是说List<String>并不是List<Object>类的子类。
        由以上出现的问题引出了泛型限定:对于一个范围内的事物,可以通过泛型限定的方式定义。
        泛型限定有两个方向:向上限定和向下限定。
        限定上限:? extends T:可以接受T类型或者T的子类型;ArrayList<? extends Person> list,表示能够接受Person类的元素,以及Person的子类元素。
        限定下限:? super T:可以接受T类型或者T类型的父类型。ArrayList<? super Integer> x = new Arraylist<Number>();,表示能够接受Integer类型,或者其父类型。
泛型应用
import java.util.*;public class GenericDemo8 {public static void main(String[] args) {// TODO Auto-generated method stub// TreeSet(Comparator<? super E> comparator) E代表的是TreeSet中存放的元素类型// TreeSet中存放的是Student类型,传递的比较器是其父类TreeSet<Student> ts = new TreeSet<Student>(new PersonCmp());ts.add(new Student("zhang01"));ts.add(new Student("zhang12"));ts.add(new Student("zhang11"));Iterator<Student> it = ts.iterator();while (it.hasNext()) {System.out.println(it.next().getName());}TreeSet<Worker> ts1 = new TreeSet<Worker>(new PersonCmp());ts1.add(new Worker("zhang-->01"));ts1.add(new Worker("zhang-->12"));ts1.add(new Worker("zhang-->11"));Iterator<Worker> it1 = ts1.iterator();while (it1.hasNext()) {System.out.println(it1.next().getName());}}}class StuCmp implements Comparator<Student> {public int compare(Student s1, Student s2) {return s1.getName().compareTo(s2.getName());}}class WorkCmp implements Comparator<Worker> {public int compare(Worker s1, Worker s2) {return s1.getName().compareTo(s2.getName());}}class PersonCmp implements Comparator<Person> { // 可使用父类型构造比较器public int compare(Person p1, Person p2) {return p1.getName().compareTo(p2.getName());}}class Worker extends Person {public Worker(String name) {super(name);}}
       代码分析:在创建集合TreeSet(Comparator<? super E> comparator)时,其参数可以是集合存入元素或其父类的比较器,以此对集合元素进行比较。在定义TreeSet<Student> ts和TreeSet<Work> ts1时,指定了存入集合元素的类型,但传入的比较器可以是以上两种类型,或其父类型。TreeSet(Comparator<? super E> comparator) E代表的是TreeSet中存放的元素类型。
泛型应用的注意事项
       其一、在定义泛型时,还可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){};
       其二、普通方法、构造方法和静态方法中都可以使用泛型;
       其三、可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但不能用于catch字句中;
       其四、在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开。
0 0
原创粉丝点击