7.集合1

1. 集合

1.1 什么是集合

  集合是存储对象的容器。集合中可以存储任意类型的变量。Java提供了多种集合类,不同集合类的数据结构不同,分别适合在不同的场景使用。

1.2 集合和数组

  数组也用来存储数据,如果定义一个对象数组,例如Object[] objArr;那么对象数组也能存储对象。但是集合和数组有很大区别:

  (1)数组长度是固定的,集合的长度是可变的。如果无法预先知道需要多少存储多少个数据,那么很难使用数组。

  (2)数组一般只能存储同一类型的元素,集合可存储不同类型的元素。

  (3)集合只能存储引用类型的元素。JDK 5实现了自动装箱功能,所以集合形式上也能存储基本数据类型的元素。

1.3 Collection接口

  集合有很多共性,因此集合抽象出了一个Collection接口,下面讲的单例集合都是Collection接口的实现类。下面通过一个实现类来讲Collection接口中的常用方法。

2. Collection接口

  为了了解Collection接口中方法,首先要创建一个集合对象,那么需要用多态方式来创建一个Collection接口的实现类对象。

  先找到一个实现Collection接口的类。查看API,有如下说明:

  “JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List)的实现。”

  也就是说Collection没有直接的实现类,我们可以找Collection子接口Set或者List接口的实现类。其中,ArrayList是List接口的一个实现类,我们就用它来创建集合对象,以此介绍Collection接口中的方法。【提示,目前看API时会遇到、等字样,而且在IDE中会有警告,这涉及到泛型,暂时不用管它】

2.1 添加和删除对象方法

  (1)public boolean add(Object o):添加一个元素到集合中。此方法总是返回true,因此返回值不重要。

  (2)public boolean addAll(Collection c):将一个集合的元素全部添加到该集合中。

  (3)public boolean remove(Object o):将元素从集合中删除(如果有重复,只会删除一个)。如果集合中没有该元素,则返回false(不会出现异常)。

  (4)public boolean removeAll(Collection c):移除集合中存在于集合c中的元素,包括重复的元素。只要有一个元素被移除了就返回true。

  (5)public void clear():移除集合中所有的元素,集合变为一个空集合。

2.2 其他方法

  (1)public boolean contains(Object o):判断集合中是否包含指定的元素。对于引用类型,contains方法中是调用对象的equals方法来判断的,因此当集合存储自定义对象时,注意重写好自定义类的equals方法。

  (2)public boolean isEmpty():判断集合是否为空。

  (3)public int size():返回集合中元素个数。

  (4)public boolean containsAll(Collection c):判断集合中是否包含了集合c的所有元素。必须全部包含才返回true。

  (5)public boolean retainAll(Collection c):将该集合与集合c进行交集,结果保存在本集合中,如果本集合改变了就返回true,否则返回false。

  (6)public Object[] toArray():将集合转成Object数组。

  (7)public String toString():Collection重写了toString()方法,可以直接将集合以类似数组的方式输出。

  下面是将自定义对象存储再集合中的例子。

  (1)Student类

package com.zhang.test;

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    // 构造方法
    public Student(){}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 下面重写equals、hashCode和toString方法

    public boolean equals(Object obj) {
        if(!(obj instanceof Student)) {
            return false;
        }
        if(obj == this) {
            return true;
        }
        Student stu = (Student) obj;
        if(stu.getName().equals(this.getName()) && stu.getAge() == this.getAge()) {
            // 若姓名和年龄相同,就假定他们是同一对象
            return true;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return this.getName().hashCode() + this.getAge();
    }

    public String toString() {
        return this.getName() + ":" + this.getAge();
    }
}

  (2)主类

package com.zhang.test;

import java.util.ArrayList;
import java.util.Collection;

public class Demo {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(new Student("张三", 12));
        c.add(new Student("李四", 13));
        c.add(5); //可以加任意类型
        // 判断,底层调用的是equals方法
        System.out.println(c.contains(new Student("张三", 12)));
        // 得到元素个数
        System.out.println(c.size());
        // 输出集合,调用的是toString()方法,显示很友好,把其中自定义对象equals方法也调用了
        System.out.println(c);
    }
}

