黑马程序员——Java要点笔记——集合框架(泛型)

来源:互联网 发布:优美图软件下载 编辑:程序博客网 时间:2024/06/05 16:34

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

day18 01-常用对象API(集合框架-泛型-概述)

1、泛型这种参数类型可以用在类、接口、方法的创建中,分别称为泛型类、泛型接口、泛型方法。

2、泛型的引出,需求:定义一个表示坐标的操作类(Point),这个类可以表示3种类型的坐标。(1)整数坐标:x=10、y=20;(2)小数坐标:x=10.1、y=20.3;(3)字符串数据:x=“东经100度”、y=“北纬20度”

3、分析,类中如果要保存以上的数据,一定需要定义x和y两个属性,而这两个属性可以直接接收3种数据类型,那么使用Object类来定义会比较合适,这样会发生如下几种转换关系:

       (1)整数:int→自动装箱为Integer→向上转型为Object;

       (2)小数:double→自动装箱为Double→向上转型为Object;

       (3)字符串:字符串→向上转型为Object

4、代码示例:

class Point{    private Object x;    private Object y;    public void setX(Object x){       this.x = x;    }    public void setY(Object y){       this.y = y;    }    public Object getX(){       return x;    }    public Object getY(){       return y;    }}

5、范例一:设置整型

public class Demo34 {    public static void main(String[] args) {       Point point = new Point();       point.setX(10);       point.setY(20);       int x = (Integer)point.getX();       int y = (Integer)point.getY();       System.out.println("X的坐标是:"+x+",Y的坐标是:"+y);    }}

运行结果:X的坐标是:10,Y的坐标是:20

6、范例二:设置小数

public class Demo34 {    public static void main(String[] args) {       Point point = new Point();       point.setX(10.2);       point.setY(20.3);       double x = (Double)point.getX();       double y = (Double)point.getY();       System.out.println("X的坐标是:"+x+",Y的坐标是:"+y);    }}

运行结果:X的坐标是:10.2,Y的坐标是:20.3

7、范例三:设置字符串

public class Demo34 {    public static void main(String[] args) {       Point point = new Point();       point.setX("东经100度");       point.setY("北纬20度");       String x = (String)point.getX();       String y = (String)point.getY();       System.out.println("X的坐标是:"+x+",Y的坐标是:"+y);    }}

    运行结果:X的坐标是:东经100度,Y的坐标是:北纬20度

8、但是本程序存在问题,关键就在于Object,所有的类型都可以向Object转换。

范例四:发现程序问题所在

public class Demo34 {    public static void main(String[] args) {       Point point = new Point();       point.setX(10);       point.setY("北纬20度");       String x = (String)point.getX();       String y = (String)point.getY();       System.out.println("X的坐标是:"+x+",Y的坐标是:"+y);    }}

    运行结果:Exception in thread "main" java.lang.ClassCastException: java.lang.Integer

    atcom.itheima.Demo34.main(Demo34.java:25)

