黑马程序员_泛型概述和工具类

来源:互联网 发布:java 敏感词检测 编辑:程序博客网 时间:2024/03/29 23:45

 

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

泛型概述和工具类

一、泛型的理解

先看一个程序:

classTest{

         public static void main(String[] args) {

                   ArrayList al = newArrayList();

                   al.add("abc2");

                   al.add("abc1");

                   al.add(3);

                   Iterator it = al.iterator();

                   while(it.hasNext()){

                            String s =(String)it.next();

                            System.out.println(s);

                   }

         }

}

         上面程序中,我们建立一个ArrayList的集合,加入三个元素,分别是StringInteger类型的。但是当用迭代器迭代的时候,就会出现错误,因为我们不能吧Integer强制转换为String类型。这是在运行时期出现的错误,这样程序安全性就降低了。

泛型,是JDK1.5出现的新特性,用于解决安全问题,是一个安全机制。

个人理解来说,泛型就是在编译时期,就限定某一集合输入元素的类型,在编译的时候就挡住外面的非法元素。泛型的格式:通过< >来定义要操作的引用数据类型。

                                     ArrayList<String> al  =  new  ArrayList<String>();

其实< > 就是接受类型的,当使用集合时,将集合要存储的数据类型作为参数传递到<>中就好。

这样就限定了ArrayList集合所应有的元素是什么类型的。这样在迭代器迭代的时候,就可以不用强制类型转换了。

对于我们应用来说,需要定义泛型的,当我们在查API文档的时,只要见到 < >,就要定义泛型,泛型中的类型参数,就是我们需要传入集合中的数据类型。

总结来说,有两点好处:

1、             将运行时期会出现的问题,转义到编译时期,方便与程序员解决问题,让运行时期问题减少,安全性提高。

2、             在迭代过程中,减少了强制类型转换这一步骤。

 

二、泛型的应用

在集合中,使用泛型可以省去好多的强制类型装换。集合,迭代器,比较方法和比较器,都使用泛型。我们可以自定义泛型类

1、泛型定义在类上,看示例:

         class Utils<Q>{

         private Q q ;

         public void setObject(Q q){

                   this.q = q;

         }

         public Q getObject(){

                   return q;

         }

}

         当我们在定义一个类时,这个类需要引用的数据类型不确定,我们就可以引入泛型,这样就可以接入不同的类型,从而提高程序拓展性。

         同时,我们看到的是,类中的方法也有泛型,但这个泛型是类上的泛型。简单来说,泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作具体的类型之后,所要操作的类型就已经固定了。

2、  泛型定义在方法上。为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。示例程序:

classUtils{

  public <T> void printA(T t){

  }

  public <T> void showB(T t){

  }

}

上述程序,类上并没有定义泛型,在方法上定义泛型,两个方法中的泛型是不同的,因为只在本方法中有效。但有一点要注意,定义泛型后,因为不能明确具体的操作类型,所以对相关我们需要的类型的具体方法就不能定义,这也是缺点吧。

当然,泛型类和泛型方法可以同时存在。

3、  静态泛型方法

静态泛型方法不可以访问类上定义的泛型,类上定义的泛型,通常是在建立对象的时候调用的,先有类后又泛型,所以这个不可以。但如果静态方法操作的应用数据类型不确定时,就可以将泛型定义在静态方法上,示例:

                     publicstatic  <T>  void method (T t );

静态泛型方法格式是固定的,所以,要注意的是,泛型必须定义在修饰符之后,返回类型之前。

4、  泛型定义在接口上

基本就是和定义在类上一致。看个示例把:

interfaceInter<T>{

void show(T t);

}

classDemo implements Inter<String>{

}

classDemoA<T> implements Inter<T>{

}

注意DemoDemoA的区别,一个是我们在实现接口的时候,就要把泛型类型确定,一个是我们也不知道传入的泛型类型是什么,那么就把子类也泛型化。

 

通配符 ?

具体格式为:<?>

就是我们想要任意类型的,就可以加通配符。例如:

public static  void method(ArrayList<?>  al) {语句 }

