第十三章、 泛型

第十三章、 泛型

13.1 泛型语法

13.1.1 泛型的引入与入门

  1. 看一个需求
    (1). 请编写程序,在ArrayList中,添加3个Dog对象
    (2). Dog对象含有name和age,并输出name和age(要求使用getXxx())
    使用传统的方法来解决 -->引出泛型
package chapter13.generic_;

import java.util.ArrayList;

/**
 * @aim java基础学习
 * @note java笔记
 */
@SuppressWarnings({"all"})
public class Generic01 {
    public static void main(String[] args) {
        //传统方法
        ArrayList arrayList = new ArrayList();
        arrayList.add(new Dog("小呆呆", 2));
        arrayList.add(new Dog("超人强", 3));
        arrayList.add(new Dog("波比", 4));

        //假如程序员不小心把其他类型的数据放入这个arrayList集合中,明显会发生ClassCastException异常,
        //这是一个运行时异常,编译器发现不了。
//        arrayList.add(123);
        //使用增强for输出
        for (Object o :arrayList) {
            //默认是Object,需要进行向下转型为Dog
            Dog dog =(Dog) o;
            System.out.println(dog.getName()+"-"+dog.getAge());
        }
        /*
        小呆呆-2
        超人强-3
        波比-4
         */
    }
}
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 int getAge() {
        return age;
    }
}
  1. 使用传统方法的问题分析上面代码出现的问题
    (1). 不能对加入到集合ArrayList中的数据类型进行约束(不安全)
    (2). 遍历的时候,需要进行类型转换,如果集合中的数据量比较大时,对效率是有影响的。

  2. 泛型快速体验-用泛型来解决前面的问题。

package chapter13.improve_;

import java.util.ArrayList;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Generic02 {
    public static void main(String[] args) {
        //使用泛型进行修改
        // ArrayList<Dog>表示存放到 ArrayList集合中的元素类型是Dog类型
	// public class ArrayList<E> {} 其中E就是泛型,也就是形参,而Dog就是实参
        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog("小呆呆", 2));
        dogs.add(new Dog("超人强", 3));
        dogs.add(new Dog("波比", 4));
        //当不小心把其他类型加入到ArrayList,就会发生编译异常
//        dogs.add(123);

        //在使用增强for的时候,也可以直接使用Dog类型,而不是Object,就省略向下转型
        for (Dog dog :dogs) {
            System.out.println(dog.getName()+"-"+dog.getAge());
        }
        /*
        小呆呆-2
        超人强-3
        波比-4
         */
    }
}
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 int getAge() {
        return age;
    }
}
  1. 泛型的好处
    (1). 编译时,检查添加元素的类型,提高了安全性
    (2). 减少了类型转换的次数,提供效率
    (3). 若不使用泛型,在添加数据到ArrayList时,会先转成Object,在取出时,还需转成其他类型。如上面的Dog。若使用泛型,在添加数据和取出数据,都不需要类型转换,提高效率。
    (4). 不在提示编译警告

13.1.2 泛型说明

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Generic03 {
    public static void main(String[] args) {
        Person<String> person = new Person<String>("hello");
//        Person<String> person = new Person<String>(1223);//报错
        /*
        可以这样理解:
        class Person<String>{
            String s;//E 表示 s的数据类型,具体是什么类型,在定义Person对象的时候就可以指定,即在编译期间,就可以确定E是什么类型

            public Person(String s) {//E也可以是参数类型
                this.s = s;
            }
            public String f(){//返回类型也可以使用E
                return s;
            }
        }
         */
        Person<Integer> person2 = new Person<Integer>(123);
        /*
       class Person<Integer>{
            Integer s;//E 表示 s的数据类型,具体是什么类型,在定义Person对象的时候就可以指定,即在编译期间,就可以确定E是什么类型

            public Person(Integer s) {//E也可以是参数类型
                this.s = s;
            }
            public Integer f(){//返回类型也可以使用E
                return s;
            }
        }
         */
    }
}
class Person<E>{
    E s;//E 表示 s的数据类型,具体是什么类型,在定义Person对象的时候就可以指定,即在编译期间,就可以确定E是什么类型

