十五、泛型(完结)


十五、泛型


15.1 泛型的引入

15.1.1 集合添加指定类型元素问题

需求:请编写程序,在 ArrayList 中,添加3个 Dog 对象,Dog 对象含有 nameage,并输出 nameage(要求使用getXxx())

15.1.2 使用传统方法解决

package com.hspedu.generic;

import java.util.ArrayList;

/**
 * @author Carl Zhang
 * @description
 * @date 2021/12/12 13:17
 */
public class Generic {
    public static void main(String[] args) {
        //需求 请编写程序,在ArrayList中,添加3个Dog对象
        //Dog对象含有name和age,并输出name和age(要求使用getXxx())

        //传统方法:
        ArrayList arrayList = new ArrayList();
        arrayList.add(new Dog("二狗", 2));
        arrayList.add(new Dog("小白", 1));
        arrayList.add(new Dog("老黑", 5));

        arrayList.add("赵四"); //

        for (Object o : arrayList) {
            Dog o1 = (Dog) o; //遍历到赵四的时候,抛异常 ClassCastException
            System.out.println("name = " + o1.getName() + ", age = "
                    + o1.getAge());
        }

        //问题:1. 无法对集合里传入的元素类型做限制
        //      2. 每次使用都要进行类型转换,数据量大的时候影响效率
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = 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;
    }
}

15.1.2 传统方法的弊端

  • 无法对集合里传入的元素类型做限制
  • 每次使用都要进行类型转换,数据量大的时候影响效率

15.1.3 使用泛型解决

package com.hspedu.generic;

import java.util.ArrayList;

/**
 * @author Carl Zhang
 * @description
 * @date 2021/12/12 20:39
 */
public class Generic02 {
    public static void main(String[] args) {
        //用泛型解决问题
        ArrayList dogs = new ArrayList<>();
        dogs.add(new Dog("二狗", 2));
        dogs.add(new Dog("小白", 1));
        dogs.add(new Dog("老黑", 5));

        //dogs.add("赵四"); //添加别的类型编译报错

        for (Dog dog :dogs) { //可以直接获取Dog 类型元素
            System.out.println("name = " + dog.getName() + ", age = "
                    + dog.getAge());

        }

        //好处:
        //1. 可以在编译时对集合里的元素类型进行限制
        //2. 使用时不用每次都向下转型,可以直接获取对应类型元素,效率提高
    }
}

15.2 泛型的理解与好处

15.2.1 泛型的好处

  • 编译时可以对添加的元素类型进行检查,提高安全性,防止 ClassCastException 异常
  • 使用时不用每次都向下转型,可以直接获取对应类型元素,效率提高

15.2.2 泛型的介绍

  • 泛型又称参数化类型,是 Jdk5.0 出现的新特性,解决数据类型的安全性问题
  • 类声明或实例化时只要指定好需要的具体的类型即可。
  • Java 泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁健壮
  • 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
package com.hspedu.generic;

/**
 * @author Carl Zhang
 * @description
 * @date 2021/12/12 20:59
 */
public class Generic03 {
    public static void main(String[] args) {
        //解读:
        //1. class Person 中 E 表示泛型标识,可以接收任意类型,E也可以用其他字母表示
        //2. new Person(); 表示将String类型传递给E , 指定E的类型为String
        //3. E 的具体类型在创建对象的时候就指定,即在编译阶段就会确定 E 的类型
        Person person = new Person();
        person.name = "赵四";
        //person.name = 33; //传入不同的类型,编译报错
        person.show();
    }
}

//泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型
class Person { //在类声明时通过标识符 E 表示类中对应变量的类型
    E name; //属性name的类型是 E,E的具体类型在实例化Person对象的时候指定

    public void p(E e) { //参数e 的类型是E
        System.out.println(e);
    }

    public E p2() { //返回值的类型是E
        return name;
    }

    public void show() {
        System.out.println(name.getClass()); //通过getClass() 查看name的具体类型
    }
}

15.3 泛型的语法