上面就是说,我们在一个函数中,可以接受一个任意类型的集合进来。这样提升了程序的拓展性。但和我们上面定义的泛型函数和类的不同是,我们只能操作通用的方法,不能使用关于参数类型的方法。如果我们泛型中定义的是<T>,这样我们就可以操作这个类型,比如建立这个类型的引用。

 

泛型还要注意,就是会所参数类型之间没有继承关系,比如:

ArrayList<Object>   al =  new  ArrayList<String>();

这是程序时不成立的,因为al我们要装的是Object,可以是String,可以是Integer,但是建立的对象缺只能传入String。而且上式把参数类型转换过来也是不成立的,而且更好解释,你要装的是String类型,缺建立一个可以装任意类型的对象,显然是不成立的。

泛型也要注意,泛型接受传统类型,传统类型就收泛型。传统类型就是没有泛型。因为泛型是后来出现的,但是要兼顾以前的版本,所以上述描述是成立的。示例:

ArrayList<Object>   al =  new  ArrayList();

ArrayList al  =  new ArrayList<Object> ();

 

这是要看个程序:

Vector v1 = new Vector<String>();

Vector<Object>  v =  v1;

仔细看程序,其实我们在想的时候,脑袋里面是在执行程序。这段代码是没有错的,可以在eclipse中应用一下,是没有问题的。上面的程序里面,其实是把Vector 的引用v1传给了v,在编译上没有问题,运行商也没有问题。

 

 

泛型限定

先看应用实例:

public static  void method(ArrayList<? extendsE>  al) {语句 }

泛型限定,就是把我需要的类型,限定在一个范围之内,既不是全部接收,也不是只接受一个,而是接收一定范围内的类型。

1、? extends E:可以接受E类型或者E的子类型。定义上限。

2、? super E  :可以接受E类型或者E的父类型。定义下限。

具体使用方式其实和普通泛型是一样的,只是普通泛型只能有一种类型,而泛型限定可以有多重类型。这打打提高的程序的拓展性。举例来说:

一个classA继承了classCclassB继承了classC

class A   extends C {

         语句。

}

class B   extends C {

         语句。

}

我们想把A的对个对象传入TreeSet集合,泛型方式是:

TreeSet<A>  t1 = TreeSet<A>();

t1.add()……………………add方法传入多个对象。

同样对于B

TreeSet<B>  t1 = TreeSet<B>();

t1.add()……………………add方法传入多个对象。

然后当我们自定义比较器的时候,问题就来了,普通泛型方式的话,我们就得定义两个比较器,分别可以传入AB的对象。这里用泛型限定就比较简单。

class Comp implement Comparator<C> {………………Comparator接受的是<?super E>

         publicint compare(C p1 , C  p2) { }

}

因为AB都继承C,所以这个比较器就可以传入子类和子类的父类对象。这是只需一个比较器即可。

 

 

 

 

 

 

泛型总结:

1、  泛型,是JDK1.5出现的新特性,用于解决安全问题,是一个安全机制,限定任意类型参数传入。个人理解来说,泛型就是在编译时期,就限定某一集合输入元素的类型,在编译的时候就挡住外面的非法元素。

格式为:类名<参数类型>

2、  泛型优点:

2.1提高安全性。将运行时期会出现的问题,转义到编译时期,方便与程序员解决问题,让运行时期问题减少,安全性提高。这里注意,泛型只在编译时期有效,运行时期为了提高效率,就会去掉泛型,这里再反射部分可以讲到。

2.2 在迭代中就不用强制类型转换了。

3、自定义泛型,可以定义在类上,定义在函数上,定义在静态函数上。定义在静态函数上时,要注意泛型符号要定义在修饰符后面,返回类型之前。

4、泛型方法和泛型类可以同时存在。

5、通配符?  是接受任意类型参的。但有局限性,接受进来的对象,不定义和参数类型有关的方法,比如,在Collection中接受一个Integer对象,就不能定义IntegertoHex方法。因为我在定义某个函数的时候,接受的是任意类型,在实际应用时,并不知道要传入的具体类型。

6、泛型限定:

         6.1 extends E:可以接受E类型或者E的子类型。定义上限。

         6.2 superE  :可以接受E类型或者E的父类型。定义下限。