9、你把程序写完了。用户瞎往里传了个10,但由于是都拿Object接收,没有问题。但其实出现了数据的不统一。而这问题却不会在编译时暴露出来,而是在运行时暴露的。(用户往里传了个int,你向上转型成Object接收,没问题。但你再向下转型成String,不行,挂了。

10、泛型:广泛的类型,类中操作的属性或方法的参数类型不在定义是声明,而是在使用时动态设置。

11、泛型,是JavaSE1.5的新特性。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口、方法的创建中,分别称为泛型类、泛型接口、泛型方法。

    在JavaSE1.5之前没有泛型的情况下,通过对类型Object的引用来实现参数的“任意化”。这种“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

12、注意:泛型类型只能设置为类

    如果要设置泛型类型,只能将其定义为类的形式。例如,如果要保存的数据为int,则设置的类型必须是其包装类Integer。而此时,自动装箱及拆箱机制就可以给予很好的支持。

13、利用泛型解决问题,代码示例:

class Point<T>{   //T:type,可以使用任意标记,例如:A、B均可    private T x;  //属性类型由外部决定    private T y;  //属性类型由外部决定    public void setX(T x){       this.x = x;    }    public void setY(T y){       this.y = y;    }    public T getX(){       return x;    }    public T getY(){       return y;    }}

14、之后再来试验一下数据类型不统一的情况

public class Demo44 {    public static void main(String[] args) {       Point<String> point = new Point<String>();    //实例化对象       point.setX(10);   //编译时出现错误       point.setY("北纬20度");   //1、设置y坐标的内容       String x = (String)point.getX();   //2、取得x坐标数据       String y = (String)point.getY();   //3、取得y坐标数据       System.out.println("x的坐标是:"+x+",y的坐标是:"+y);    }}  

    由于设置了泛型类型为String,所以本程序中对应的setter方法的苍南数类型就统一设置为了String,而如果设置的数据不是String,则在编译时就会出现语法错误。

15、泛型的好处:

    ①、将运行时期的问题ClassCastException转到了编译时期,这时由程序员来解决这个问题。即:在编译的时候检查类型安全。

    ②、避免了强制转换的麻烦。所有的强制转换都是自动和隐式的,提高了代码的重用率。

16、进一步理解编译时期的错误、与运行时期的错误。

    编译时出现错误,则在程序员编写程序时就可以很明显的发现,并且该错误可以由程序员立刻进行修改。这样,程序员就可以最早的对这样的问题进行解决。

    运行时出现错误,则可能是在用户正常使用、运行该软件时出现错误。而该错误用户无法解决,对用户的使用造成很大不便。所以这是一种安全隐患。

    打个比方就是,做一个装置箱,我们肯定希望在设计时就发现其中存在的问题缺陷。而不是在制造出来后,甚至是已近给到用户后,再发现其中的缺陷错误。因此,我们希望错误最好能在编译时期就暴露出来。

17、进一步理解ClassCastException在编译时期和运行时期的问题

    异常中说过,编译时出错的问题可以理解为是“语法问题”。而运行时出错问题可以理解为是“逻辑问题”。那么来看本节中出现的ClassCastException。

    没用泛型时,我相当于说了一句“小猫变成生物,生物再变成小猪”,这句话在语法上完全没有任何问题,他只是在逻辑上有问题。

    用泛型时,我相当于事先就限制了所应传入的数据类型,你传的不对,所以这个错误就暴露在了编译时期。就像method(int i),你写method("字符串"),当然就挂了。

18、如果你定义了反省,但是在使用时没有设置反省。那么程序在编译过程中会出现警告信息。而且为了保证程序不出现错误,所有的类型都将使用Object进行处理。

19、两个有关泛型的其他程序

代码一:

public class Demo37 {    public static void main(String[] args) {       ArrayList<String> al = new ArrayList<String>();       show(al);    }    public static void show(ArrayList al){       al.add("abc");       al.add(100);       System.out.println(al);    }}

    运行结果:[abc, 100]

    怎么回事?我的泛型废了。100也进来了。因为你的show方法的参数是ArrayList al。这相当于ArrayList al =new ArrayList<String>();只在后面写泛型是没用的。代码二可证明这一点。

代码二:

public class Demo37 {    public static void main(String[] args) {       ArrayList al1 = new ArrayList<String>();       al1.add("abc");        al1.add(100);             ArrayList<String> al2 = new ArrayList();       al2.add("def");       al2.add(200);//仅在这里编译报错,类型不对    }}

所以,用泛型,重要的是在前面。JDK1.5和JDK1.7在具体化泛型时稍微有些区别。

JDK1.5具体化泛型方式,Point<String> point = new Point<String>();

JDK1.7具体化泛型的方式,Point<String> point = new Point();

但仍然建议使用完整语法进行编写。

day18 02-常用对象API(集合框架-泛型-擦除&补偿)了解

1、泛型是用在编译时期的安全机制,是给编译器使用的技术,用于编译时期,确保了类型的安全。

2、运行时,会将泛型去掉,生成的class文件是不带泛型的,这个称为泛型的擦除。

    那么,为什么要擦除呢?因为为了兼容运行的类加载器。我不擦他,你以前的类加载器运行不了!

3、泛型的补偿:在运行时,通过获取元素的类型进行转换动作,不用使用者再强制转换。

day18 03-常用对象API(集合框架-泛型-在集合中的应用)

day18 04-常用对象API(集合框架-泛型-泛型类)

1、泛型类什么时候用?当类中操作的引用数据类型不确定的时候,就使用泛型来表示。

2、代码示例:

class Tool<QQ>{    private QQ q;    public QQgetObject(){       return q;    }    public void setObject(QQ object){       this.q = object;    }}

day18 05-常用对象API(集合框架-泛型-泛型方法)

1、同理,当方法中操作的引用数据类型不确定的时候,就使用泛型方法。把泛型也可以定义在方法上。另外,在方法上定义泛型时,这个方法不一定非要在泛型类中定义。

2、代码示例

class Tool<QQ>{    private QQ q;    public QQ getObject(){       return q;    }    public void setObject(QQ object){       this.q = object;    }    public void show(QQ object){       System.out.println("show:"+object);    }    public <W> void print(W w){       System.out.println("print:"+w);    }}public class Demo35 {    public static void main(String[] args) {       Tool<String> tool = new Tool<String>();       tool.show("haha");       tool.print(100);    }}


运行结果:show:haha

print:100

3、上例中,show方法使用了类上定义的泛型QQ,所以只能往里传String。而print方法上面自己有个泛型,所以可以不受约束。

4、当方法静态时,不能访问类上定义的泛型(因为类上定义的泛型需要对象来明确,但是静态方法是不需要对象的)。如果静态方法使用泛型,只能将泛型定义在方法上。

    例如:public <Y> static void method(Y obj){}

5、注意,泛型方法在定义时,泛型一定要放在返回值的前面,修饰符的后面。

public <Y> staticvoid method(Y obj){}

这么定义是错误的,<Y>要放到static后面。

day18 06-常用对象API(集合框架-泛型-泛型接口)

1、代码示例:

interfaceMessage<T>{    public String echo(T msg);}

2、对于泛型接口的实现,在java中有两种方式:

①、方式一:在子类上继续使用泛型,在实例化子类时,设置具体类型

interface Message<T>{           publicString echo(T msg);}class MessageImpl <T> implements Message<T>{           publicString echo(T msg){               return "ECHO:"+msg;           }}public classDemo36 {           public static voidmain(String[] args) {               Message<String> msg = newMessageImpl<String>();               System.out.println(msg.echo("张三"));           }}

运行结果:ECHO:张三

    ②、方式二:在子类上设置具体类型

     

  interface Message<T>{           publicString echo(T msg);}class MessageImpl implementsMessage<String>{           publicString echo(String msg){               return "ECHO:"+msg;           }}public classDemo36 {           public static voidmain(String[] args) {               Message<String> msg = new MessageImpl();               System.out.println(msg.echo("张三"));           }}

运行结果:ECHO:张三

day18 06.5-常用对象API(集合框架-泛型-通配符)

1、为什么要出现通配符?

2、为了说明这个问题,定义了一个类:

class Message<T>{    private T info;    public void setInfo(T info){       this.info = info;    }    public T getInfo(){       return info;    }}

3、代码示例一:

public class Demo45 {    public static void print(Message<String> temp){       System.out.println(temp.getInfo());    }    public static void main(String[] args) {       Message<String> msg = new Message<String>();       msg.setInfo("HelloWorld");       print(msg);    }}

    运行结果:Hello World

4、代码示例二:

public class Demo45 {    public static void main(String[] args) {       Message<Integer> msg = new Message<Integer>();       msg.setInfo(100);       print(msg);    }    public static void print(Message<String> temp){       System.out.println(temp.getInfo());    }}

    print(msg)一句报错。此时的print(Message<String> temp)方法无法再接收Message<Integer>对象的引用,因为这个方法只能接收Message<String>,那么如果将print()方法重载,换成Message<Integer>呢?

5、代码示例三:尝试通过重载解决问题。

public class Demo45 {    public static void main(String[] args) {       Message<Integer> msg = new Message<Integer>();       msg.setInfo(100);       print(msg);    }    public static void print(Message<String> temp){       System.out.println(temp.getInfo());    }    public static void print(Message<Integer> temp){       System.out.println(temp.getInfo());    }}

    此处重载失败了。编译报错:已定义了方法。因为方法重载时观察的不是泛型类型,而是类的名称,或者是数据类型。

6、代码示例四:定义方法时不写泛型

public class Demo45 {    public static void main(String[] args) {       Message<Integer> msg = new Message<Integer>();       msg.setInfo(100);       print(msg);    }    public static void print(Message temp){       System.out.println(temp.getInfo());    }}

    运行结果:100

7、但是又有新的问题,print()方法可以任意设置

public class Demo45 {    public static void main(String[] args) {       Message<Integer> msg = new Message<Integer>();       msg.setInfo(100);       print(msg);    }    public static void print(Message temp){       temp.setInfo("HelloWorld");       System.out.println(temp.getInfo());    }}

    运行结果:Hello World

    因为你不写泛型,相当于Message<Object> temp。T成了Object,想接受谁都行。

8、解决方法,通配符

public class Demo45 {    public static void main(String[] args) {       Message<Integer> msg = new Message<Integer>();       msg.setInfo(100);       print(msg);    }    public static void print(Message<?> temp){       System.out.println(temp.getInfo());    }}

    运行结果:100

9、泛型的出现,将一个类又划分成了若干个不同的小类型:

10、对于“?”就记住一点,他表示任意类型,如果有参数返回时也是这个“?”,就当成Object进行理解。

day18 07-常用对象API(集合框架-泛型-上限)

1、写法:? extends 某一个类

2、举例:

public static voidprintCollection(Collection<? Extends Person> al){}

该方法使用了上限,只接收“元素为Person或Person子类的Collection集合”。

所以,如果是:

    

ArrayList<String> al = new ArrayList<String>();    al.add(“str1”);    al.add(“str2”);    printCollection(al);//这一步挂了,因为al的泛型具体化显示,它里面的元素不是Person或Person的子类。

3、另外,你这么限定之后,就可以用具体方法了。因为printCollection方法只接收“元素为PersonPerson子类的Collection集合”。那么我就可以在printCollection方法里使用Person类的特有方法。如下例:

public static voidprintCollection(Collection<? Extends Person> al){    Iterator<? Extend Person> it = al.iterator();    while(it.hasNext()){       Person p = it.next();       System.out.print(p.getName()+”…”+p.getAge());    }}

4、注意:public static void printCollection(Collection<? Extends Person>al){}这个方法并不是泛型方法。他只是利用通配符,对传入的"存在泛型的参数"(例如Collection<?extends Person>有所限制。而泛型方法则是:当方法中操作的引用数据类型不确定的时候,就使用泛型方法。要注意区分。

day18 08-常用对象API(集合框架-泛型-下限)

1、上限的写法是:? extends E——接收E类型或者E的子类对象。

2、下限的写法是:? super E——接收E类型或者E的父类型。

3、代码示例:

public static voidprintCollection(Collection<? super Student> al){    Iterator<? super Student> it = al.iterator();    ……}

    你<? super Student>一加,可以传Person、Student,但你不能传Worker了。因为Worker不是Student的父类。

4、关于迭代器的泛型

    迭代器的泛型,100% 和获取迭代器集合的泛型一致。那么,我集合已经加了泛型,为什么迭代器上还要加?因为你使用迭代器是要便利集合里面的数据。遍历的时候,iterator默认遍历出来的都向上转成了Object类。你加上泛型就不用强转了。

day18 09-常用对象API(集合框架-泛型-泛型限定(上限的体现))

1、上限什么时候用?

    答:一般在往里面存元素的时候,用上限(? extends E)。因为这样去除的都是按照上限类型来运算的。不会出现类型安全隐患。

2、JAVA中用上限的例子:

    接口Collection<E>

    方法摘要:

       boolean addAll(Collection<? extends E> c)

3、代码示例:

class MyCollection<E>{    public void add(E e){          }    public void addAll(MyCollection<E> c){          }}public class Fanxing {    public static void main(String[] args) {       MyCollection<Person> mc1 =new MyCollection<Person>();       MyCollection<Person> mc2 =new MyCollection<Person>();       MyCollection<Student> mc3 =new MyCollection<Student>();       MyCollection<String> mc4 =new MyCollection<String>();             mc1.addAll(mc3);//此句编译报错    }}

但如果改成如下,则不会报错。

class MyCollection<E>{    public void add(E e){          }    public void addAll(MyCollection<? extends E> c){          }}public class Fanxing {    public static void main(String[] args) {             MyCollection<Person> mc1 =new MyCollection<Person>();       MyCollection<Person> mc2 =new MyCollection<Person>();       MyCollection<Student> mc3 =new MyCollection<Student>();       MyCollection<String> mc4 =new MyCollection<String>();             mc1.addAll(mc3);    }}

 

day18 10-常用对象API(集合框架-泛型-泛型限定(下限的体现))

1、什么时候用下限?通常对集合中的元素进行取出操作时,可以使用下限。你存什么类型,我用什么类型接收。

2、JAVA中用下限的例子:

    类TreeSet

    构造方法摘要:

       TreeSet(Comparator<? super E> comparator)

       构造一个新的空TreeSet,他根据指定比较器进行排序。

2、你一个容器中,既能添加Person对象,又能添加Student对象,又能添加Worker对象等子对象。那这里我又有Person,又有学生、又有工人。我想对这些对象进行统一排序。排的时候,要将这些对象取出来比较,我拿什么接收?Person!

3、举个例子:

    我的容器里面是Person,那么

day18 11-常用对象API(集合框架-泛型-泛型限定(通配符的体现))

1、通配符一般什么时候用?你只要里面全是Object方法,就用这个。

2、JAVA中用通配符的例子

    接口Collection

       boolean containsAll(Collection<?> c)

       如果此Collection包含指定Collection中的所有元素,则返回true。
0 0
原创粉丝点击