    public Person(E s) {//E也可以是参数类型
        this.s = s;
    }
    public E f(){//返回类型也可以使用E
        return s;
    }
}

13.1.3 泛型的语法

  1. 泛型的声明
    interface 接口{}和class 类<K,V>{}
    //比如:List、ArrayList
    (1). 说明其中T、K、V不代表值,而是表示类型
    (2). 任意字母都可以。常用T表示,是Type的缩写

  2. 泛型的实例化:
    要在类名后面指定类型参数的值(类型)。如:
    (1). List strList = new ArrayList();
    (2). Iterator iterator = customer.iterator();

  3. 泛型使用举例 GenericExercise.java
    (1). 创建3个学生对象
    (2). 分别放入到HashSet和HashMap中,要求Key是String name,Value就是学生对象
    (3). 使用两种方式遍历

package chapter13.generic_;

import java.util.*;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class GenericExercise01 {
    public static void main(String[] args) {
        HashSet<Student> students = new HashSet<>();//public class HashSet<E>{}
        students.add(new Student("小明", 18));
        students.add(new Student("小红", 15));
        students.add(new Student("小花", 17));

        for (Student student : students) {
            //使用泛型后,取出来的对象就Student
            System.out.println(student);
        }

        HashMap<String, Student> hashMap = new HashMap<>();//public class HashMap<K,V>{}
        hashMap.put("第一个学生", new Student("小明", 18));
        hashMap.put("第二个学生", new Student("小红", 15));
        hashMap.put("第三个学生", new Student("小花", 17));
        /*
            (1).为什么hashMap.entrySet().var直接回车就会自动填充Set<Map.Entry<String, Student>>这里面的泛型
            (2).原本是Object的
            (3).因为entrySet()是HashMap<K,V>类的方法,在实例化的时候这里的k和V就会传入具体的类型,就会替换为具体的类型
            (4).那么Set<Map.Entry<K,V>> entrySet()这个方法里面K和V自然也会替换为具体的类型
            public Set<Map.Entry<K,V>> entrySet() {
                Set<Map.Entry<K,V>> es;
                return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
            }
         */
        Set<Map.Entry<String, Student>> set = hashMap.entrySet();
        /*
        (1).为什么set.iterator().var也会自动填充Iterator<Map.Entry<String, Student>> 里面的类型,同上面的原理
            final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
                public final int size()  { return size; }
                public final void clear()  { HashMap.this.clear(); }
                public final Iterator<Map.Entry<K,V>> iterator() {
                    return new EntryIterator();
                }
            }
         */
        Iterator<Map.Entry<String, Student>> iterator = set.iterator();
        while (iterator.hasNext()) {//使用itit模版,也会自动填充为相应的类型,而不是Object啦
            Map.Entry<String, Student> next =  iterator.next();
            System.out.println(next.getKey()+":"+next.getValue());
        }
    }
}
/*
Student{name='小红', age=15}
Student{name='小明', age=18}
Student{name='小花', age=17}
第一个学生:Student{name='小明', age=18}
第三个学生:Student{name='小花', age=17}
第二个学生:Student{name='小红', age=15}
 */


class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 泛型使用的注意事项和细节 GenericDetail.java
    (1). interface List{} 和public class HashSet{}………等等
    说明:T、E只能是引用类型
    看看下面语句是否正确?
    List list = new ArrayList();//ok
    List list2 = new ArrayList();//报错

(2). 在给泛型指定具体类型后,可以传入该类型或者子类类型
(3). 泛型使用形式
List list1 = new ArrayList();
List list2 = new ArrayList<>();
如果这样写List List3 = new ArrayList();默认给它的泛型是,而E就是Object

package chapter13.generic_;

import chapter8.interface_.E;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class GenericDetail {
    public static void main(String[] args) {
        //1. 给泛型指向数据类型时,要求是引用类型,不能是基本数据类型
        List<Integer> list = new ArrayList<Integer>();//ok
//        List<int> list2 = new ArrayList<int>();//报错

        //(2). 实例化C指定泛型为A引用类型,这里可以传A类型或者A的子类B
        C<A> ac = new C<A>(new A());
        ac.f();//class chapter13.generic_.A
        C<A> ab = new C<A>(new B());
        ab.f();//class chapter13.generic_.B

        //(3). 泛型使用形式,第二种写法是简写(推荐):编译器会自动进行类型推断
        List<Integer> list1 = new ArrayList<Integer>();
        List<Integer> list2 = new ArrayList<>();
        List List3 = new ArrayList();//默认给它的泛型是<E>,而E就是Object
    }
}
class A{}
class B extends A{}
class C<E>{
    E e;

