第1章 集合 1.1 集合介绍 1.2 ArrayList集合存储元素 1.3 集合的继承实现关系 1.4 Collection 接口概述 1.5 Collection 接口的基本方法 第2章 Iterator迭代器 2.1 概述 2.2 Iterator迭代方式的代码 2.3 集合元素的向下转型 第3章 增强for循环 第4章 泛型 4.1 泛型的引入 4.2 定义与使用 4.2.1 含有泛型的类 4.2.2 含有泛型的方法 4.2.3 含有泛型的接口 4.3 使用泛型的优势 4.4 泛型通配符 4.5 泛型的限定
1)add,已经演示过,不赘述。 2)clear():移除 Collection中的所有元素
import java.util.ArrayList; import java.util.Collection; /* * Collection接口中的方法 * 是集合中所有实现类必须拥有的方法 * 使用Collection接口的实现类,进行程序的演示 * ArrayList implenments List * List extends Collection * 方法的执行,都是实现类的重写方法 */ public class CollectionDemo { public static void main(String[] args) { function(); } /* * Collection接口的方法 void clear():清空集合中的所有元素 */ public static void function() { //接口多态的方式调用 Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("dda"); System.out.println(coll);//输出结果为:[abc, dda] 可看出为有序集合 coll.clear(); System.out.println(coll);//输出结果为:[] coll.add("123"); coll.add("345"); System.out.println(coll);//输出结果为:[123, 345] //由此可见,clear清楚的是集合中的元素,而不是容器本身 } }3)contains(Object o):如果此collection包含指定元素,则返回true
import java.util.ArrayList; import java.util.Collection; /* * Collection接口中的方法 * boolean contains (Object o):判断对象是否在集合里,在则返回true * 方法参数为Object类型,因为不知道该集合里是什么类型的对象,所以若是Object,则什么都可以传 */ public class CollectionDemo1 { public static void main(String[] args) { function(); } public static void function() { //接口多态的方式调用 Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("my friend"); coll.add("money"); coll.add("birthday"); coll.add("123"); boolean b = coll.contains("Money"); boolean b1 = coll.contains(123); System.out.println(b);//输出结果为:false 必须完全一样 System.out.println(b1);//输出结果为:false 数据类型不同,一个是String 一个是Integer } }4)remove(Object o):移除集合中指定的元素
import java.util.ArrayList; import java.util.Collection; public class CollectionDemo3 { public static void main(String[] args) { function(); } /* * Collection接口方法 * boolean remove(Object o)移除集合中指定的元素 */ private static void function(){ Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("money"); coll.add("itcast"); coll.add("itheima"); coll.add("money"); coll.add("123"); System.out.println(coll);//输出结果为:[abc, money, itcast, itheima, money, 123] boolean b = coll.remove("money"); System.out.println(b);//输出结果为:true System.out.println(coll); //输出结果为:[abc, itcast, itheima, money, 123] 有两个相同元素时,删除第一个遇到的元素 boolean b1 = coll.remove("Money"); System.out.println(b1);//输出结果为:false System.out.println(coll); //输出结果为:[abc, itcast, itheima, money, 123] 没有该元素时 结果不变 } }5)size():返回此Collection中的元素个数
import java.util.ArrayList; import java.util.Collection; public class CollectionDemo2 { public static void main(String[] args) { function(); } /* * Collection接口的方法 size():集合中的元素个数 */ public static void function() { //接口多态的方式调用 Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("dda"); System.out.println(coll.size());//输出结果为:2 coll.clear(); System.out.println(coll.size());//输出结果为:0 /* * 注意:学习Java中三种长度表现形式 * 数组.length 属性 返回值 int * 字符串.length() 方法,返回值int * 集合.size()方法, 返回值int */ } }6)toArray():返回包含此Collection中的所有元素的数组
import java.util.ArrayList; import java.util.Collection; public class CollectionDemo2 { public static void main(String[] args) { function(); } /* Collection接口方法 * Object[] toArray() 集合中的元素,转成一个数组中的元素, 集合转成数组 * 返回是一个存储对象的数组, 数组存储的数据类型是Object * 一旦把集合转换成数组,则数组的长度是不能变化的 */ private static void function() { Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("itcast"); coll.add("itheima"); coll.add("money"); coll.add("123"); Object[] objs = coll.toArray(); for(int i = 0 ; i < objs.length ; i++){ System.out.println(objs[i]);//输出结果为:abc // itcast // itheima // money // 123 } } }java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。 要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素, 如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。直到取出所有元素。这种取出方式专业术语称为迭代。
每种集合的底层的数据结构不同,例如ArrayList是数组,LinkedList底层是链表,但是无论使用哪种集合,都需要判断是否有元素以及取出元素的动作,那么Java为我们提供一个迭代器定义了统一的判断元素和取元素的方法 。
2)代码图解:
指针移到最后就不能在返回最初位置,所以迭代器只能执行一次。1)在使用集合时,我们需要注意以下几点: ① 集合中存储其实都是对象的地址。 ② 集合中可以存储基本数值吗?jdk1.5版本以后可以存储了。 ③ 因为出现了基本类型包装类,它提供了自动装箱操作(基本类型对象),这样,集合中的元素就是基本数值的包装类对象。 2)存储时提升了Object。取出时要使用元素的特有内容,必须向下转型。
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class CollectionDemo4 { public static void main(String[] args) { //集合可以存储任意类型的对象 //不指定存储的类型,什么都可以存 Collection coll = new ArrayList(); coll.add("abc"); coll.add("hjkyrdsh"); //迭代器获取 Iterator it = coll.iterator(); while(it.hasNext()) { //没有指定集合存储具体数据类型的情况下,it.hasNext()获取的是什么数据类型? //回答:Object类型 //Object obj = it.next(); //System.out.println(obj); //想要获得字符串的长度,做强转,把Object类型转成String类型 String s = (String)it.next(); //字符串长度是String类的特有属性,可以直接调用 System.out.println(s.length());//输出结果为:3 8 } } } 没有指定集合存储具体数据类型的情况下,默认集合内元素为Object类型,此时想要调用子类特有属性,就必须进行向下转型,转成某固定类型后才能调用该类型的特有属性和方法如果集合中存放的是多个对象,这时进行向下转型会发生类型转换异常。2) 增强for循环遍历集合
public class Person { private String name ; private int age; public String toString(){ return "[name="+ name +" ,age="+ age +"]"; } //定义构造方法 public Person(String name ,int age ) { this.name= name; this.age =age; } public Person() {} public void setName( String name) { this.name = name; } public String getName() { return name ; } public void setAge( int age) { this.age = age; } public int getAge() { return age ; } public void speak() { System.out.println(name+"..."+age); } } import java.util.ArrayList; import cn.itcast.demo.Person; public class ForEachDemo1 { public static void main(String[] args) { function(); } /* * 增强for循环遍历集合 * 存储自定义Person类型 * 弊端:以索引为根据的方法都不可以操作了 */ public static void function(){ ArrayList<Person> array = new ArrayList<Person>(); array.add(new Person("a",20)); array.add(new Person("b",10)); for(Person p : array){ System.out.println(p); //System.out.println(p.toString()); //输出结果为:[name=a ,age=20] // [name=b ,age=10] } } }在前面学习集合时,我们都知道集合中是可以存放任意对象的, 只要把对象存储集合后,那么这时他们都会被提升成Object类型。 当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。 此时会有安全隐患,比如下面程序:
public class GenericDemo { public static void main(String[] args) { function(); } public static void function() { Collection coll = new ArrayList(); coll.add("abc"); coll.add("rtey"); coll.add("fdyujhkiul"); //在不规定类中具体数据类型的情况下,下例情况会报错 coll.add(1); //报错java.lang.Integer cannot be cast to java.lang.String //如果在集合中输入数字,会被自动装箱为Integer对象,不能转换成String类型,就会报错 //String str=(String)obj; //编译时期仅检查语法错误,String是Object的儿子可以向下转型 //运行时期String str=(String)(new Integer(5)) //String与Integer没有父子关系所以转换失败 Iterator it = coll.iterator(); while(it.hasNext()){ //System.out.println(it.next());//输出结果为:abc // rtey // fdyujhkiul String s = (String)it.next(); System.out.println(s.length());//输出结果为:3 // 4 // 10 } } }由于集合中什么类型的元素都可以存储,导致取出时,如果出现强转就会引发运行时的ClassCastException,即类型转换异常。想要解决这个问题,就必须明确集合中的元素类型,这种方式成为:泛型。
1) 在集合中会大量使用泛型,用来灵活地将数据应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
/* * JDK1.5 出现新的安全机制,保证程序的安全性 * 泛型: 指明了集合中存储数据的类型 <数据类型> */ public class GenericDemo { public static void main(String[] args) { function(); } public static void function() { Collection<String> coll = new ArrayList<String>(); coll.add("abc"); coll.add("rtey"); coll.add("fdyujhkiul"); //此时若添加coll.add(1); 的话,就会报错,因为类型不符,不能添加进String类的集合中 Iterator<String> it = coll.iterator(); while(it.hasNext()){ String s = (String)it.next(); System.out.println(s.length());//输出结果为:3 // 4 // 10 } } }2) Java中的伪泛型:
泛型只在编译时存在,编译后就被擦除,在编译之前我们就可以限制集合的类型,起到作用
1)定义格式:
修饰符 class 类名<代表泛型的变量> { }° 例如,API中的ArrayList集合:
class ArrayList<E>{ public boolean add(E e){ } public E get(int index){ } }2) 使用格式:
创建对象时,确定泛型的类型° 例如,ArrayList list = new ArrayList(); 此时,变量E的值就是String类型
class ArrayList<String>{ public boolean add(String e){ } public String get(int index){ } }° 例如,ArrayList list = new ArrayList(); 此时,变量E的值就是Integer类型
class ArrayList<Integer>{ public boolean add(Integer e){ } public Integer get(int index){ } }1) 定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ } ° 例如,API中的ArrayList集合中的方法:
public <T> T[] toArray(T[] a){ } //该方法,用来把集合元素存储到指定数据类型的数组中,返回已存储集合元素的数组2)使用格式:调用方法时,确定泛型的类型 ° 例如:
此时,变量T的值就是Integer类型。变量T,可以与定义集合的泛型不同
1)将运行时期的ClassCastException,转移到了编译时期变成了编译失败。 2) 避免了类型强转的麻烦 3) 带来了增强for的使用 4) 增强安全性
public class GenericDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("abc"); list.add("itcast"); //list.add(5);//当集合明确类型后,存放类型不一致就会编译报错 //集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); System.out.println(str.length()); //当使用Iterator<String> //控制元素类型后,就不需要强转了。获取到的元素直接就是String类型 } } }1)案例需求:将酒店的员工、厨师、经理分别存储到3个集合中,定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法。 2)代码示例:(hotel包可以从day14中的案例复制)
import java.util.ArrayList; import java.util.Iterator; /* * 将的酒店员工,厨师,服务员,经理,分别存储到3个集合中 * 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 */ public class GenericTest { public static void main(String[] args) { //创建三个集合对象 ArrayList<Chushi> cs = new ArrayList<Chushi>(); ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>(); ArrayList<JingLi> jl = new ArrayList<JingLi>(); //将的酒店员工,厨师,服务员,经理,分别存储到3个集合中 cs.add(new Chushi("张三", "后厨001")); cs.add(new Chushi("李四", "后厨002")); fwy.add(new FuWuYuan("翠花", "服务部001")); fwy.add(new FuWuYuan("酸菜", "服务部002")); jl.add(new JingLi("小名", "董事会001", 123456789.32)); jl.add(new JingLi("小强", "董事会002", 123456789.33)); iterator(jl); //输出结果为:小名...董事会001管理,谁出错就罚钱 // 小强...董事会002管理,谁出错就罚钱 iterator(fwy);//输出结果为: 翠花...服务部001服务员上菜 // 酸菜...服务部002服务员上菜 iterator(cs); //输出结果为:张三...后厨001厨师在炒菜 // 李四...后厨002厨师在炒菜 } /* * 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work */ /* * 问题: * 迭代器it.next()方法取出来的是Object类型,想调用子类特有的work方法,必须强制转换成子类 * 强制转换: it.next()=Object o ==> 可以转成他们的父类Employee * 上述想法是对的,但是存在安全隐患,比如添加其他类型的集合 * 如ArrayList<String> arrayString = new ArrayList<String>();时就会报错转换异常 */ /* * 解决办法: * 泛型的限定 : * 通过编译方法参数,达到可以传递Employee对象,也可以传递Employee的子类对象的效果 * 本案例,父类固定Employee,但是子类可以无限?个用通配符表示 * ? extends Employee 限制的是父类, 上限限定, 可以传递Employee,传递他的子类对象 * ? super Employee 限制的是子类, 下限限定, 可以传递Employee,传递他的父类对象 */ public static void iterator(ArrayList<? extends Employee> array){ Iterator<? extends Employee> it = array.iterator(); while(it.hasNext()){ //获取出的next() 数据类型是什么?回答:Employee 因为不知道有哪些子类 Employee e = it.next(); e.work(); } } }day14中的hotel包: ① Employee
/* * 酒店的员工类 * 员公共性:姓名,工号,工作方法 * */ public abstract class Employee { private String name; private String id; //构造器空参有参的都要写,使用者用哪个随便 public Employee () {} public Employee (String name,String id) { this.name = name; this.id = id; } public abstract void work(); public void setName(String name) { this.name = name; } public void setId(String Id) { this.id = id; } public String getName() { return name; } public String getId() { return id; } }② Chushi
/* * 定义厨师类 * 属于员工的一种,继承员工类 * 具有额外的VIP功能,实现VIP接口 */ public class Chushi extends Employee implements VIP{ //空参构造方法 public Chushi() {} //有参构造方法 public Chushi (String name,String id) { super(name,id); } //重写抽象方法 public void work() { System.out.println(super.getName()+"..."+super.getId()+"厨师在炒菜"); } public void services() { System.out.println(super.getName()+"..."+super.getId()+"厨师做菜加量"); } }③ FuWuYuan
/* * 定义服务员类 * 属于员工的一种,继承员工类 * 具有额外的VIP功能,实现VIP接口 */ public class FuWuYuan extends Employee implements VIP{ //空参构造方法 public FuWuYuan() {} //有参构造方法 public FuWuYuan (String name,String id) { super(name,id); } //重写抽象方法 public void work() { System.out.println(super.getName()+"..."+super.getId()+"服务员上菜"); } public void services() { System.out.println(super.getName()+"..."+super.getId()+"服务员给顾客倒酒"); } }④ JingLi
/* * 定义经理类 * 属于员工的一种,继承员工类 * 没有VIP功能,不是实现类 * 自己有奖金属性 */ public class JingLi extends Employee { //空参构造方法 public JingLi() {} //有参构造方法,注意不要忘记奖金属性! public JingLi (String name,String id,double money) { super(name,id); this.money = money; } //定义奖金属性 private double money; //有private就有set、get方法 public void setMoney(double money) { this.money = money; } public double getMoney () { return money; } //重写抽象方法 public void work() { System.out.println(super.getName()+"..."+super.getId()+"管理,谁出错就罚钱"); } }⑤ VIP
/* * 酒店的VIP服务 * 只有厨师和服务员才有 * 所以厨师和服务员为接口VIP的两个实现类 */ public interface VIP { public abstract void services(); }