第十三章、 泛型

news2024/9/22 13:40:53

第十三章、 泛型

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'}]
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IPv6和IPv4的主要区别

地址变化与转换&#xff1a; IPv6 二进制数有 128 位&#xff0c;IPv6 使用十六进制表示&#xff0c;中间使用 : 分隔。 简写&#xff1a; 将前面连续的 0 &#xff0c;使用 :: 表示。缩写只能使用一次&#xff0c;而且缩头不缩尾。 首部&#xff1a; 地址划分&#xff1a; IPv…

WEB:探索富文本编辑器的详细指南

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 富文本编辑器&#xff08;Rich Text Editor, RTE&#xff09;是一种允许用户在不需要编写HTML或CSS代码的情况下创建和编辑复杂文本内容的工具。无论是博客平台、内容管理系统&…

pnpm 查看库的所有版本

1、最近在做图布局的时候&#xff0c;发现默认版本是beta版 2、那么我们如何来查看远程库中有什么版本可以安装呢&#xff1f; 3、使用命令&#xff1a; pnpm view antv/layout versions pnpm view 这里替换成要查看的库名 versions

Flutter-->自定义容器Widget(类比Android自定义ViewGroup)

上一篇Flutter–&#xff1e;自定义Widget(类比Android自定义View) 介绍了如何自定义一个Widget, 这一篇文章介绍如果自定义容器Widget, 相当于Android中的ViewGroup Android自定义ViewGroup 先来简单介绍一下Android中自定义的ViewGroup: class CustomViewGroup(context: …

树上dp+分组背包类问题

今天也是无意间看到了一个树上dp分组背包类的问题&#xff0c;有些难度的&#xff0c;不好想的嘞&#xff0c;终究还是一个蒟蒻罢了&#xff0c;唔无捂误 话不多说直接看题 P1273 有线电视网 在说明这道题之前&#xff0c;还有一个要提醒的地方就是&#xff0c;树上dp&#…

在 DataOps 体系建设中,主动元数据是何角色?

首先&#xff0c;主动元数据是相对静态元数据而言&#xff0c;它是一种动态、智能化的元数据管理技术&#xff0c;能够将传统静态元数据的被动等待变为实时在线、主动触发&#xff0c;推动数据探查、开发、测试、部署、运维和监控等数据治理工作高效运转&#xff0c;为数据的治…

springWeb介绍、以及SpringWeb的搭建

ssm框架 早期 ssm springstrtuts2mybatis 现在 ssm springspringwebmybatis springweb运行流程 1、SpringWeb概述 SpringWeb是spring框架中的一个模块&#xff0c;基于Servlet API构建的web框架&#xff0c;springweb是Spring为web层开发提供的一套完备的解决方案。在we…

Java毕业设计 基于SSM校园心理咨询服务平台

Java毕业设计 基于SSM校园心理咨询服务平台 SSM 校园心理咨询服务平台 功能介绍 学生: 注册 登录 首页 心理测试 心理文章 心理导师 在线交流 关于我们 搜索 学生中心 我的咨询问题 我的测试结果 我的预约 我的发言 我的收藏 账户信息 教师&#xff1a;登录 发布文…

Linux——nginx 负载均衡

常规的web服务器一般提供对于静态资源的访问&#xff0c;比如说&#xff1a;图片、web样式 网站提供的大部分交互功能都需要web编程语言的支持&#xff0c;而web服务对于程序的调用&#xff0c;不管编译型语言还是解释型语言&#xff0c;web服务同将对于应用程序的调用递交给通…

音频变声怎么弄?(实测好用)快来试试这6个AI变声工具

音频变声怎么弄&#xff1f;随着短视频平台和社交平台的发展&#xff0c;很多小伙伴们会自己拍摄视频分享到平台上&#xff0c;还有一些视频创作者会制作有趣的视频吸引观众。而视频配音就是锦上添花&#xff0c;很多人觉得自己的声音不好听&#xff0c;想要实现录音音频变声&a…

结合ollama gemma2:2b大模型来实现数据分析系统的智能交互

在最近的人员风险行为分析系统开发过程中&#xff0c;需要解决一个问题&#xff1a;在缺乏GPU资源的情况下&#xff0c;如何提升智能交互能力。​我们探索并研究了集成gemma2:2b模型的可行性&#xff0c;这一举措旨在在有限的硬件条件下&#xff0c;为我们的系统注入更高级别的…

如何理解进程

一、进程的概念 进程&#xff1a;顾名思义&#xff0c;就是一个完整执行程序的过程。没错&#xff0c;就是这么简单&#xff0c;但是在程序执行的过程之中&#xff0c;系统会为这个执行的程序分配内存资源&#xff0c;这些过程也包含在进程当中。 进程是动态的&#xff0c;是程…

css-50 Projects in 50 Days(2)

html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>步骤条</title><link rel"style…

基于混沌麻雀搜索算法的光伏MPPT控制MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 此模型主要研究光伏系统MPPT控制&#xff0c;通过将麻雀搜索算法引入至MPPT控制策略中&#xff0c;在模型中通过改变光照强度&#xff0c;来验证算法引入的有效性。模型中包含麻雀搜索算法MPPT与混…

单链表——随机链表的复制

深拷贝&#xff0c;就是将原链表彻底的拷贝&#xff0c;当我们观察这个链表时我们会发现&#xff0c;val与next都比较好拷贝&#xff0c;难点就是在random的拷贝&#xff0c;因为我们需要知被拷贝的节点的random指向的是哪个&#xff0c;所以我们很容易想到的方法就是从头遍历链…

从开题到答辩:ChatGPT超全提示词分享!(上)【建议收藏】

在浩瀚的知识领域中&#xff0c;提问的艺术是探索真理的钥匙。在这个信息爆炸的时代&#xff0c;深入探索知识的海洋&#xff0c;不仅需要热情和毅力&#xff0c;更需要正确的方法和工具。学术研究是一个复杂而严谨的过程&#xff0c;涉及从选题、文献综述到研究设计、数据收集…

网络层,数据链路层和应用层

1.网络层 网络层最主要的协议就是IP协议。 下图是IP协议的格式&#xff1a; 1.1 IP协议解析 &#xff08;1&#xff09;4位版本&#xff1a; 有两种&#xff1a;IPV4&#xff08;IP地址4个字节大小&#xff09;和IPV6&#xff08;IP地址16个字节大小&#xff09; &#xf…

推荐系统实战(八)-冷启动(上)

一、冷启动基本描述 &#xff08;一&#xff09;冷启动与新用户新物料 冷启动针对的是对缺少消费记录的新用户、新物料的推荐。 新用户不仅包含初次使用应用的用户&#xff0c;还包含安装很久但是处于低活跃状态的用户。 &#xff08;二&#xff09;部分经典算法无法支持新…

2024年【安全员-C证】新版试题及安全员-C证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【安全员-C证】新版试题及安全员-C证复审模拟考试&#xff0c;包含安全员-C证新版试题答案和解析及安全员-C证复审模拟考试练习。安全生产模拟考试一点通结合国家安全员-C证考试最新大纲及安全员-C证考试真题汇…

二叉搜索树进阶之红黑树

前言&#xff1a; 在上文我们已经学习了AVL树的相关知识以及涉及的四种旋转的内容&#xff0c;但是AVL树追求平衡导致旋转操作过多&#xff0c;一些情况下影响性能&#xff0c;由此我们就来了解一下二叉搜索树的另外一个分支&#xff0c;红黑树。 &#xff08;倘若对旋转知识…