    public C(E e) {
        this.e = e;
    }
    public void f(){
        System.out.println(e.getClass());
    }
}
  1. 泛型练习
    定义Employee类
    (1). 该类包含:private成员变量name、sal、birthday,其中birthday为MyDate类的对象;
    (2). 为每一个属性定义getter和setter方法
    (3). 重写toString方法输出name、sal、birthday
    (4). MyDate类包含:private成员变量month、day、year;并为每一个属性定义getter和setter方法;
    (5). 创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
    (6). 排序方式:调用ArrayList的sort方法,传入Comparator对象【使用泛型】,先按照name【a-z】排序,如果name相同,则按生日日期的先后排序【1-9】【即:定制排序】
  • Employee类
package chapter13.generic_;

/**
 * @aim java基础学习
 * @note java笔记
 */
@SuppressWarnings({"all"})
public class Employee {
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}
  • MyDate类
package chapter13.generic_;

/**
 * @aim java基础学习
 * @note java笔记
 */
@SuppressWarnings({"all"})
public class MyDate implements Comparable<MyDate>{
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }


    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
    //为什么这里打出compareTo一回车MyDate就自动填充了,因为 Comparable<MyDate>泛型已经指定了MyDate类型,
    // 那么重写的时候直接打出compareTo自动填充
    @Override
    public int compareTo(MyDate o) {
        //如果name相同,就先比较year
        int yearMinus = year-o.getYear();
        if(yearMinus!=0){return yearMinus;}
        //如果year相同,再比较month
        int monthMinus = month-o.getMonth();
        if(monthMinus!=0){return monthMinus;}
        //如果month相同,最后比较day
        return day-o.getDay();
    }

}
  • 执行程序类GenericExercise02
package chapter13.generic_;

import java.util.ArrayList;
import java.util.Comparator;

/**
 * @aim java基础学习
 * @note java笔记
 */
@SuppressWarnings({"all"})
public class GenericExercise02 {
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<Employee>();
        employees.add(new Employee("Rose",10000,new MyDate(2003,8,9)));
        employees.add(new Employee("Rose",10000,new MyDate(2002,5,23)));
        employees.add(new Employee("Mak",70000,new MyDate(2001,10,4)));
        employees.add(new Employee("Jerry",40000,new MyDate(2002,2,28)));
        employees.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee emp1, Employee emp2) {
                //先对传入的参数进行验证
                if(!(emp1 instanceof Employee) && emp2 instanceof Employee){
                    System.out.println("类型不匹配");
                    return 0;
                }
                //比较name
                int compare=emp1.getName().compareTo(emp2.getName());
                if(compare!=0){
                    return compare;
                }
                //比较年月日
                return emp1.getBirthday().compareTo(emp2.getBirthday());
            }
        });

        for (Employee employee :employees) {
            System.out.println(employee);
        }
        //Employee{name='Jerry', sal=40000.0, birthday=MyDate{year=2002, month=2, day=28}}
        //Employee{name='Mak', sal=70000.0, birthday=MyDate{year=2001, month=10, day=4}}
        //Employee{name='Rose', sal=10000.0, birthday=MyDate{year=2002, month=5, day=23}}
        //Employee{name='Rose', sal=10000.0, birthday=MyDate{year=2003, month=8, day=9}}
    }
}

13.2 自定义泛型

13.2.1 自定义泛型类

  1. 基本语法
class 类名<T,R..>{
成员
}
  1. 注意细节
    (1). 普通成员可以使用泛型(方法、属性)
    (2). 使用泛型的数组,不能初始化
    (3). 静态方法中不能使用类的泛型
    (4). 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
    (5). 如果在创建对象时,没有指定类型,默认为Object