2.3 Iterator迭代器

  迭代器用于对集合元素的遍历。迭代器以内部类的形式存在于集合类中,通过迭代器能够拿到集合中元素,也能通过迭代器来操作元素。

  调用集合的iterator()方法能得到此集合的迭代器。Collection接口继承自Iterable接口,正是Iterable接口定义了iterator()方法。iterator()方法返回的是Iterator接口,该接口有如下方法以便遍历和获取元素:

  (1)boolean hasNext():判断是否还有可迭代的元素。

  (2)Object next():获取下一个元素,同时指针再向后移动。如果没有下一个元素,则抛出NoSuchElementException异常。

  小知识点:如果一个方法能返回任意类型或者接受任意类型,那么返回值类型或者参数类型就可设置为Object,因为Object是所有类的基类。

  因此常用上面的方法实现遍历,例子:(Student类还是上面的代码)

package com.zhang.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(new Student("张三", 12));
        c.add(new Student("李四", 13));
        // 迭代
        Iterator it = c.iterator();
        while(it.hasNext()) {
            // 先拿出对象。由于存储的都是Student,直接转换
            Student stu = (Student) it.next();
            System.out.println(stu);
            /*
            注意,不能这样写:
            System.out.println( ((Student) it.next()).getName() );
            System.out.println( ((Student) it.next()).getAge() );
            这样每次循环都会执行两次next(),会导致跳过元素和出现
            NoSuchElementException的异常
             */
        }
    }
}

  推荐在for循环中创建迭代器并迭代,这样可以尽早释放迭代器资源。代码如下:

package com.zhang.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(new Student("张三", 12));
        c.add(new Student("李四", 13));
        // 迭代
        for(Iterator it = c.iterator(); it.hasNext(); ) {
            Student stu = (Student) it.next();
            System.out.println(stu.getName());
            System.out.println(stu.getAge());
        }
    }
}

  迭代器中还有remove()方法,可在遍历时删除元素,但是使用remove()方法之前必须先调用next()方法,否则会出现IllegalStateException异常。此外,在用迭代器迭代过程中,只能通过迭代器操作元素,不能使用集合本身提供的方法或者其他方法操作集合元素,否则会有安全隐患,会抛出并发修改异常ConcurrentModificationException。这点在List集合中还会强调和加深。

  总结:上述讲的Collection方法和迭代器,在下面讲的单例集合(List和Set集合)中都能使用,因为单例集合就是Collection的实现类。

3. List集合

3.1 简介

  List也是接口,最常用的实现类是ArrayList。List集合有如下特点:

  (1)List集合是有序的。List集合会保留元素的存储顺序,这样取出元素时是顺序取出。

  (2)List集合中元素可以重复,即一个集合能存储多个相同的元素。

3.2 List集合特有方法

  (1)public void add(int index, Object o):在指定索引处添加元素。

  (2)public Object get(int index):获取指定位置的元素。

  (3)public Object remove(int index):删除指定索引处的元素,并返回此元素

  (4)public Object set(int index, Object o):将指定索引处的元素修改为新值o,返回值是原本此位置的元素。若索引超出范围,则抛出IndexOutOfBoundsException。

  (5)public boolean addAll(int index, Collection c):在指定索引处添加集合中所有元素。

  (6)public int indexOf(Object o):返回集合中第一次出现该对象的索引,如果没有,就返回-1。

  (7)public int lastIndexOf(Object o):返回集合中最后一次出现该对象的索引,如果没有,就返回-1。

  (8)public List subList(int fromIndex, int toIndex):获得指定区间的子集合。不包含toIndex。

  说明:上述介绍的很多方法,比如remove()、indexOf()等都要先找到集合中与目标相同的对象,调用的方法都是调用equals()方法。

  List集合有get(int index)方法,这样可以结合Collection的size()方法自行实现遍历,而不需用迭代器。例子:

