1.单元测试

1.1、单元测试概述

  • 单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性
  • 目前测试是怎么样的,存在的问题
    • 只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响
    • 无法得到测试的结果报告,需要程序员自己去管擦和测试是否成功
    • 无法实现自动化测试
  • Junit单元测试框架
    • Junit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用Junit编写单元测试。
    • 此外,几乎所有的IDEA工具都继承了Junit,可以直接使用Junit测试
  • Junit优点
    • Junit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法
    • Junit可以生成全部方法的测试报告
    • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试

1.2、单元测试快速入门

  • 使用步骤

    1. 将Junit的jar包导入项目(IDEA版本靠前的,一般不需要导)
    2. 编写测试方法,该测试方法必须是公共的无参数无返回值的非静态犯法
    3. 在测试方法上使用@Test注解,标注该方法是一个测试方法
    4. 在测试方法中完成被测试的方法的预期正确性测试
  • UserService

public class UserService {
    public String login(String userName,String pwd){
        if(userName.equals("mahaonan") && pwd.equals("010220")){
            return "登录成功";
        }else {
            return "账号或密码有错误";
        }
    }
    public void selectName(){
        System.out.println(1/0);
        System.out.println("查询用户名字");
    }
}
  • TestUserService
public class TestUserService {

    /*
        必须是,无参数,无返回值,非静态方法
     */
    @Test
    public void testLogin(){
        UserService service = new UserService();
        String mes = service.login("mahaonan", "010220");

        //进行预期结果的正确性测试:断言
        Assert.assertEquals("您的业务功能可能有问题","登录成功",mes);
    }

    @Test
    public void testSelectNames(){
        UserService service = new UserService();
        service.selectName();
    }
}

1.3、单元测试常用注解

  • Junit常见注解(Junit 4.xxxx版本)

image-20230810163822253.png

  • 开始执行的方法:初始化资源
  • 执行完之后的方法:释放资源

  • Junit常见注解(Junit 5.xxx版本)

image-20230810163828524.png

public class Test {
    @Before
    public void before(){
        System.out.println("在方法执行前,执行before-------");
    }
    @After
    public void after(){
        System.out.println("在方法执行后,执行after-------");
    }
    @BeforeClass //必须修饰为static静态方法
    public static void beforeClass(){
        System.out.println("在所有方法执行前,执行该方法beforeClass,只执行一次");
    }
    @AfterClass //必须修饰为static静态方法
    public static void afterClass(){
        System.out.println("在所有方法执行后,执行该方法afterClass,只执行一次");
    }
}

2.反射

2.1、反射概述

  • 概述
    • 反射是指对于任何一个Class类,在”运行的时候“都可以直接得到这个类的全部成分
    • 在运行时,可以直接得到这个类的构造器:constructuor
    • 在运行时,可以直接得到这个类的成员变量对象:Field
    • 在运行,可以得到这个类的成员方法对象:Method
    • 这种运行时动态的获取类信息以及动态调用类中成分的能力称为Java语言反射机制
  • 反射的杆件
    • 反射的第一步都是先得到编译后的Class类对象,然后可以得到Class的全部成分

2.2、反射获取类对象

  • Class.forName(全限名);
  • 类名.class
  • 对象.getClass();
public class Test {
    public static void main(String[] args) throws Exception{
        //1.Class类中的一个静态方法来获取Class:forName(全限名)
        Class c = Class.forName("junit_reflect_.reflect.Student");
        System.out.println(c);
        //2.类名.class 来获取
        Class c1 = Student.class;
        System.out.println(c1);
        //3.通过对象.getClass()来获取
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2);
    }
}

2.3、反射获取构造器对象

【反射的第一步是先获取类对象,然后从类对象中获取类的成分对象】

  • Class类中用于获取构造器的方法

image-20230810163839041.png

【获取构造器的作用依然是初始化一个对象返回】

  • Constructor类中用于创建对象的方法

image-20230810163844388.png

  • Student
public class Student {
    private String name;
    private Integer age;
    public Student() {
        System.out.println("无参构造器");
    }