package chapter13.customgeneric;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class CustomGeneric_ {
    public static void main(String[] args) {
        
    }
}
//1.Tiger 后面加有泛型,所以就可以称 为自定义泛型类
//2.T,R,M 是泛型的标识符, 一般是单个大写字母
//3.泛型标识符可以有多个。
//4.普通成员可以使用泛型(方法、属性)
//5.使用泛型的数组,不能初始化
//6.静态方法中不能使用类的泛型
//7.泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
//8.如果在创建对象时,没有指定类型,默认为Object
class Tiger<T,R,M>{
    String name;
    R r;//属性使用泛型
    M m;
    T t;
    //报错,泛型的数组不能初始化。因为数组在new时不能确定T的类型,就无法在内存开辟空间,在指定类型后才可new
//    T[] ts = new T[8];
    public Tiger(String name, R r, M m, T t) {//构造器使用泛型
        this.name = name;
        this.r = r;
        this.m = m;
        this.t = t;
    }

    //因为静态是和类相关的,在加载时,对象还没有创建,所以如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
//    static R r2;//报错
//    public static void m(){}//报错

    public String getName() {
        return name;
    }

    public R getR() {//方法使用泛型
        return r;
    }

    public M getM() {
        return m;
    }

    public T getT() {
        return t;
    }
}
  1. 自定义泛型类练习
    思考下面自定义泛型代码是否正确,并说明原因。
    CustomGenericExercise.java
package chapter13.customgeneric;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class CustomGenericExercise {
    public static void main(String[] args) {
        Person<Double, String, Integer> jack = new Person<>("jack");//ok
        //通过方法去指定泛型
        jack.setT(18.0);//ok
//        jack.setT(18);//报错,
        Person mark = new Person<>("Mark");//ok
        mark.setT("hello");//ok,默认都是Object,hello是String,String是Object的子类
    }
}

class Person<T,R,M>{
    private String name;
    T t;
    R r;
    M m;

    public Person(String name) {
        this.name = name;
    }

    public Person(T t, R r, M m) {
        this.t = t;
        this.r = r;
        this.m = m;
    }

    public void setT(T t) {
        this.t = t;
    }

    public void setR(R r) {
        this.r = r;
    }

    public void setM(M m) {
        this.m = m;
    }
}

13.2.2 自定义泛型接口

  1. 基本语法
interface 接口名<T,R..>{}
  1. 注意细节
    (1). 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
    (2). 泛型接口的类型,在继承接口或者实现接口时确定
    (3). 没有指定类型,默认为Object
package chapter13.customgeneric;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class CustomInterfaceGeneric {
    public static void main(String[] args) {

    }
}

/**
 * 1.接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
 * 2.泛型接口的类型,在继承接口或者实现接口时确定
 * 3.没有指定类型,默认为Object
 */
//在继承接口时,直接确定泛型接口的类型
interface IA extends IUsb<String ,Double>{}
//当实现IA这个接口时,因为IA在继承IUsb接口时,指定了 U=String, R=Double
//所以在A实现IA接口既要实现IA的抽象方法,也要实现IUub的抽象方法。
// 在使用快捷键实现IUsb的方法时会把String自动替换为U,Double自动替换为R
class A implements IA{
    @Override
    public Double get(String s) {
        return null;
    }
    @Override
    public void hi(Double aDouble) {

    }
    @Override
    public void run(Double r1, Double r2, String u1, String u2) {

    }
}

//在实现接口时,直接指定泛型接口的类型
//即U 指定了Integer  R指定了Float
//所以,当实现IUsb的方法时,会使用Integer替换U, 使用Float替换R
class B implements IUsb<Integer,Float>{
    @Override
    public Float get(Integer integer) {
        return null;
    }
    @Override
    public void hi(Float aFloat) {

    }
    @Override
    public void run(Float r1, Float r2, Integer u1, Integer u2) {
    }
}

//没有指定类型,默认为Object,但不推荐不写,建议即使不指定也要加上<Object,Object>
class C implements IUsb{//等价于class C implements IUsb<Object,Object>{}
    @Override
    public Object get(Object o) {
        return null;
    }
    @Override
    public void hi(Object o) {  }
    @Override
    public void run(Object r1, Object r2, Object u1, Object u2) {
    }
}