package com.zhang.test;

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new Student("张三", 12));
        list.add(new Student("李四", 13));
        // 利用get()和size()遍历List集合
        for(int i = 0; i < list.size(); i++) {
            Student stu = (Student) list.get(i);
            System.out.println(stu);
        }
    }
}

3.3 List特有迭代器——ListIterator

  List集合可以直接用Iterator迭代器,还能用List特有的迭代器ListIterator。通过调用List集合对象的listIterator()方法,能得到ListIterator对象。

  ListIterator继承自Iterator,基本的用法和Iterator相同,只是ListIterator中增加了如下方法:

  (1)public boolean hasPrevious():判断是否有上一个元素

  (2)public Object previous():获取上一个元素,并将指针再指向前一个元素。

  通过上述方法,能够实现逆向遍历List集合。当然,一般先要使用next()方法,将指针指向后面,才可能逆向遍历,因为一开始的游标(指针)都是在第一个元素的前面。

  另外,ListIterator还有方法:

  public void add(Object o):将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有),或者 previous 返回的下一个元素之后(如果有);如果列表没有元素,那么新元素就成为列表中的唯一元素。新元素被插入到隐式光标前:不影响对 next 的后续调用,并且对 previous 的后续调用会返回此新元素。

  借此机会着重讲“并发修改异常”。

  前面提到过:在用迭代器过程中,只能通过迭代器操作元素,否则会有安全隐患,产生并发修改异常。例子:集合中存储“hello”,“world”和“java”三个字符串,要求遍历集合时,一旦遇到“world”字符串,就在集合中添加一个“c++”字符串。

  错误示范:在用迭代器过程中,使用集合方法操作集合元素:

package com.zhang.test;

import java.util.*;

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("java");
        // 迭代器遍历
        for(Iterator it = list.iterator(); it.hasNext(); ) {
            String str = (String) it.next();
            if(str.equals("world")) {
                // 用集合的add()方法添加元素
                list.add("c++");
            }
        }
        // 最后输出
        System.out.println(list);
    }
}

  运行程序后出现异常:ConcurrentModificationException。为什么出现并发修改异常呢?因为使用迭代器时,如果使用了集合来操作元素,会导致迭代器并不知道集合被修改了,而迭代器的使用是完全依赖于集合的,因此出现并发修改异常。

  解决的办法就是只用迭代器来操作集合(ListIterator中有add方法),或者只用集合的方法来遍历和操作集合。下面是例子。只不过根据他们的实现不用,用ListIterator添加时,元素被添加在当前元素下面,而用集合本身的add()方法添加时,新元素被添加在集合的最后。

  方法1:使用List迭代器的add()方法。

package com.zhang.test;

import java.util.*;

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("java");
        // 迭代器遍历
        for(ListIterator it = list.listIterator(); it.hasNext(); ) {
            String str = (String) it.next();
            if(str.equals("world")) {
                it.add("c++");
            }
        }
        // 最后输出
        System.out.println(list); // [hello, world, c++, java]
    }
}

  方法2:使用List集合的方法来遍历和操作。

package com.zhang.test;

import java.util.*;

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("java");
        // 用集合的size()和get()遍历,并修改
        for(int i = 0; i < list.size(); i++) {
            if(list.get(i).equals("world")) {
                list.add("c++");
            }
        }
        // 最后输出
        System.out.println(list); // [hello, world, java, c++]
    }
}

3.4 List集合常用子类

  List集合常用的子类有ArrayList(前面一直用的)、LinkedList和Vector。详细解释如下:

  (1)ArrayList:底层的数据结构是动态数组。特点是查询速度快(允许直接用索引来查找对应的元素)、增删慢(插入和删除操作涉及元素移动和内存操作)。ArrayList是线程不安全的类,效率较高。

  (2)LinkedList:底层数据结构是用链表存储数据。链表的特点是查询慢(需要遍历节点查找),增删快(只需记住上个节点和下个节点)。LinkedList也是线程不安全的类,效率较高。LinkedList中一些特有的方法(需要针对头尾的操作):

  addFirst(E e)、addLast(E e)、getFirst()、getLast()、removeFirst()和removeLast()等。

  (3)Vector:是线程安全的ArrayList。用法和ArrayList相似。说明一下,Vector中提供了一个elements()实例方法,返回的是一个Enumeration对象。此对象用法和Iterator一致,只不过方法名不一样,方法原型为:

  public boolean hasMoreElement();和public Object nextElement()。

  除此之外,List的实现类还有Stack,就是堆栈结构。其实List就是数据结构里面的线性表,使用时,结合数据结构的知识,就能很好的选择使用,并能迅速的了解不同结构中提供的方法的含义。因此推荐学习完C语言之后研究下数据结构和算法。

