十五、泛型(完结)

十五、泛型


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<Dog> 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 表示泛型标识,可以接收任意类型,E也可以用其他字母表示
        //2. new Person<String>(); 表示将String类型传递给E , 指定E的类型为String
        //3. E 的具体类型在创建对象的时候就指定,即在编译阶段就会确定 E 的类型
        Person<String> person = new Person<String>();
        person.name = "赵四";
        //person.name = 33; //传入不同的类型,编译报错
        person.show();
    }
}

//泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型
class Person<E> { //在类声明时通过标识符 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 接口<T> {}class 类<K,V> {}
    • TKV 不代表具体值,只表示类型
    • 任意字母、任意数量。常用 TType 缩写
  • 实例化语法:在类名后面指定类型参数的值
    • 例:HashSet<Student> students = new HashSet<Student>();

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<Student> students = new HashSet<Student>();
        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<Student> iterator = students.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next);
        }
        System.out.println();

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

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

        final class EntrySet extends AbstractSet<Map.Entry<K,V>> {.....} 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<Student> values = studentHashMap.values();
        /*
        public Collection<V> values() {
            Collection<V> vs = values;
            if (vs == null) {
                vs = new Values();
                values = vs;
            }
            return vs;
        }
        */

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

        final class ValueIterator extends HashIterator
            implements Iterator<V> {
            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<String, Student> studentHashMap = new HashMap<String, Student>();
    • Map<String, Student> studentHashMap = new HashMap<String, Student>();
    • HashMap<String, Student> studentHashMap = new HashMap<>(); 推荐
  • 不指定类型,默认类型是 Object
    • ArrayList arrayList = new ArrayList(); 等同于 ArrayList<Object> objects = new ArrayList<>();
  • 注意:泛型只在编译时期限定数据的类型 , 在运行时期会被擦除。后面学习反射的时候,可以实现在代码运行的过程中添加其他类型的数据到集合 见 20.7.4

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<Employee>
        //5.2 arrayList.sort(new Comparator<Employee>(){
        //      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<Employee> 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<Employee>() {
            @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> {
    //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<T, R, M> {}

要点:

  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<String, Integer, Double> tiger = new Tiger<>();
    }
}

//1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
//2, T, R, M 泛型的标识符, 一般是单个大写字母
//3. 泛型标识符可以有多个.
class Tiger<T, R, M> {
    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<T, R> {}


要点:

  • 接口中,静态成员也不能使用泛型
  • 泛型接口的类型, 在继承接口或者实现接口时确定
  • 没有指定类型,默认为 Object
package com.hspedu.customgeneric;

/**
 * @author: Carl Zhang
 * @create: 2021-12-13 14:10
 */
public class CustomGenericInterface {

}

//1. 接口中,静态成员也不能使用泛型
interface IA<T, R> {
    String D = "无名氏";

    //T T1 = "test"; //接口的属性默认是public static final

    void getT(T t); //接口中普通方法可以使用泛型
    void getR(R r);

}

//2. 泛型接口的类型, 在继承接口或者实现接口时确定
class AA implements IA<String, Double> { //表示类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  自定义泛型方法

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

要点:

  • 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  • 当泛型方法被调用时传入参数,类型会确定
  • public void eat(T t, E e),修饰符后没有 <T, 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<String, Double> 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 <T, E> void methodA(T t, E e) { //定义在普通类的泛型方法
        System.out.println("t的类型" + t.getClass() + ", e的类型" + e.getClass());
    }

    public <T, E> String methodB(T t, E e) { //定义在普通类的泛型方法
        return "t的类型" + t.getClass() + ", e的类型" + e.getClass();
    }
}

class BBB<T, E> {
    public <Q, W> void methodA(Q q, W w) { //定义在泛型类的泛型方法
        System.out.println("q的类型" + q.getClass() + ", w的类型" + w.getClass());
    }

    //可以使用自己声明的泛型,也可以使用类声明的泛型
    public <Q, W> String methodB(T t, E e) { //定义在泛型类的泛型方法
        return "t的类型" + t.getClass() + ", e的类型" + e.getClass();
    }