interface IUsb<U,R>{
    static int i=10;
//    static U u="hello";//报错
    //普通方法中,可以使用泛型接口
    R get(U u);
    void hi(R r);
    void run(R r1,R r2,U u1,U u2);
    //在jdk8中,可以在接口中使用默认方法
    default R method(U u){
        return null;
    }
}

13.2.3 自定义泛型方法

  1. 基本语法
修饰符 <T,R..> 返回类型 方法名(参数列表){}
  1. 注意细节
    (1). 泛型方法,可以定义在普通类中,也可以定义在泛型类中
    (2). 当泛型方法被调用时,类型会确定
    (3). Public void eat(E e){}不是泛型方法,而是使用了泛型。因为修饰符后没有<T,R…> eat方法不是泛型方法。
package chapter13.customgeneric;

import java.util.ArrayList;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class CustomMethodGeneric {
    public static void main(String[] args) {
        Car car = new Car();
        car.fly("horse",2000);//当调用方法时,插入参数,编译器就会确定类型(如果是基本数据类型会自动装箱)
        //class java.lang.String
        //class java.lang.Integer
        Fish<String, ArrayList> fish = new Fish<>();
        fish.hello(new ArrayList(),100.3f);
        //class java.util.ArrayList
        //class java.lang.Float
    }
}
class Car{//普通类
    public void run(){//普通方法

    }
    //1.<T,R>就是泛型
    //2.是提供给福利院使用的
    public <T,R> void fly(T t,R r){//泛型方法.当泛型方法被调用时,类型会确定
        System.out.println(t.getClass());
        System.out.println(r.getClass());
    }
}
class Fish<T,R>{//泛型类
    public void run(){//普通方法

    }
    public <U,M>void eat(U u,M m){//泛型方法
    }
    //下面的hi方法不是泛型方法,只是使用了类声明的泛型而已
    public void hi(T t){}
    //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
    public <K> void hello(R r,K k){
        System.out.println(r.getClass());
        System.out.println(k.getClass());
    }
}
  1. 自定义泛型方法练习
    判断下面代码是否正确,如果有错误,修改正确,并说明输出什么?
    package chapter13.customgeneric;
/**
 * @aim java基础学习
 * @note java笔记
 */
public class CustomMethodGenericExercise {
    public static void main(String[] args) {
        Apple<String,Integer,Double> apple = new Apple<>();
        apple.fly(10);//Integer
        apple.fly(new Dog());//Dog
    }
}
class Apple<T,R,M>{
    public <E> void fly(E e){
        System.out.println(e.getClass().getSimpleName());
    }
//    public void eat(U u){}//报错,因为泛型U没有声明
    public void run(M m){}
}
class Dog{ }

13.3 泛型继承和通配符

  1. 泛型的基础和通配符说明
    (1). 泛型不具备继承性
    List list = new ArrayList(); //对吗
    (2). <?>:表示支持任意泛型类型
    (3). <? extends A>:支持A类以及A类的子类,规定了泛型的上限。
    (4). <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
4.	<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
package chapter13.extendsgeneric_;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class ExtendsGeneric_ {
    public static void main(String[] args) {
        Object o = new String("hello");
//        List<Object> list = new ArrayList<String>(); //对吗,不对泛型没有继承关系

        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<A> list3 = new ArrayList<>();
        List<B> list4 = new ArrayList<>();
        List<C> list5 = new ArrayList<>();

        //(2). <?>:表示支持任意泛型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);

        //(3). <? extends A>:支持A类以及A类的子类,规定了泛型的上限。
//        printCollection2(list1);//报错
//        printCollection2(list2);//报错
        printCollection2(list3);
        printCollection2(list4);
        printCollection2(list5);

        //(4). <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
        printCollection3(list1);
//        printCollection3(list2);//报错
//        printCollection3(list3);//报错
//        printCollection3(list4);//报错
//        printCollection3(list5);//报错

    }

    //(2). <?>:表示支持任意泛型类型
    public static void printCollection1(List<?> c) {
        for (Object o : c) {
            System.out.println(o);
        }
    }

    //(3). <? extends A>:支持A类以及A类的子类,规定了泛型的上限。
    public static void printCollection2(List<? extends A> c) {
        for (Object o : c) {
            System.out.println(o);
        }
    }

    //(4). <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
    public static void printCollection3(List<? super A> c) {
        for (Object o : c) {
            System.out.println(c);
        }
    }

}