4. 泛型(Generic)

4.1 引入泛型

  集合能存储不同的数据类型,但这个特点可能会出现安全问题。因为操作集合元素时,要先拿到该元素,用集合(或迭代器)的方法只能拿到Object类型元素,因为集合能存储任意类型,集合本身无法知道自己所存储的究竟是什么类型。因此我们拿到Object类型元素时再将元素强制转换成需要的类型,这时可能发生转换错误。

  例子:

package com.zhang.test;

import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add(new Student());
        // 该集合中存储了字符串,最后一个元素存储了学生对象
        // 遍历取出元素
        for(int i = 0; i < list.size(); i++) {
            // 强制转换为String类型
            String str = (String) list.get(i);
            System.out.println(str);
        }
    }
}

  运行程序会出现“ClassCastException”,即类型转换异常,这是因为集合中还存储了Student对象。这样取出元素会有安全性问题,这种问题在编译时是无法发现的,因此IDE只能提示警告。虽然能通过类型检查(如instanceof)来判断元素的类型,但是这样效率较低。处理这样的问题,一般是直接限定该集合只能存储一种特定类型的对象。

  而泛型就是用来限定该集合只能存储一种特定数据类型的。

  对集合使用泛型的格式:

  集合类 集合变量 = new 集合类();这里的就是泛型,E就指定该集合能存储何种数据类型。比如:

  ArrayList arrayList = new ArrayList();就表示arrayList集合只能存储String类型的数据。

  需要注意的是,这里的E只能写引用类型,不能写基本数据类型,比如不能写成,而只能写成。以后就常用泛型了,使用泛型后,IDE环境中也不会出现警告了。现在也能知道API中等的含义了。

  下面体会下泛型的使用:

package com.zhang.test;

import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        ArrayList<Student> stuList = new ArrayList<Student>();
        stuList.add(new Student("张三", 13));
        stuList.add(new Student("李四", 14));
        // 遍历
        for(int i = 0; i < stuList.size(); i++) {
            // 使用泛型无需类型转换
            Student stu = stuList.get(i);
            System.out.println(stu);
        }
    }
}

  上面的例子中体会到,使用泛型后,集合中只能存特定类型的数据,并且取出元素时,会自动根据泛型得到对应的数据类型,无需类型转换,即如果这是用String泛型的集合,那么用get()方法得到的就会是String类型的数据。

  很多地方使用泛型更加友好,比如JDK中提供的一个接口Comparable:

package java.lang;

public interface Comparable {
    public int compareTo(Object o);
}

  在JDK 1.5之后,此接口被改成了泛型:

package java.lang;

public interface Comparable<T> {
    public int compareTo(T o);
}

  这样的话,这里的泛型类型就能被以后具体的类型所替换,避免了从特定类型转到Object类型,再从Object类型转到具体类型的过程。(学了后面的泛型方法、泛型类等后理解更深刻)

