我们写了一个集合,并且遍历这个集合,在代码上来看 并没有有任何的错误但是在运行的时候,出现了ClassCastException的异常(类型转换失败)。
因为在刚刚存储的时候,我们存储的数据类型是String类型Integer两种数据类型,而整数10是被原来的整数进行了自动装箱,而不是我们以为的int类型的整数,而我们在遍历的时候,我们都将这些数据当成的String来处理,
而String和Integer都是不同类型对象,彼此之间进行赋值,就肯定是不兼容的,所以出现了类型转换失败(具体回忆我们的多态),但是我们在编译代码的时候,没有给予我们任何的错误提示,这样是是不好的,因为在编译器报错我们可以解决掉,而在运行期报错 这个代码很有可能已经进入到的生产环节。
怎么解决?
回忆下我们的数组:
String [] arr = new String[3];
arr[0] = “hello”;
arr[1] = “toobug”;
arr[2] = 10;
集合其实也模仿了数组一样的做法,在创建集合对象的时候,就可以预知或者明确这个集合的限定数据类型,而这种技术被称之为:泛型
其实在很久之前,我们的集合统一使用Object来接收,但是Object并不能同时兼容多个数据类型 所以还是会造成类型转换失败,所以后面在JDK5的时候推出了泛型
JDK5的新特性:泛型、自动拆装箱、增强for、静态导入、可变参数、枚举
泛型的作用:是一种把类型明确的工作推前到了创建对象或者调用方法的时候再去明确的特殊类型,把类型进行参数化,可以当成参数传递
格式:
<数据类型>:只能是引用数据类型
堆内存的数据可以不用泛型进行限定,JDK7提供的泛型推断机制
泛型的好处:
1.把运行时候的错误提前到编译器进行处理
2.避免了强制类型转换的问题
3.优化了程序的结构设计,解决了黄色警告线的问题
泛型通配:
?:任意类型,只要是Object及子类都可以(任意类)
? extends E:向下限定,数据必须是E 或者E的子类
? super E:向上限定。数据必须是E 或者E的父类
泛型的使用场景:集合、类、接口、方法
方法:public void show(T t) {}
接口:public interface Inter
情况1:在接口实现的时候,就限定所实现接口的数据类型是什么,这个时候不需要在继续限定所实现的具体类
*/
/*public class InterImpl implements Inter{
情况2:如果此时还不明确接口的数据类型,那么就必须限定具体的数据类型
*/
public class InterImpl implements Inter {
类:
public class ClassGeneric {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
集合:ArrayList arr=new ArrayList();
Collection:集合的根接口
| List:具备的特点:有序,元素可以重复
| ArrayList 底层实现的是一个Object的数组,特点 查询快 增删慢 线程不安全
| LinkedList 底层实现的链表,特点 查询慢 增删快 线程不安全
| Vector(淘汰)
|Set
| HashSet 底层是哈希表进行实现的,特点,存取速度快
HashSet存储原理:
往HashSet存储元素的时候,先会在HashSet里去调用元素的hashCode,得到元素的哈希码值,然后通过这个哈希码值就可以计算出元素在哈希表中的位置
HashSet:他不能够保证元素的迭代顺序是一致的,只能保证元素存储在哈希表的位置是固定的
情况1:如果根据元素的哈希码算出的位置目前没有存储元素的话,那么该元素就直接存储到哈希表当中
情况2:如果元素的哈希码值计算出的结果已经存在元素,那么就会调用该元素的equals方法来进行对比,如果对比返回的结果为true,那么就不会进行添加,否则继续写入到哈希表
LinkedHashSet:底层的实现是哈希表和链表组成
哈希表:保证元素唯一性
链表:保证元素的迭代顺序一致
TreeSet:能够对元素进行自然排序
TreeSet特点:排序和唯一