15.3.1 泛型的语法

  • 声明语法:interface 接口 {}class 类 {}
    • TKV 不代表具体值,只表示类型
    • 任意字母、任意数量。常用 TType 缩写
  • 实例化语法:在类名后面指定类型参数的值
    • 例:HashSet students = new HashSet();

15.3.2 案例

需求:

  1. 创建3个学生对象
  2. 放入到 HashSet 中学生对象,使用
  3. 放入到 HashMap 中,要求 KeyString nameValue 就是学生对象
  4. 使用两种方式遍历
package com.hspedu.generic;

import javafx.scene.chart.ValueAxis;

import java.util.*;

/**
 * @author: Carl Zhang
 * @create: 2021-12-13 09:23
 */
public class GenericExercise01 {
    public static void main(String[] args) {
        //1.创建3个学生对象
        Student stu1 = new Student("赵四", 25);
        Student stu2 = new Student("马大帅", 23);
        Student stu3 = new Student("德彪", 26);

        //2.放入到HashSet中学生对象,使用.
        HashSet students = new HashSet();
        students.add(stu1);
        students.add(stu2);
        students.add(stu3);

        //forEach遍历
        System.out.println("forEach遍历");
        for (Student stu : students) {
            System.out.println(stu);
        }
        System.out.println();

        //Iterator 遍历
        System.out.println("Iterator 遍历");
        Iterator iterator = students.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next);
        }
        System.out.println();

        //3.放入到HashMap中,要求Key 是String name,Value就是学生对象
        HashMap studentHashMap = new HashMap();
        studentHashMap.put(stu1.getName(), stu1);
        studentHashMap.put(stu2.getName(), stu2);
        studentHashMap.put(stu3.getName(), stu3);

        //4.使用两种方式遍历
        //通过entrySet遍历
        System.out.println("通过entrySet遍历");
        Set> stuEntries = studentHashMap.entrySet();
        /*
        //new 对象的时候指定了K、V的类型,所以Set>编译会通过,通过.var会自动填充
        public Set> entrySet() {
            Set> es;
            return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
        }

        final class EntrySet extends AbstractSet> {.....} AbstractSet 实现了 Set接口
        */
        System.out.println("stuEntries.getClass() = " + stuEntries.getClass()); //HashMap$EntrySet

        for (Map.Entry stuEntry : stuEntries) {
            System.out.println(stuEntry.getValue());
        }
        System.out.println();

        //通过values遍历
        System.out.println("通过values遍历");
        Collection values = studentHashMap.values();
        /*
        public Collection values() {
            Collection vs = values;
            if (vs == null) {
                vs = new Values();
                values = vs;
            }
            return vs;
        }
        */

        Iterator iterator1 = values.iterator();
        /*
        //debug查看源码:最终返回的是一个HashMap$ValueIterator类型的iterator,V = Student
        public final Iterator iterator()     { return new ValueIterator(); }

        final class ValueIterator extends HashIterator
            implements Iterator {
            public final V next() { return nextNode().value; }
        }
        * */
        System.out.println("iterator1.getClass() = " + iterator1.getClass()); //HashMap$ValueIterator

        while (iterator1.hasNext()) {
            Student next = iterator1.next();
            System.out.println(next);
        }
        System.out.println();

        //查看运行类型
        System.out.println("stuEntries.getClass() = " + stuEntries.getClass()); //HashMap$EntrySet

    }
}