4.2 泛型方法

  泛型方法就是在方法中使用泛型,实际也就是在方法形参和返回值中使用泛型,使形参接受指定类型的参数,然后返回该类型的数据,这样可以使方法适应不同的数据类型,而且不用像使用Object那样,进行强制转换。

  可以只在形参中使用泛型,返回值可以不用泛型,而是返回特定的一种类型,但是一般不只在返回值中使用泛型,因为没有什么意义。

  泛型相当于将类型当作变量处理。规范泛型的定义一般是一个大写的任意字母(实际上任意合法的变量名都是可以的)。在方法中使用泛型的格式:

  public <声明泛型的变量> 返回值类型 方法名(数据类型和形参…)。尖括号中就是声明泛型的变量,声明好了后,就能在方法中使用该变量作为泛型类型了,比如:

  public T getModel(T t) {…}。

  上面的方法就先在尖括号中声明了泛型变量T,这个T就相当于一个类型了,然后在返回值、参数和方法体中就能使用这个T类型了。方法说明此方法接受一个泛型类型T参数,方法返回值是也是类型T。这个T到底是什么类型呢,只有在调用方法时,传递参数时才知道。也就是调用方法时传递什么类型,这个T就是什么类型。

  实际上泛型就是实现参数化类型,把类型当作参数一样的传递。

  例子:

package com.zhang.test;

import java.util.Date;

public class Demo {
    public static void main(String[] args) {
        Student stu = getObj(new Student("张三", 12));
        System.out.println(stu);
        // 传递什么类型进去,就返回什么类型
        Date date = getObj(new Date(System.currentTimeMillis()));
        System.out.println(date);
    }
    // 这是一个静态的泛型方法,直接返回传递进来的参数
    public static <E> E getObj(E e) {
        return e;
    }
}

4.3 泛型类

  当一个类中多个方法都用到了泛型方法时,特别是在类中也需要使用泛型时,那么就可将泛型声明到类上,使用泛型类。

  泛型类是在类名后声明泛型,也是在尖括号中。这时,泛型的类型就是在创建此对象时使用的泛型类型。当然,类中的静态成员不能使用类中定义的泛型,如果静态成员要使用泛型,只能独立声明泛型,因为静态成员是在类对象存在之前就存在的,不能根据创建对象时使用的类型来决定静态泛型的类型。

  泛型类的格式:

  public class 类名<声明泛型的变量> {

    …

  }

  例子:

  (1)GenericDemo类(泛型类)

package com.zhang.test;

// 类上声明泛型
public class GenericDemo <E> {
    // 类中可以使用泛型类型
    private E obj; // 泛型成员
    // get和set方法
    public void setObj(E obj) {
        this.obj = obj;
    }
    public E getObj() {
        return this.obj;
    }

    // 实例方法,展示其中维护的泛型类型对象
    public void showObj() {
        System.out.println(this.getObj().toString());
    }

