下面我总结了集合、泛型、数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获。
1、List ,List> 与 List
说实话,我敢保证很多人是不知道 List, List> 与 List
1、我们先来看看 List 与 List
很多可能觉得 List
List
与
List list;
这两种定义方法是一模一样的,然而他们是不一样的。看下面一段代码
List t1 =newArrayList<>();// 编译通过Listt2 = t1;//编译失败List t3 = t1;
t1 可以赋给 t2, 但是 t1 不能赋给 t3,会抛出如下异常
从这里可以看出
List list;
与
List
是有区别的,List 变量可以接受任何泛型的变量,而 List 则不可以。
进群:697699179可以获取Java各类入门学习资料!
这是我的微信公众号【编程study】各位大佬有空可以关注下,每天更新Java学习方法,感谢!
学习中遇到问题有不明白的地方,推荐加小编Java学习群:697699179内有视频教程 ,直播课程 ,等学习资料,期待你的加入
2、我们在看看 Lis> 有什么需要注意的地方:
看下面一段代码:
List t1 =newArrayList<>();List t2 = t1;// 编译通过t2.remove(0); t2.clear();// 编译不通过2.add(newObject());
List 是一个泛型,在没有赋值之前,是可以接受任何集合的赋值的,我想这点大家都知道,但是请注意, 赋值之后就不能往里面添加元素了 ,提示如下错误:
所以 List> 一般用来作为参数来接受外部的集合,或者返回一个不知道具体元素的集合。
List 与 List>, List
2、 extends T> 与 super T>你真的懂吗?
我们知道泛型 List
为了能够放置多种类型,于是有了 与 ,下面先说一些你 可能 原本就知道的知识:
1、 对于 extends T> a ,a 这个变量可以接受 T 及其 T 子类的集合,上界为 T,并且从 a 取出来的类型都会被强制转换为 T。重点看下面一个例子:
注意:我们先约定 Cat(猫) 继承自 Animal(动物),RedCat(黑猫) 继承自 Cat
List animals =newArrayList<>();List cats =newArrayList<>();List redCats =newArrayList<>();// 可以通过编译List extendsCat = redCats;// 不能通过编译,因为只能接受 Cat 及其子类的集合extendsCat = animals;// 重点注意:下面三行都不能通过编译extendsCat.add(newAnimal()); extendsCat.add(newCat()); extendsCat.add(newRedCat());// 重点注意:可以通过编译extendsCat.add(null);
注意,最需要注意的是,就是 不能向里面添加除null之外的其他所有元素 ,这个和 List 有点类似。
2、现在说说 ,它和 有点相反。 对于 super T> a ,a 这个变量可以接受 T 及其 T 父类的集合,下界为 T,并且从 a 取出来的类型都会被强制转换为 Object。重点看下面一个例子:
List animals =newArrayList<>();List cats =newArrayList<>();List redCats =newArrayList<>();// 可以通过编译List superCat = animals;// 不能通过编译,因为只能接受 Cat 及其父类的集合superCat = redCats;// 重点注意:不能通过编译,只能添加 Cat 及其 Cat 的子类superCat.add(newAnimal());// 重点注意,可以通过编译superCat.add(newCat()); superCat.add(newRedCat()); superCat.add(null);
注意,<? super T>最需要注意的是,在 虽然可以接受 T 及其父类的赋值,但是只能向里面添加 T 及其 T 的子类 。
总结
1、List a ,可以把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过需要注意的是, 不能向 a 添加任何除 null 外是元素 。
2、List a ,可以把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过需要注意的是,可以向 a 添加元素, 但添加的只能是 T 及其子类元素 。
3、泛型与重载
我们先来看一道题,你觉得下面这道题能够 编译 通过吗?
publicclassGernerTypes{publicstaticvoidmethod(List
答是编译不通过。
两个方法的参数不同,为什么会重载不通过呢?
实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为 原生类型 了,并且在相应的地方插入了强制转换的代码。为了方便理解,可以看下面的一段代码例子:
// 源码publicstaticvoidmain(String[] args){ Listlist=newArrayList<>();list.add(1); System.out.println(list.get(0)); }
编译之后泛型就不存在了,并且在相应的地方插入了强制转换的代码,编译之后,我们反编译的代码如下:
// 反编译之后的代码publicstaticvoidmain(String[] args){ Listlist=newArrayList();list.add(1); System.out.println((Integer)list.get(0)); }
这种 编译之后泛型就不存在了,并且在相应的地方插入了强制转换代码的机制我们也称之为 擦除 。
所以上面的两个方法,看似参数不一样,但是经过编译擦出之后,他们的参数就是一样的了,所以编译不通过。
4、数组与集合相互转换时需要注意的点
1、数组转集合
大家先看一个例子吧,
publicstaticvoidmain(String[] args) {String[] arr = {"one","two","three"};// 数组转换成集合List list = Arrays.asList(arr);// 向集合添加元素:编译正常,但运行时抛出了异常list.add("four"); }
向集合添加元素抛出了如下异常:
问题来了,向集合添加元素为啥会抛出异常呢??
我们先来看一下 Arrays.asList(arr) 方法究竟返回了什么?
源码如下:
返回的明明是 ArrayList 啊,为啥就不能添加元素呢??
实际上,此 ArrayList 非彼 ArrayList,这个返回的 ArrayList 实际上是 Arrays 的一个 内部类 。该内部类也是十分简单,和真实的那个 ArrayList 没得比,部分源码如下:
而且这个假的 ArrayList 是直接 引用原数组的,不然你看它的构造器(第二条画线)
也就是说,ArrayList 内部是直接引用 arr 数组,你对 arr 数组进行改变,也会同时改变到 list 集合。
下面的代码证明这一点
publicstaticvoidmain(String[] args) {String[] arr = {"one","two","three"};// 数组转换成集合List list = Arrays.asList(arr);// 修改 arrarr[0] ="0";//打印看看System.out.println(list.get(0)); }
打印结果是 “0”。
所以,我们向 list 添加元素肯定失败,因为 arr 数组的长度了 3 ,本来就有 3 个元素了,你在向里面添加第四个元素,肯定是不行的。
所以,在把数组转换为集合的过程中,需要特别注意。
建议大家这样转换比较安全
List list = new ArrayList<>(Arrays.asList(arr));
2、集合转数组
集合转换为数组相对比较不苛刻,我就不拉很多源码来进行分析了,我只简单说下几个需要注意的地方。例如对于下面这个转换:
// 集合大小为 sizeList list =newArrayList<>();// 长度为 n 的数组String[] arr =newString[n];// 进行转换list.toArray(arr);
1、如果数组长度比集合小:由于 arr 的长度不够,所以集合里的元素不会赋给 arr,而且自己再重新创建一个新数组反回去。
2、如果数组长度不小于集合:此时 arr 的长度够了,所以集合里的元素直接复制给 arr 数组,不会重新创建一个新的元素。
一览源码:
public T[] toArray(T[] a) {if(a.length size) a[size] =null;returna; }
以上这些陷进相信有不少人是不知道了,我把它总结整理了出来,如果大家看完觉得有收获