class A {
}

class B extends A {
}

class C extends B {
}

13.4 JUnit

  1. 为什么需要Junit
    (1). 一个类中有很多功能代码需要测试,为了测试,就需要写入到main方法中
    (2). 如果有多个功能代码测试,就需要来回注销,切换很麻烦
    (3). 如果可以直接运行一个方法,就方便很多,并且可以给出相关信息,就好了
  2. 基本介绍
    (1). Junit是一个java语言的单元测试框架
    (2). 多数java的开发环境都已经集成了Junit作为单元测试的工具
package chapter13;

import org.junit.jupiter.api.Test;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class JUnit_ {
    public static void main(String[] args) {
        //传统方法的测试调用
        //先测试m1
//        new JUnit_().m1();

        //为了测试m2,有时候会把m1的调用注销或加上注释不影响其他功能的测试
//        new JUnit_().m2();
        //所以就有JUnit测试框架
    }

    //写两个方法测试输出
    @Test
    public void m1(){
        System.out.println("m1 方法被调用");
    }
    public void m2(){
        System.out.println("m2 方法被调用");
    }
}
  1. 如何添加Junit
    在需要运行功能的地方打上@Test,然后将光标放到Test上快捷键alt+enter。然后下载即可。具体图片如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4. 添加完Junit的好处
添加完Junit,在某个功能的上面添加@Test,就可以单独运行某个功能,既不用写到main方法中,也不影响其他的运行。
在这里插入图片描述
在这里插入图片描述

13.5 本章作业

  1. 编程题
    (1). 定义一个泛型类DAO,在其中定义一个Map成员变量,Map的键为String类型,值为T类型。分别创建以下方法:
    ① public void save(String id,T entity):保存T类型的对象到Map成员变量中
    ② public T get(String id):从map中获取id对应的对象
    ③ public void update(String id, T entity):替换map中的key为id的内容,改为entity对象
    ④ public List list():返回map中存放的所有T对象
    ⑤ public void delete(String id):删除指定id对象
    (2). 定义一个User类:该类包含:private成员变量(int类型) id , age;(String)name
    (3). 创建DAO类的对象,分别调用其save、get、update、list、delete方法来操作User对象(也就是T=User),使用Junit单元测试框架进行测试
  • User类
package chapter13.task_;

/**
 * @aim java基础学习
 * @note java笔记
 */
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;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
  • DAO类
DAOpackage chapter13.task_;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class DAO <T>{
    private Map<String,T> map=new HashMap<>();

    //保存T类型的对象到Map成员变量中
    public void save(String id,T entity){
        map.put(id,entity);
    }

    //从map中获取id对应的对象
    public T get(String id){
        return map.get(id);
    }

    //替换map中的key为id的内容,改为entity对象
    public void update(String id,T entity){
        map.put(id,entity);
    }

    //返回map中存放的所有T对象
    public List<T> list(){
        List<T> list = new ArrayList<>();
        for (String key : map.keySet()) {
            list.add(get(key));
        }
        return list;

    }

    //删除指定id对象
    public void delete(String id){
        map.remove(id);
    }
}
  • Task01测试类
package chapter13.task_;

import org.junit.jupiter.api.Test;

import java.util.List;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Task01 {
    public static void main(String[] args) {

    }
    @Test
    public void testing(){
        DAO<User> dao = new DAO<>();
        dao.save("001",new User(1,19,"mark"));
        dao.save("002",new User(2,15,"rose"));
        dao.save("003",new User(3,12,"lucy"));
        System.out.println(dao.get("001"));//User{id=1, age=19, name='mark'}
        dao.update("003",new User(3,10,"jack"));
        dao.delete("001");
        List<User> list = dao.list();
        System.out.println(list);
        //[User{id=2, age=15, name='rose'}, User{id=3, age=10, name='jack'}]
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值