15.3.3 泛型使用细节

  • 泛型标识符只能接收引用类型
  • 在给泛型指定具体类型后,可以传入该类型或者其子类类型
  • 泛型的使用形式
    • HashMap studentHashMap = new HashMap();
    • Map studentHashMap = new HashMap();
    • HashMap studentHashMap = new HashMap<>(); 推荐
  • 不指定类型,默认类型是 Object
    • ArrayList arrayList = new ArrayList(); 等同于 ArrayList objects = new ArrayList<>();

      15.4 泛型使用案例

      image.png

      package com.hspedu.generic;
      
      import java.util.ArrayList;
      import java.util.Comparator;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 10:57
       */
      public class GenericExercise02 {
          public static void main(String[] args) {
              //5.1 new Employee(name, sal, birthday)*3 将元素添加到集合 new ArrayList
              //5.2 arrayList.sort(new Comparator(){
              //      public int compare(Employee emp1, Employee emp2) {
              //          //声明变量保存排序结果
              //          int res = -1;
              //          //调用String的compareTo()方法
              //          if ((res = emp1.getName().compareTo(emp2.getName())) != 0)
              //              return res
              //          //比较生日日期 --> 实现Comparable接口,重写compareTo方法
              //          //比较规则:按照年月日顺序比较,返回年月日的差值
              //          return emp1.getBirthday().compareTo(emp2.getBirthday())
              //      }
              // })
              ArrayList employees = new ArrayList<>();
              employees.add(new Employee("赵四", 20000, new MyDate("1999", "01", "01")));
              employees.add(new Employee("本山", 10000, new MyDate("1999", "01", "01")));
              employees.add(new Employee("刘能", 20000, new MyDate("1999", "01", "01")));
              employees.add(new Employee("赵四", 80000, new MyDate("1998", "01", "01")));
              employees.add(new Employee("刘能", 30000, new MyDate("1999", "02", "01")));
      
              employees.sort(new Comparator() {
                  @Override
                  public int compare(Employee emp1, Employee emp2) {
                      //声明变量保存排序结果
                      int res = -1;
                      //调用String的compareTo()方法比较name
                      //res = emp1.getName().compareTo(emp2.getName());
                      if ((res = emp1.getName().compareTo(emp2.getName())) != 0)
                          return res; //name不同就返回name的比较结果
      
                      //比较生日日期 --> 实现Comparable接口,重写compareTo方法
                      //比较规则:按照年月日顺序比较,返回年月日的差值
                      //封装后,将来可维护性和复用性,就大大增强
                      return emp1.getBirthday().compareTo(emp2.getBirthday());
                  }
              });
      
              for (Employee o :employees) {
                  System.out.println(o);
              }
      
          }
      }
      
      package com.hspedu.generic;
      
      import java.util.Objects;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 11:04
       */
      
      public class MyDate implements Comparable {
          //MyDate类 ,year,month,day属性 继承LocalDate类
          //重写MyDate类的hashCode() ,equals()
          private String year;
          private String month;
          private String day;
      
          public MyDate(String year, String month, String day) {
              this.year = year;
              this.month = month;
              this.day = day;
          }
      
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (!(o instanceof MyDate)) return false;
              MyDate myDate = (MyDate) o;
              return Objects.equals(year, myDate.year) &&
                      Objects.equals(month, myDate.month) &&
                      Objects.equals(day, myDate.day);
          }
      
          @Override
          public int hashCode() {
              return Objects.hash(year, month, day);
          }
      
          @Override
          public String toString() {
              return
                      "year:" + year + '\'' +
                              ", month:'" + month + '\'' +
                              ", day:'" + day + '\'' +
                              '}';
          }
      
          //比较生日日期 --> 实现Comparable接口,重写compareTo方法
          //比较规则:按照年月日顺序比较,返回年月日的差值
          @Override
          public int compareTo(MyDate o) {
              int res = -1;
              int year1 = Integer.parseInt(year);
              int year2 = Integer.parseInt(o.year);
              //如果年不同,就return year 1 - year2
              if ((res = year1 - year2) != 0)
                  return res;
      
              //比较月
              int month1 = Integer.parseInt(month);
              int month2 = Integer.parseInt(o.month);
              if ((res = month1 - month2) != 0)
                  return res;
      
              //比较日
              int day1 = Integer.parseInt(day);
              int day2 = Integer.parseInt(o.day);
              return day1 - day2;
          }
      }
      
      public class Employee {...}
      

      15.5 自定义泛型


      15.5.1 自定义泛型类

      语法:class Tiger {}

      要点:

      1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
      2. TRM 泛型的标识符,一般是单个大写字母
      3. 泛型标识符可以有多个
      4. 普通成员可以使用泛型 (属性、方法)
      5. 泛型类型不能直接实例化
      6. 静态成员不能使用类的泛型(方法、属性)
      7. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
      8. 如果在创建对象时,没有指定类型,默认为 Object
      package com.hspedu.generic;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 13:28
       */
      public class CustomGeneric {
          public static void main(String[] args) {
              Tiger tiger = new Tiger<>();
          }
      }
      
      //1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
      //2, T, R, M 泛型的标识符, 一般是单个大写字母
      //3. 泛型标识符可以有多个.
      class Tiger {
          String name;
          //4. 普通成员可以使用泛型 (属性、方法)
          T t;
      
          public T m1(R r, M m) {
              //5. 泛型类型不能直接实例化
              //   数组:因为new对象的时候不知道具体类型,无法初始化对应空间
              //T[] ts = new T[3]; //提示:类型参数'T'不能直接实例化
              return t;
          }
      
          //6. 静态成员不能使用类的泛型(方法、属性)
          //   因为静态成员是类相关的,如果静态成员使用了泛型,就无法初始化
          //static T t2;
          //public static void m2(T t, R r) { }
      }
      //7. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
      //8. 如果在创建对象时,没有指定类型,默认为Object
      

      15.5.2 自定义泛型接口

      语法:interface IA {}


      要点:

      • 接口中,静态成员也不能使用泛型
      • 泛型接口的类型, 在继承接口或者实现接口时确定
      • 没有指定类型,默认为 Object
      package com.hspedu.customgeneric;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 14:10
       */
      public class CustomGenericInterface {
      
      }
      
      //1. 接口中,静态成员也不能使用泛型
      interface IA {
          String D = "无名氏";
      
          //T T1 = "test"; //接口的属性默认是public static final
      
          void getT(T t); //接口中普通方法可以使用泛型
          void getR(R r);
      
      }
      
      //2. 泛型接口的类型, 在继承接口或者实现接口时确定
      class AA implements IA { //表示类AA实现接口IA时,指定了T是String类型,R是Double类型
          @Override
          public void getT(String s) {
              System.out.println(s.getClass());
          }
      
          @Override
          public void getR(Double d) {
              System.out.println(d.getClass());
          }
      }
      
      //3. 没有指定类型,默认为 Object
      interface BB extends IA { //接口BB继承接口IA,没有指定T,R的类型,默认用Object
          @Override
          void getT(Object o);
      
          @Override
          void getR(Object o);
      }
      

      15.5.3 自定义泛型方法

      语法:修饰符 返回值类型 方法名(参数列表) {}

      要点:

      • 泛型方法,可以定义在普通类中,也可以定义在泛型类中
      • 当泛型方法被调用时传入参数,类型会确定
      • public void eat(T t, E e),修饰符后没有 eat 方法不是泛型方法,而是使用了泛型
      package com.hspedu.customgeneric;
      
      import com.hspedu.generic.Student;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 14:29
       */
      public class CustomGenericMethod {
          public static void main(String[] args) {
              AAA aaa = new AAA();
              //2.当泛型方法被调用时传入参数,类型会确定
              aaa.methodA("赵四", 11); //t的类型class java.lang.String,
                                           //e的类型class java.lang.Integer
              String s = aaa.methodB("赵小四", 11);
              System.out.println(s);
      
              BBB bbb = new BBB<>();
              bbb.eat("炒粉", 33d);
              bbb.methodA("本山", 22); //q的类型class java.lang.String,
                                            // w的类型class java.lang.Integer
              String s1 = bbb.methodB("河粉", 2d);
              System.out.println(s1);
          }
      }
      
      //1.泛型方法,可以定义在普通类中,也可以定义在泛型类中
      class AAA {
          public  void methodA(T t, E e) { //定义在普通类的泛型方法
              System.out.println("t的类型" + t.getClass() + ", e的类型" + e.getClass());
          }
      
          public  String methodB(T t, E e) { //定义在普通类的泛型方法
              return "t的类型" + t.getClass() + ", e的类型" + e.getClass();
          }
      }
      
      class BBB {
          public  void methodA(Q q, W w) { //定义在泛型类的泛型方法
              System.out.println("q的类型" + q.getClass() + ", w的类型" + w.getClass());
          }
      
          //可以使用自己声明的泛型,也可以使用类声明的泛型
          public  String methodB(T t, E e) { //定义在泛型类的泛型方法
              return "t的类型" + t.getClass() + ", e的类型" + e.getClass();
          }
      
          //3.public void eat(Ee)0,修饰符后没有eat方法不是泛型方法,而是使用了泛型
          public void eat(T t, E e) { //修饰符后没有 eat方法不是泛型方法,而是使用了泛型
              System.out.println("t的类型" + t.getClass() + ", e的类型" + e.getClass());
          }
      }
      
      

      15.6 泛型的继承和通配符


      15.6.1 泛型的继承和通配符介绍

      • 泛型没有继承性
      • <?> 表示任意类型都能接收
      • <? extends A> 表示能接收 A 类型及 A 的子类类型,规定了泛型的上限
      • <? super A> 表示能接收 A 类型及 A 的父类类型,规定了泛型的上限

      15.6.2 案例

      package com.hspedu.genericextend;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 15:08
       */
      public class GenericExtend {
          public static void main(String[] args) {
              //1. 泛型没有继承性
              //List strings = new ArrayList(); //报错
      
              List list1 = new ArrayList<>();
              List list2 = new ArrayList<>();
              List list3 = new ArrayList<>();
              List list4 = new ArrayList<>();
              List list5 = new ArrayList<>();
      
              //能传入任意的泛型类型List
              methodA(list1);
              methodA(list2);
              methodA(list3);
              methodA(list4);
              methodA(list5);
      
              //methodB(list1); //报错 需要 List<? extends AA>, 提供了List
              //表示集合的泛型类型为AA或继承了AA的类型
              //methodB(list1); //报错,Object没继承AA
              //methodB(list2); //报错,String没继承AA
              methodB(list3);
              methodB(list4);
              methodB(list5);
      
              //表示集合的泛型类型为BB或BB的父类,不限于直接父类
              methodC(list1);
              //methodC(list2); //报错,String 不是BB的父类或非直接父类
              methodC(list3);
              methodC(list4);
              //methodC(list5); //报错,String 不是BB的父类或非直接父类
      
          }
      
          //2. <?> 表示任意类型都能接收
          public static void methodA(List<?> list) { //List<?>表示任意类型都能接收
              for (Object o : list) { // 通配符,取出时,就是 Object
                  System.out.println("o.getClass() = " + o.getClass());
              }
          }
      
          //3. <? extends A>表示能接收 A 类型及 A 的子类类型,规定了泛型的上限
          //List<? extends List> 表示能接收 List 类型及 List 的子类类型
          public static void methodB(List<? extends AA> lists) {
              for (Object o : lists) {
                  System.out.println("o.getClass() = " + o.getClass());
              }
          }
      
          //4. <? super A>表示能接收 A 类型及 A 的父类类型,规定了泛型的上限
          //List<? super ArrayList> list 表示能接收 ArrayList 类型及 ArrayList 的父类
          public static void methodC(List<? super BB> list) {
              for (Object o : list) {
                  System.out.println("o.getClass() = " + o.getClass());
              }
          }
      }
      
      class AA {
      }
      
      class BB extends AA {
      }
      
      class CC extends BB {
      }
      
      

      15.7 JUnit 使用


      15.7.1 JUnit 引入

      • 一个类有很多功能代码需要测试,为了测试,就需要写入到 main 方法中
      • 如果有多个功能代码测试,就需要来回注销,切换很麻烦
      • 如果可以直接运行一个方法,就方便很多,并且可以给出相关信息,就好了->JUnit
      • image.png

      15.7.2 JUnit介绍

      • JUnit 是一个 Java 语言的单元测试框架
      • 多数 Java 的开发环境都已经集成了 JUnit 作为单元测试的工具

      15.7.3 使用案例

      package com.hspedu.junit_;
      
      import org.junit.jupiter.api.Test;
      
      /**
       * @author: Carl Zhang
       * @create: 2021-12-13 16:47
       */
      public class JUnit_ {
          public static void main(String[] args) {
              //传统方法 每次使用就要创建对象,并且注释其他地方
              //new JUnit_().method1();
              //new JUnit_().method2();
          }
      
          @Test //第一次添加@Test会报红,Alt + Enter 弹出提示,选中5.xx版本,点确定自动从仓库下载并配置
          public void method1() {
              System.out.println("method1被调用");
          }
      
          @Test
          public void method2() {
              System.out.println("method2被调用");
          }
      }
      

      15.7.4 泛型作业

      package com.hspedu.homework;
      
      import java.util.*;
      
      /**
       * @author Carl Zhang
       * @description 定义个泛型类DAO,在其中定义一个Map成员变量,Map的键为String类型,值为T类型。
       * @date 2021/12/13 21:15
       */
      @SuppressWarnings("AlibabaClassNamingShouldBeCamel")
      public class DAO {
          Map map = new HashMap<>();
      
          /**
           * 分别创建以下方法:
           * (1)public void save(String id,T entity):保存T类型的对象到Map成员变量中
           * put(id, entity)
           * */
          public boolean save(String id, T entity) {
              //返回添加结果
              return map.put(id, entity) == null;
          }
      
          /**
           * (2)public T get(String id):从map中获取id 对应的对象
           * get(id)
           * */
          public T get(String id) {
              return map.get(id);
          }
      
          /**
           * (3)public void update(String id,T entity):替换map 中key为id的内容,改为entity 对象
           * put(id, entity)
           */
          public boolean update(String id, T entity) {
              //返回修改结果
              return map.put(id, entity) == entity;
          }
      
          /**
           * (4)public List list):返回map中存放的所有T对象
           * keySet()
           */
          public List list() {
              ////class java.util.HashMap$Values
              // System.out.println(map.values().getClass());
              // java.util.HashMap$Values cannot be cast to java.util.List
              //return (List) map.values();
      
              //方式二:通过keySet() 获取每个key,遍历keySet,通过key获取value,封装到List集合里
              Set keySet = map.keySet();
              List ts = new LinkedList<>();
              for (String o :keySet) {
                  T t = map.get(o);
                  ts.add(t);
              }
      
              return ts;
          }
      
          /**
           * (5)public void delete(String id):删除指定id对象
           * remove(key)
           */
          public void delete(String id) {
              map.remove(id);
          }
      }
      
      package com.hspedu.homework;
      
      /**
       * @author Carl Zhang
       * @description 该类包含:private成员变量(int类型)id,age;(String 类型)name。
       * @date 2021/12/13 21:15
       */
      public class User {
          private int id;
          private int age;
          private String name;
      
          public User(int id, int age, String name) {
              this.id = id;
              this.age = age;
              this.name = name;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", age=" + age +
                      ", name='" + name + '\'' +
                      '}';
          }
      }
      
      package com.hspedu.homework;
      
      import org.junit.jupiter.api.Test;
      
      import java.util.List;
      
      @SuppressWarnings("AlibabaClassNamingShouldBeCamel")
      /**
       * 创建 DAO类的对象,分别调用其 save、get、update、list、delete 方法来操作User对象,
       * 使用Junit 单元测试类进行测试。
       */
      class DAOTest {
          @Test
          public void testMethod() {
              DAO userDAO = new DAO<>();
              userDAO.save("101", new User(101, 33, "赵四儿"));
              userDAO.save("102", new User(102, 22, "刘能儿"));
              userDAO.save("103", new User(103, 24, "范德彪"));
              userDAO.save("105", new User(103, 24, "谢广坤"));
              userDAO.save("106", new User(103, 24, "马大帅"));
      
              User user = userDAO.get("105");
              System.out.println("user = " + user);
      
              userDAO.update("101", new User(101, 26, "谢大脚"));
              System.out.println(userDAO.get("101"));
      
              //java.util.HashMap$Values cannot be cast to java.util.List
              List list = userDAO.list();
      
              for (User o :list) {
                  System.out.println(o);
              }
              System.out.println();
      
              userDAO.delete("102");
              System.out.println("删除之后:");
              for (User o :list) {
                  System.out.println(o);
              }
          }
      }