    // 如果是静态方法,同样还要定义泛型类型,不能使用实例成员
    public static <T> void showArr(T[] arr) {
        // 用于展示任意类型的数组
        for(int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

  (2)主类

package com.zhang.test;

import java.util.Date;

public class Demo {
    public static void main(String[] args) {
        // 使用泛型类。
        // 创建对象时,new后面的泛型类型可省略,只要声明时带有泛型类型即可
        GenericDemo<Student> stu = new GenericDemo<>();
        stu.setObj(new Student("张三", 14));
        stu.showObj();
        // 使用静态方法
        String[] strArr = {"hello", "world", "java"};
        GenericDemo.showArr(strArr);
    }
}

  创建对象时,如果不指定泛型的具体类型,那么默认类型是Object(和创建集合对象时一样)。

4.3.1 泛型类的继承

如果一个类继承了泛型类,那么下面的方式:

(1)子类不使用父类泛型,那么直接:

  public class SubClass extends GenericDemo {

    …

  }

  这时在子类中调用父类的泛型方法,其类型是Object。

  或者子类使用自己定义的泛型,那么语法是:

  public class SubClass extends GenericDemo {…}

(2)子类使用和父类一样的泛型:

  public class SubClass extends GenericDemo {…};

(3)子类继承父类的指定类型:

  public class SubClass extends GenericDemo{…}

  当然这时子类也能使用自己的泛型:

  public class SubClass extends GenericDemo{…}

4.4 泛型接口

  接口泛型的声明也是在接口名后面的尖括号中。比如:

package com.zhang.test;

public interface GenericInterface <E> {
    void show(E e);
}

  那么泛型接口的实现类,可以还是用泛型,也可以处理具体的类型,比如:

  (1)方法1:还是用泛型,处理任意类:

package com.zhang.test;
// 声明泛型类型的名字可以和接口的名字不一样。只要他们一致就表示处理的还是泛型
public class ImplDemo1<T> implements GenericInterface<T> {
    public void show(T t) {
        System.out.println(t);
    }
}

  然后主类创建实现类对象时指定具体类即可。

  (2)方法2:处理具体的类型:

package com.zhang.test;

public class ImplDemo2 implements GenericInterface<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

  当然,如果直接implements GenericInterface,那么具体的类就是Object,此外,实现类也能使用自己的泛型,即:

  public class ImplDemo2 implements GenericInterface。但是就是不能同时使用接口的泛型,又使用自己的泛型,泛型类中也是这样。

4.5 泛型通配符

  先看一个需求:写一个方法,此方法能接受任意泛型的集合对象,然后将对象打印。

  由于能接受任意类型,那么方法参数可定义为Collection,即:

public static void print(Collection<Object> c) {
    for(Iterator<Object> it = c.iterator(); it.hasNext(); ) {
        Object obj = it.next();
        System.out.println(obj);
    }
}

  我们的想法是,传递一个ArrayList对象或者一个ArrayList对象进去,都能打印出集合中的元素,即能:

public static void main(String[] args) {
    ArrayList<String> arr = new ArrayList<String>();
    arr.add("hello");
    print(arr);
}

  但是print(arr)这行报错,并不能通过编译,原因是print函数已经限定了类型是Object,因此arr是不能作为参数的。

  解决的办法有:1.就传递给print方法Coolection对象,但是这样可能要将原数据进行转换。2.print方法不使用泛型,即形参直接是Collection c。但是这样没有使用泛型会有警告,JDK不推荐。

  而通配符?就是用于解决这个问题的,可以将?作为泛型类型,这个?就代表任意类型。如果传递进来的泛型是String,那么?就代表String,如果传递进来的泛型是Integer,那么?就代表Integer。因此最终可用这样的代码:

package com.zhang.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> arr = new ArrayList<String>();
        arr.add("hello");
        ArrayList<Integer> num = new ArrayList<>();
        num.add(1);
        num.add(23);
        print(arr);
        print(num);
    }

    public static void print(Collection<?> c) {
        for(Iterator<?> it = c.iterator(); it.hasNext(); ) {
            Object obj = it.next();
            // 这里还是用Object是理所当然的,因为不知道具体类型
            // 使用?为了使用泛型并且不用将原来对象进行转换
            System.out.println(obj);
        }
    }
}

  通配符用来限定类型的范围。单单的?没有类型的限定,是没有边界的,可以用下面的方式进行限定:

  

package com.zhang.test;

import java.util.ArrayList;
import java.util.Collection;

public class Demo {
    public static void main(String[] args) {
        //?通配符可以是任意类型
        Collection<?> e1 = new ArrayList<Animal>();
        Collection<?> e2 = new ArrayList<Dog>();
        //? extends E,可以是E和E的子类
        Collection<? extends Animal> e3 = new ArrayList<Animal>();
        Collection<? extends Animal> e4 = new ArrayList<Dog>();
        //? super E,可以是E及其父类
        Collection<? super Animal> e5 = new ArrayList<Animal>();
        Collection<? super Animal> e6 = new ArrayList<Dog>();//错误
    }
}
//先写两个具有继承关系的类
class Animal{
}
class Dog extends Animal{
}

5. 补充

  泛型允许程序员在编写代码时,就限制集合的处理类型,从而把原来程序运行时可能发生的问题,转变为编译时问题,以此提高程序的可读性和稳定性。

  注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上就阻止向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受影响,这个过程称为“擦除”。

  泛型的基本术语:

  以ArrayList为例:<>念:typeof。

  ArrayList中的E称为类型参数变量

  ArrayList中的Integer称为实际类型参数

  整个称为ArrayList泛型类型

  整个ArrayList称为参数化的类型ParameterizedType


本篇文章已结束,若您喜欢,欢迎支持我的工作。

支付宝

微信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值