    public Student(String name, Integer age) {
        System.out.println("有参构造器");
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

}
  • Test
public class TestOne {
    //1.getContructors:获取全部public修饰的构造器(只能获取public修饰的)
    @Test
    public void getConstructors(){
        Class c = Student.class;
        //获取所有public的构造器
        Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName()+"---->"+constructor.getParameterCount());
        }
    }
    //2.getDeclareConstructors:获取全部的构造器(无论什么修饰)
    @Test
    public void getDeclareConstructors(){
        Class c = Student.class;
        //获取所有的构造器
        Constructor[] constructors = c.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName()+"---->"+constructor.getParameterCount());
        }
    }
    //3.获取某个构造器,只能获取public修饰的
    @Test
    public void getConstructor() throws Exception {
        Class c = Student.class;
        //获取单个public修饰构造器:无参
        Constructor constructor = c.getConstructor();
        System.out.println(constructor.getName()+"---->"+constructor.getParameterCount());
    }
    //4.获取某个构造器,无论什么修饰的
    @Test
    public void getDeclareConstructor() throws Exception {
        Class c = Student.class;
        //获取单个无所谓修饰构造器:无参
        Constructor constructor = c.getDeclaredConstructor();
        System.out.println(constructor.getName()+"---->"+constructor.getParameterCount());
        //获取单个构造器:有参
        Constructor constructorPara = c.getDeclaredConstructor(String.class,Integer.class);
        System.out.println(constructorPara.getName()+"---->"+constructorPara.getParameterCount());
    }
}

【获取并创建实例对象】

public class TestTwo {
    public static void main(String[] args) throws Exception {
        //获取无参构造器并创建实例
        Class c = Student.class;

        Constructor constructor = c.getConstructor();
        //遇到私有构造器,可强行暴力反射
        constructor.setAccessible(true);//权限被打开
        Student s = (Student) constructor.newInstance();
        System.out.println(s);

        //获取某个有参构造器并创建实例
        Constructor constructor1 = c.getConstructor(String.class, Integer.class);
        Student s1 = (Student) constructor1.newInstance("马浩楠", 22);
        System.out.println(s1);
    }
}

2.4、反射获取成员变量对象

  • Class类中获取成员变量的方法

image-20230810163856293.png

  • Field类中用于取值、赋值的方法

image-20230810163901717.png

  • Student
public class Student {
    private String name;
    private Integer age;
    public static String schoolName;
    public static final String COUNTRY = "中国";

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Student(String name, Integer age) {
        System.out.println("有参构造器");
        this.name = name;
        this.age = age;
    }

    public Student() {
        System.out.println("无参构造器");
    }
}

2.5、反射获取方法对象

  • Class类中用于获取成员方法的方法

image-20230810163910128.png

  • Method类中用于触发执行的方法

image-20230810163916020.png

  • Dog
public class Dog {
    private String name;

    public Dog() {
    }

    public void run(){
        System.out.println("狗正在跑!~");
    }
    public void eat(){
        System.out.println("狗正在吃骨头!");
    }
    public String eat(String name){
        System.out.println("狗正在吃:"+name);
        return "吃的很开心";
    }
    public static void inAddress(){
        System.out.println("正在宿舍苦逼学习!");
    }


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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • Test
public class Test {
    //获取类中的所有成员方法对象
    @org.junit.Test
    public void allMethods(){
        Class c = Dog.class;

        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName()+"---方法返回值类型:"+method.getReturnType()+"---->参数个数:"+method.getParameterCount());
        }
    }
    //获取某个方法
    @org.junit.Test
    public void singleMethod() throws Exception{
        Class c = Dog.class;

        Method eat = c.getDeclaredMethod("eat");
        Method eat1 = c.getDeclaredMethod("eat",String.class);

        Dog dog = new Dog();
        //暴力反射
        eat.setAccessible(true);
        eat1.setAccessible(true);

        //没有返回值,返回的结果则为null
        Object result = eat.invoke(dog);
        System.out.println(result);

        Object result1 = eat1.invoke(dog, "骨头");
        System.out.println(result1);
    }
}

2.6、反射的作用-绕过编译阶段为集合添加数据

  • 反射是作用在运行时的技术,此时集合的泛型将不能产生约束,此时是可以为集合存入其他任意类型的元素的
  • 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译为Class文件进入运行阶段的时候,其实真是类型都是ArrayList了,泛型相当于被擦除了
public class Test {
    public static void main(String[] args) throws Exception{
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();
        System.out.println(list1.getClass() == list2.getClass());
        System.out.println("----------------------");


        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(10);
        list3.add(40);
        //通过反射,来动态获取该数组,并添加字符串
        Class c = list3.getClass();
        Method add = c.getDeclaredMethod("add", Object.class);
        add.invoke(list3,"马浩楠");
        System.out.println(list3);
    }
}

2.7、反射的作用-通用框架的底层原理

  • 需求:参数为任意一个对象,在不清楚对象字段的情况下,可以将对象的字段名和对应的值存储到文件中

  • Student

