------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的集合,加入三个元素,分别是String和Integer类型的。但是当用迭代器迭代的时候,就会出现错误,因为我们不能吧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>{
}
注意Demo和DemoA的区别,一个是我们在实现接口的时候,就要把泛型类型确定,一个是我们也不知道传入的泛型类型是什么,那么就把子类也泛型化。
通配符 ?
具体格式为:<?>。
就是我们想要任意类型的,就可以加通配符。例如:
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继承了classC,classB继承了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方法传入多个对象。
然后当我们自定义比较器的时候,问题就来了,普通泛型方式的话,我们就得定义两个比较器,分别可以传入A和B 的对象。这里用泛型限定就比较简单。
class Comp implement Comparator<C> {………………Comparator接受的是<?super E>
publicint compare(C p1 , C p2) { }
}
因为A和B都继承C,所以这个比较器就可以传入子类和子类的父类对象。这是只需一个比较器即可。
泛型总结:
1、 泛型,是JDK1.5出现的新特性,用于解决安全问题,是一个安全机制,限定任意类型参数传入。个人理解来说,泛型就是在编译时期,就限定某一集合输入元素的类型,在编译的时候就挡住外面的非法元素。
格式为:类名< 参数类型>
2、 泛型优点:
2.1提高安全性。将运行时期会出现的问题,转义到编译时期,方便与程序员解决问题,让运行时期问题减少,安全性提高。这里注意,泛型只在编译时期有效,运行时期为了提高效率,就会去掉泛型,这里再反射部分可以讲到。
2.2 在迭代中就不用强制类型转换了。
3、自定义泛型,可以定义在类上,定义在函数上,定义在静态函数上。定义在静态函数上时,要注意泛型符号要定义在修饰符后面,返回类型之前。
4、泛型方法和泛型类可以同时存在。
5、通配符? 是接受任意类型参的。但有局限性,接受进来的对象,不定义和参数类型有关的方法,比如,在Collection中接受一个Integer对象,就不能定义Integer的toHex方法。因为我在定义某个函数的时候,接受的是任意类型,在实际应用时,并不知道要传入的具体类型。
6、泛型限定:
6.1 ? extends E :可以接受E类型或者E的子类型。定义上限。
6.2 ? superE :可以接受E类型或者E的父类型。定义下限。
7、泛型的一些注意点:一、泛型的参数类型之间没有继承关系。二、泛型可以指向传统型,传统型也可指向泛型。三、通配符?可以指向其他参数类型的泛型
工具类和一些JDK1.5新特性
两个工具类:Collections和Arrays
一、Collections
这个工具类中方法都是静态的,因为操作的共享数据,就是说,这里面的方法直接类名调用即可。举例来说:
List集合,没有排序,这里面就有给List集合排序的方法,这个方法就是
sort(List l)。这个方法注意的是,方法中是按自然排序的,就是说传入的List集合中的元素都是实现Comparable的,所以当我们自定义类时也要实现这个接口。sort方法也可以接收一个比较器。这样,就完成的List集合排序。
同样对于max方法,也要元素自身具备比较性,实现Comparable接口,也可接受比较器。
binarySearchDemo ( obj) 二分查找,在有序的List集合中,查找目标元素的角标位置。
有的话,返回角标位。没有的话,就返回 ( - (插入点 ) – 1 )。
fill(list,obj)方法,把目标集合中的所有元素,重新换为一个指定元素。
replaceAll(list,oldO1,newO2)
reverse(list)反转。
注意一个重要方法:
1、 reverseOrder()这会返回一个被强行逆转了的自然书序比较器。其重载方法reverseOrder(Comparator 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语句有keySet和entrySet语句可以转为Set,这是就可以使用高级for循环。因为for只能遍历Collection和数组。
2、 可变参数
void method (int…arr); 可变参数就是:类型...名称。
可变参数,实际上就是将数组参数简写,这样我们就不用每次都手动的建立数组对象,只要将操作的元素作为参数传递即可,然后隐式的将这些参数封装成数组。
使用的时候要注意:可变参数一定要定义在参数列表最后面。
3、 静态导入
imoprt static java.util.Arrays.*;导入的是Arrays这个类中所有静态成员。
静态导入,就导入某个类中的静态成员。这样就在程序中就可以省略方法前的类名,直接用方法名即可。但如果当类名重名时,血药指定包名;当方法重名时,指定具备所属的对象或者类。
比如某个类中导入一个Arrays这个工具类,在主函数中使用toString方法,这个时候,主函数中就有两个toString方法,一个是继承的Object的,一个是静态导入Arrays工具类中的,调用这个方法,就必须把前面的类名加上。