    //3.public void eat(Ee)0,修饰符后没有<T,R.>eat方法不是泛型方法,而是使用了泛型
    public void eat(T t, E e) { //修饰符后没有<T, R> 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<Object> strings = new ArrayList<String>(); //报错

        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();

        //能传入任意的泛型类型List
        methodA(list1);
        methodA(list2);
        methodA(list3);
        methodA(list4);
        methodA(list5);

        //methodB(list1); //报错 需要 List<? extends AA>, 提供了List<Object>
        //表示集合的泛型类型为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  Junit4单元测试概述

  • 单元测试就是编写测试代码,可以准确、快速地保证程序的正确性JunitJava单元测试的框架。
  • JUnit4 可以通过注解的方式来标记方法 , 让方法存某种意义 ,常见的注解有:
    • @BeforeClass  全局只会执行一次,而且是第一个运行(标记的方法需要是一个静态无参无返回值方法)
    • @Before  在测试方法运行之前运行(非静态无参无返回值方法)
    • **@Test****  测试方法(此方法必须是非静态无参无返回值方法), 主要用于测试的方法 **
    • @After  在测试方法运行之后运行(非静态无参无返回值方法)
    • @AfterClass 全局只会执行一次,而且是最后一个运行(标记的方法需要是一个静态无参无返回值方法)
    • @Ignore  忽略此方法

15.7.2 Junit的基本使用

  • 已知存在需要测试的类 Calculator,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试。
public class Calculator {
    // 静态变量,用于存储运行结果
    private static int result; // 0
  
    // 加法运算
    public void add(int n) {
        result = result + n;
    }
    
    // 减法运算
    public void substract(int n) {
        // Bug: 正确的应该是 result = result - n
        result = result - 1;  
    }
  
    // 乘法运算
    public void multiply(int n) {
        // 此方法尚未写好
    }         
  
    // 除法运算
    public void divide(int n) {
        result = result / n;
    }
    
    // 平方运算
    public void square(int n) {
        result = n * n;
    }
    
    // 平方根运算
    public void squareRoot(int n) {
        // Bug : 死循环
        for (; ;) ;            
    }
    
    // 将结果清零
    public void clear() {     
        result = 0;
    }
    
    // 返回运算结果
    public int getResult(){
        return result;
    }
}
引入 Junit4jar 包到模块中
  • 第一步 :  在模块中新建文件夹lib,拷贝今天资料中 junit4hamcrest-core-1.3jar 包到模块中
  • 第二步 : 选中 jar 文件 , 右键选择 Add as Library
  • image.png
生成 Junit 测试框架
  • 使用 IDEA 能够直接给需要测试的类生成测试框架,如下:
    • 选中本类任何位置右键选择  Generate(Alt+Insert)/ go to 选项  ,  选择 Test...
      • image.png
    • 选择需要进行测试的方法
      • image.png
    • 在上一步 OK 后系统会自动生成一个新类 CalculatorTest ,里面包含一些空的测试用例。
      你只需要将这些测试用例稍作修改即可使用,完整的 CalculatorTest 代码如下:
package com.heima.junit;

import org.junit.*;

/**
 * @author carl
 */
public class CalculatorTest {
    static Calculator calculator = new Calculator();

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
        //每次测试完重置结果
        System.out.println("测试完成,重置结果");
        calculator.clear();
    }

    @Test
    public void add() {
        calculator.add(50);
        calculator.add(50);
        calculator.add(50);
        int result = calculator.getResult();

        //断言
        //Assert.assertEquals(150, result); //通过
    }

    @Test
    public void subtract() {
        calculator.add(100);
        calculator.subtract(50);
        calculator.subtract(50);
        calculator.subtract(50);
        int result = calculator.getResult();

        Assert.assertEquals(-50, result); //Expected :-50  Actual   :97
    }

    @Ignore //不执行
    @Test
    public void multiply() {
    }

    @Test
    public void divide() {
        calculator.divide(0); //java.lang.ArithmeticException: / by zero
        int result = calculator.getResult();

        Assert.assertEquals(-1, result);
    }

    @Test
    public void square() {
        calculator.square(2);
        int result = calculator.getResult();

        Assert.assertEquals(4, result);
    }

    @Test(timeout = 3000)
    public void squareRoot() {
        calculator.squareRoot(9); //超时
        int result = calculator.getResult();

        Assert.assertEquals(3, result);
    }

    @Test
    public void clear() {
        add();
        calculator.clear();
        int result = calculator.getResult();

        Assert.assertEquals(0, result); //通过

    }

    @Test
    public void getResult() {

    }
}

限时测试:
对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。限时测试是一个很好的解决方案。我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些 Bug 了。要实现这一功能,只需要给 @Test 标注加一个参数即可,代码如下:

  • 被测方法:

public void squareRoot(int n) {
    //Bug : 死循环
    for (; ; ) ;            
}
  • 测试方法:
@Test(timeout = 1000)
// Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。
public void squareRoot() {
    calculator.squareRoot(4);
    assertEquals(2 , calculator.getResult());
}

15.7.3  断言

概述 : 预先判断某个条件一定成立,如果条件不成立,则直接报错。
使用 :
  //第一个参数表示期望值
  //第二个参数表示实际值
  //如果实际值和期望值相同,说明结果正确就测试通过,如果不相同,说明结果是错误的,就会报错
  Assert.assertEquals( 期望值, 实际值);
  Assert.assertEquals("异常原因", 期望值, 实际值);

  //例如:
  int result = add(100,200);
  Assert.assertEquals(300, result);
小结 : 如何进行断言

Assert.assertEquals(期望值,实际值)

15.7.4 使用案例

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被调用");
    }
}
posted @   Carl-Zhang  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示