7、泛型的一些注意点:一、泛型的参数类型之间没有继承关系。二、泛型可以指向传统型,传统型也可指向泛型。三、通配符?可以指向其他参数类型的泛型

 

 

工具类和一些JDK1.5新特性

两个工具类:CollectionsArrays

一、Collections

这个工具类中方法都是静态的,因为操作的共享数据,就是说,这里面的方法直接类名调用即可。举例来说:

List集合,没有排序,这里面就有给List集合排序的方法,这个方法就是

sortList l)。这个方法注意的是,方法中是按自然排序的,就是说传入的List集合中的元素都是实现Comparable的,所以当我们自定义类时也要实现这个接口。sort方法也可以接收一个比较器。这样,就完成的List集合排序。

同样对于max方法,也要元素自身具备比较性,实现Comparable接口,也可接受比较器。

binarySearchDemo ( obj)二分查找,在有序的List集合中,查找目标元素的角标位置。

有的话,返回角标位。没有的话,就返回 ( - (插入点 ) – 1 )

filllistobj方法,把目标集合中的所有元素,重新换为一个指定元素。

replaceAlllistoldO1newO2

reverselist反转。

 

注意一个重要方法:

1、  reverseOrder()这会返回一个被强行逆转了的自然书序比较器。其重载方法reverseOrderComparator cmp),这个会返回一个强行逆转的自定义比较器。

2、  synchronizedList  我们集合一般使用都是不同步的,这个方法会给我们返回一个同步的集合。

 

二、Arrays

同样也是都是静态方法,用于操作数组的工具类。

数组变集合

其中一个重要方法:asList(数组),这个方法可以把数组转变成List集合

把数组编程集合的好处:

         可以使用集合的思想和方法来操作数组。例如,查找一个元素,集合就方便,数组得遍历。

注意,将数组编程集合,不可以使用集合的增删方法,因为数组的长度是固定的。

如果数组中的元素都是对象,那么变成集合时,数组中的元素会直接转变为集合中的元素。

如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在

 

         集合变数组:

         Collection中的toArray(数组)方法。这个方法注意的是:

1、             当指定类型数组长度小于集合的size,那么该方法内部会创建一个新的数组,长度为集合的size。当指定类型数组长度大于集合的size,那么就不会创建新数组,而是使用传递进来的数组,空位null填入。

2、             为什么要集合变数组,是为了限定对元素的操作。

JDK1.5的一些新特性:

1、  高级for循环

格式:for(数据类型变量名:被遍历的集合(Collection)或数组

           {

                    执行语句;

           }

底层原理其实还是迭代器。变量名在遍历过程中,是不断指向新对象,就是说,比如遍历一个list集合,变量名会从一开始指向第一个元素,然后在循环就指向下一个元素。对集合遍历,只能获取元素,但是不能对集合操作。而迭代器除了遍历,在遍历过程中可以对集合元素进行操作。

 

传统for和高级for有什么区别呢?

高级for有一个局限性,必须要一个遍历的目标。

传统for循环,怎可以定义角标,而且遍历数组最好使用传统for循环。

 

高级for循环,不能直接用于Map语句,但因为Map语句有keySetentrySet语句可以转为Set,这是就可以使用高级for循环。因为for只能遍历Collection和数组

2、  可变参数

void method (int…arr); 可变参数就是:类型...名称

可变参数,实际上就是将数组参数简写,这样我们就不用每次都手动的建立数组对象,只要将操作的元素作为参数传递即可,然后隐式的将这些参数封装成数组。

使用的时候要注意:可变参数一定要定义在参数列表最后面。

3、  静态导入

imoprt  static  java.util.Arrays.*;导入的是Arrays这个类中所有静态成员。

静态导入,就导入某个类中的静态成员。这样就在程序中就可以省略方法前的类名,直接用方法名即可。但如果当类名重名时,血药指定包名;当方法重名时,指定具备所属的对象或者类。

比如某个类中导入一个Arrays这个工具类,在主函数中使用toString方法,这个时候,主函数中就有两个toString方法,一个是继承的Object的,一个是静态导入Arrays工具类中的,调用这个方法,就必须把前面的类名加上。

 

 

 

 

0 0