public class Student {
    private String name;
    private char sex;
    private int age;
    private String className;
    private String hobby;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", className='" + className + '\'' +
                ", hobby='" + hobby + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public Student() {
    }

    public Student(String name, char sex, int age, String className, String hobby) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.className = className;
        this.hobby = hobby;
    }
}
  • Theacher
public class Teacher {
    private String name;
    private char sex;
    private double salary;

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", salary=" + salary +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Teacher(String name, char sex, double salary) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
    }

    public Teacher() {
    }
}
  • MyBatisUtils
public class MyBatisUtils {

    public static void save(Object obj){
        try(
                PrintStream ps = new PrintStream(new FileOutputStream("E:\\code\\Java_Demos\\Java\\reflect_data.txt",true))
                ){
            //1.提取对象的全部成员变量
            Class c = obj.getClass();
            //c.getSimpleName()获取当前类名,getName()是全限名(包名+类名)
            ps.println("============"+c.getSimpleName()+"============");
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                //获取该变量的名称
                String name = field.getName();
                field.setAccessible(true);
                //获取该名称变量的值
                String value = field.get(obj) + "";
                ps.println(name+"="+value);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        Student s = new Student("马浩楠",'男',22,"理工","打球");
        MyBatisUtils.save(s);
        Teacher t = new Teacher("程明辉",'女',3000);
        MyBatisUtils.save(t);
    }
}

3.注解

3.1、注解概述

  • 概述

    • Java 注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制
    • Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注
  • 作用

    • 对Java中类、方法、成员变量做编辑,然后进行特殊处理,至于到底做什么处理,根据业务需求
    • 例如:Junit框架中,标注了@Test的方法就可以被当称测试方法来执行

3.2、自定义注解

  • 自己做一个注解
public @interface 注解名称{
    数据类型 数据名() [default 默认值];
}
  • 特殊属性
    • value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不屑
    • 但是如果存在多个属性,且属性没有默认值,那么value名称不能被省略
  • MyBook
public @interface MyBook {
    String name();
    String[] authors();
    double price();
}
  • Book
public @interface Book {
    String value(); //特殊属性
}
  • Test2
@MyBook(name="Java",authors = {"马浩楠"},price = 200.5)
@Book("MyBatis")
public class AnnotationDemo01 {
    @MyBook(name="Java",authors = {"马浩楠"},price = 200.5)
    public static void main(String[] args) {
        @MyBook(name="Java",authors = {"马浩楠"},price = 200.5)
        int age = 22;
    }
}

3.3、元注解

  • @Target:约束自定义注解只能在那些地方可以用
    • TYPE——类,接口
    • FIELD——成员变量
    • METHOD——成员方法
    • PARAMETER——方法参数
    • CONSTRUCTOR——构造器
    • LOCAL_VARIABLE——局部变量
  • @Retention:申明注解的生命周期
    • SOURCE——注解只能作用在源码阶段,生成的字节码文件不存在
    • CLASS——注解作用在源码劫夺,字节码文件阶段,运行阶段不存在,默认值
    • RUNTIME——注解作用在源码阶段,字节码文件,运行阶段(开发常用)
@Target({ElementType.METHOD,ElementType.FIELD}) //元注解
@Retention(RetentionPolicy.RUNTIME) //生命周期:一直存在
public @interface MyTest {
}

3.4、注解解析

  • 概述
    • 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析内容
  • 于注解解析相关的接口
    • Annotation:注解的顶级接口,注解都是Annotation类型的对象
    • AnnotationElement:该接口定义了于注解解析相关的解析方法

image-20230810163957048.png

【所有的类成分Class、Method、Field、Constructor都实现了AnnotationElement接口他们都拥有解析注解的能力】

  • 案例

