Java基础——jdk1.5新特性及集合与数组的转换
jdk1.5的新特性
泛型是一种泛泛的类型,泛指某一种类型,是在创建对象或者调用方法或者实现接口才明确的类型。
泛型好处提高安全性(将运行期的类型转换错误提前到编译期),省去强转的麻烦。
泛型使用注意事项
- <>中放的必须是引用数据类型;
- 前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性菱形泛型) (建议后面的也写全);
- 在创建对象或者调用方法或者实现接口明确泛型的时候,尽量不要明确成Object,因为这样做没有意义(泛型的由来就是由Object的转型的问题得到的);
- 泛型是JDK1.5出现的。
代码演示:
public static void demo1() {
ArrayList list = new ArrayList();
list.add(110);//添加一个Integer类型的 110,在没有使用泛型的时候 list可以添加任意的引用类型
list.add(true); //添加一个Boolean类型的 true
list.add(new Person("张三", 23)); //添加一个Person
Iterator it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next()); //将集合里面的元素打印出来
}
}
public static void demo2() {
ArrayList list = new ArrayList();
list.add(110);
list.add(true);
list.add(new Person("张三", 23));
Iterator it = list.iterator();
while(it.hasNext()) {
//如果我想使用Person的特有功能 需要强制转换成Person类型
Person p = (Person)it.next();//编译不报错, 运行的时候立刻报错,因为存储的 110 和 true 并不是Person类型 单却强制转换成Person类型 会报出类型转换错误
System.out.println(p.getName() + "..." + p.getAge());
}
}
//ArrayList存储字符串并遍历泛型版
public static void demo3() {
ArrayList<Person> list = new ArrayList<>(); //<Person>规定 list对象里面必须存储Person类型的元素,存储其他类型的编译就会报错
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
list.add(new Person("赵六", 26));
Iterator<Person> it = list.iterator(); //获取的迭代器也是Person类型的泛型
while(it.hasNext()) {
Person p = it.next(); //it.next()获取到的元素 就不再是Object了,泛型里面规定的类型 it.next()就获取什么样类型的元素,所以就不用强制类型转换了
System.out.println(p.getName() + "..." + p.getAge());
}
}
public static void main(String[] args) {
//ArrayList<int> list = new ArrayList<int>(); //编译报错 <>中放的必须是引用数据类型
//ArrayList<Object> list = new ArrayList<Person>(); //编译报错 集合的泛型要保证前后的数据类型一致
ArrayList<Object> list = new ArrayList<>();//泛型最好不要定义成Object,没有意义,1.7版本的新特性,菱形泛型,后面的<>可以不用填写
list.add("aaa");
list.add(true);
}
泛型类: public class 类名<泛型类型1,泛型类型2,....>{}
public class Worker<QQ> {
public void show(QQ qq){
System.out.println(qq);
}
}
public class Worker<QQ,MM,DD,RR> { //可以写一个泛型 也可以写多个
public void show(QQ qq){
System.out.println(qq);
}
}
泛型非静态方法: public <泛型类型> 返回类型 方法名(泛型类型 变量名){}
代码演示:
public class Demo1_Generic {
public static void main(String[] args) {
/*Tool t = new Tool();
t.show("abc"); //调用show(String s)方法 传入一个"abc"
t.show(100);*/ //调用show(Integer i)方法 传入一个100
//如果想继续接着调用一个show(Student s)方法, 就需要继续修改Tool类,再继续编写一个show(Student s)方法 才能调用
//如果想继续接着调用一个show(Animal a)方法, 就需要继续修改Tool类,再继续编写一个show(Animal a)方法 才能调用
//如果想继续接着调用一个show(Worker w)方法, 就需要继续修改Tool类,再继续编写一个show(Worker w)方法 才能调用
//这样修改 将无穷无尽,所以用下面的泛型类改进代码
/*Tool1<String> t = new Tool1<>(); //想调用show方法 传入一个"abc" 那么就创建一个泛型为String的对象
t.show("abc");
Tool1<Integer> t1 = new Tool1<>(); //想调用show方法 传入一个100 那么就创建一个泛型为Integer的对象
t1.show(100);
Tool1<Student> t2 = new Tool1<>(); //想调用show方法 传入一个Student 那么就创建一个泛型为Student的对象
t2.show(new Student());*/
//如果想接着调用show方法 传入一个Animal 就又要创建一个泛型为Animal的对象 ,每次都要创建一个新的对象 浪费空间 所以用下面的泛型方法改进
Tool2 t = new Tool2();
t.show("abc"); //调用show方法的时候就明确了QQ是String类型
t.show(100);//调用show方法的时候就明确了QQ是Integer类型
t.show(new Student());//调用show方法的时候就明确了QQ是Student类型
//想传什么样的类型都可以 而且代码非常的简单
}
}
public class Tool {
public void show(String s) {
System.out.println(s);
}
public void show(Integer i) {
System.out.println(i);
}
}
public class Tool1<QQ> {
public void show(QQ qq){
System.out.println(qq);
}
}
public class Tool2 {
public <QQ>void show(QQ s){
System.out.println(s);
}
}
泛型静态方法: public static <泛型类型> 返回类型 方法名(泛型类型 变量名){}
public class Tools<Q> {
/*public static void show(Q q) { //编译报错,因为静态的 优先于对象而存在
System.out.println(q);
}*/
public static<W> void print(W w) { //静态方法必须声明自己的泛型
System.out.println(w);
}
}
泛型接口:public interface 接口名<泛型类型>
子类在实现父接口的时候可以明确泛型,也可以直接沿用父类的泛型
代码演示:
interface Inter<T> {
public void show(T t);
}
class Demo implements Inter<String> { //子类在实现父接口的时候 可以明确泛型 明确成了String类型
@Override
public void show(String t) {
System.out.println(t);
}
}
class Demo<T> implements Inter<T> { //也可以直接沿用父类的泛型 T
@Override
public void show(T t) {
System.out.println(t);
}
}
泛型-通配符:
A:泛型通配符<?>任意类型
List<?> list = new ArrayList<Integer>(); //当右边的泛型是不确定时,左边可以指定为?
B:<? extends E >明确泛型的时候明确成E或者E的儿子
代码演示:
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("张三", 23));
list1.add(new Person("李四", 24));
list1.add(new Person("王五", 25));
ArrayList<Student> list2 = new ArrayList<>();
list2.add(new Student("赵六", 26));
list2.add(new Student("周七", 27));
//boolean addAll(Collection<? extends E> c)
list1.addAll(list2); //Student 是Person的儿子 所以 可以添加成功
System.out.println(list1);
}
C:<? super E >明确泛型的时候明确成 E或者E的父类
增强for循环
为了简化数组或者集合的遍历而出现的(JDK1.5出现的)
for(集合或者数组里面元素的数据类型 变量名 : 数组或者集合){
直接使用变量名就ok;
}
代码演示:
public static void demo1() {
int[] arr = {11,22,33,44,55};
for (int i : arr) { //增强for循环可以遍历数组
//增强for循环在遍历数组或者集合的时候,优点:代码的书写确实简单了,但缺点是遍历时候没有索引,如果想用索引的时候,就请用普通for循环
System.out.println(i);
}
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (String string : list) { //增强for循环可以遍历集合
System.out.println(string);
}
}
遍历过程中能否删除元素总结:
1)普通for循环在遍历的时候(只能遍历List体系的集合),可以删除,但是在删除过程中索引会改变, size也会发生改变,所以容易出现漏删的情况。
解决:索引--,代码片段如下:
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d");
for(int i = 0; i < list.size(); i++) {
if("b".equals(list.get(i))) {
list.remove(i--); //通过索引删除元素
}
}
2)迭代器在遍历集合的时候 Iterator(可以遍历任何单列集合),不可以通过集合操作元素,否则会报并发修改异常。只能通过迭代器自身来对元素进行操作,如果是删除:可以用Iterator 也可以用ListIterator 因为他们里面都有remove();如果是添加:只能使用ListIterator,因为只有ListIterator里面有add()
代码片段如下:
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d");
Iterator<String> it = list.iterator();
while(it.hasNext()) {
if("b".equals(it.next())) {
//list.remove("b"); //不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常
it.remove(); //让迭代器自己来删除元素
}
}
3)增强for循环遍历集合的时候(可以遍历任何单列集合),由于底层就是调用的迭代器,所以不能删除,只能遍历。代码片段如下:
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d");
for (String string : list) {
if("b".equals(string)) {
list.remove("b"); //运行报错,并发修改异常,增强for循环底层用的是迭代器 所以不能在迭代器过程中让集合来删除元素,如果用迭代器自己来删除 ,哪有迭代器来让你使用呢?,在底层呢 你又看不到
}
}
System.out.println(list);
可变参数:
格式:修饰符 返回值类型 方法名(数据类型 ... 变量名){}
注意:
- 可变参数是JDK1.5后出现的新特性
- 可变参数其实是一个数组,所以调用带有可变参数的方法的时候,可以传入一个相同类型的数组
- 有多个参数的时候,可变参数放到最后
代码演示:
public class Demo3_ChangeableArgs {
public static void main(String[] args) {
int[] arr = {11,22,33,44,55};
print(); //调用的时候 可以不传入参数
print(arr); //调用的时候 可以传入一个数组,因为可变参数的底层就是使用的数组
print(11,22,55); //调用的时候 可以传入多个参数
}
public static void print(int ... arr) { //可变参数其实是一个数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void show(String s , int ... arr) { //有多个参数的时候,可变参数放到最后
}
/*
public static void method(int ... arr , String s) { //编译报错 有多个参数的时候,可变参数放到最后
}*/
}
//可变参数的应用
public class Demo4_ChangeableArgs {
public static void main(String[] args) {
//求两个数的最大值,我需要定义一个,含两个参数的方法max(int a , int b)
int big = max(5,6);
//假如现在需求变了,我想求三个数的最大值,那么我需要定义一个含三个参数的方法 max(int a , int b , int c)
int big1 = max(5,6,10);
//假如现在需求又变了,我想求四个数的最大值,难道我就再继续定义一个含有四个参数的方法 max(int a , int b , int c , int d)吗? 这样太麻烦了, 解决方案就是 用可变参数
int big2 = max(11,432,7,87,23);
}
//求两个数的最大值
public static int max(int a , int b) {
return a>b?a:b;
}
//求三个数的最大值
public static int max(int a , int b , int c) {
return a>b ? (a>c?a:c) : (b>c?b:c);
}
//求任意个数的最大值
public static void max(int ... arr) { //可变参数其实是一个数组
int a = arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i]> a){
a = arr[i]
}
}
return a;
}
}
集合和数组之间的转换
Arrays工具类的asList(T ... t):将数组转成集合(该方法用的可变参数)
注意:
1)返回一个长度固定的集合,可以改和查,不能增和删,也就是改变其长度的操作都不可以做。
2)将数组转换成集合,数组必须是引用数据类型。
Collection中toArray(T[] a)泛型版的集合转数组:
T[] 明确成什么样的数组 toArray(T[] a)方法就返回什么类型的数组
注意:当集合转换数组时,数组长度如果是小于等于集合的size时,转换后的数组长度等于集合的size;如果数组的长度大于了size,分配的数组长度就和你指定的长度一样。
好了今天就先说到这了,想了解更多学习知识,请关注微信公众号“阿Q说”,获取更多学习资料吧!你也可以后台留言说出你的疑惑,阿Q将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。