    • 定义注解Book
      • 包含属性:String value() 书名
      • 包含属性:double price() 价格 默认值为100
      • 包含属性:String[] authors() 作者
      • 限制注解使用的位置:类和成员方法上
      • 指定注解的有效范围:RUNTIME
    • 定义BookStore类,在类和成员方法上使用Book注解
    • 获取注解上面的数据
  • AnalysisBook

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnalysisBook {
    String value(); //书名
    double price() default 100;
    String[] authors();

}
  • Test BookStore
public class AnnotationDemo03 {
    @Test
    public void parseClass(){
        //获取类对象
        Class c = BookStore.class;
        //判断类上是否存在指定注解
        if(c.isAnnotationPresent(AnalysisBook.class)){
            //直接获取该注解对象
            AnalysisBook analysisBook = (AnalysisBook) c.getDeclaredAnnotation(AnalysisBook.class);
            System.out.println(analysisBook.value());
            System.out.println(analysisBook.price());
            System.out.println(Arrays.toString(analysisBook.authors()));
        }
    }
    @Test
    public void parseMethod() throws Exception {
        //获取类对象
        Class c = BookStore.class;
        //获取方法对象
        Method method = c.getDeclaredMethod("test");
        //判断类上是否存在指定注解
        if(method.isAnnotationPresent(AnalysisBook.class)){
            //直接获取该注解对象
            AnalysisBook analysisBook = method.getDeclaredAnnotation(AnalysisBook.class);
            System.out.println(analysisBook.value());
            System.out.println(analysisBook.price());
            System.out.println(Arrays.toString(analysisBook.authors()));
        }
    }
}
@AnalysisBook(value = "Java",authors = {"马浩楠"})
class BookStore{
    @AnalysisBook(value = "MyBatis",authors = {"程明辉"})
    public void test(){

    }
}

3.5、注解的应用场景:Junit框架

  • 案例:模拟Junit框架

    • 定义若干个方法,只要加了MyTest注解,就标识需要触发执行
  • MyTest注解类同上【只能在方法上使用】

  • Test

public class AnnotationDemo04 {
    @MyTest
    public void test01(){
        System.out.println("====test01====");
    }

    public void test02(){
        System.out.println("====test02====");
    }
    @MyTest
    public void test03(){
        System.out.println("====test03====");
    }

    public static void main(String[] args) throws Exception {
        AnnotationDemo04 a = new AnnotationDemo04();
        //获取类对象
        Class c = AnnotationDemo04.class;
        //获取全部方法
        Method[] methods = c.getDeclaredMethods();
        //循环判断是否存在注解,存在执行方法
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                method.invoke(a);
            }
        }
    }
}

4.动态代理

  • 案例:

    • 模拟用户管理业务,登录、删除、查询功能,统计每个功能的耗时
  • UserService

public interface UserService {
    String login(String userName,String password);
    void deleteUsers();
    String selectUsers();
}
  • UserServiceImpl
public class UserServiceImpl implements UserService{
    @Override
    public String login(String userName, String password) {
        long startTime = System.currentTimeMillis();
        String result = "登录名和密码错误";
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if("haonan".equals(userName) && "010220".equals(password)){
            result = "登录成功";
        }
        long endTime = System.currentTimeMillis();
        System.out.println("login方法耗时:"+(endTime - startTime) /1000.0 +"s");
        return result;
    }

    @Override
    public void deleteUsers() {
        long startTime = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("delete方法耗时:"+(endTime - startTime) /1000.0 +"s");
    }

    @Override
    public String selectUsers() {
        long startTime = System.currentTimeMillis();
        String result = "查询了许多用户...";
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("login方法耗时:"+(endTime - startTime) /1000.0 +"s");
        return result;
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.login("haonan","010220");
        userService.deleteUsers();
    }
}

存在问题:每个方法都要进行性能统计,存在大量重复的代码

  • 动态代理
    • 代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,对业务功能(方法)进行代理
  • 关键步骤
    • 必须有接口,实现类要实现接口(代理通常是基于接口实现的)
    • 创建实现类对象,该对象为业务对象,紧接着为业务对象做一个代理对象
  • 优点

    • 非常的灵活,支持任意接口类型的实现类对象做代理
    • 可以为被代理对象的所有方法做代理
    • 可以在不改变方法源码的情况下,实现对方法功能的增强
    • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率
  • UserService

public interface UserService {
    String login(String userName,String password);
    void deleteUsers();
    String selectUsers();
}
  • UserServiceImpl
public class UserServiceImpl implements UserService{
    @Override
    public String login(String userName, String password) {
        String result = "登录名和密码错误";
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if("haonan".equals(userName) && "010220".equals(password)){
            result = "登录成功";
        }
        return result;
    }

    @Override
    public void deleteUsers() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String selectUsers() {
        String result = "查询了许多用户...";
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}
  • ProxyUtils
public class ProxyUtils {
    public static UserService getUserProxy(UserService obj){
        return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTime = System.currentTimeMillis();
                Object result = method.invoke(obj, args);
                long endTime = System.currentTimeMillis();
                System.out.println(method.getName()+"方法耗时:"+(endTime - startTime) /1000.0 +"s");
                return result;
            }
        });
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        UserService userService = ProxyUtils.getUserProxy(new UserServiceImpl());
        userService.login("haonan","010220");
        userService.deleteUsers();
    }
}

results matching ""

    No results matching ""