黑马程序员技术交流社区

标题: Junit 断言Assert,反射机制 [打印本页]

作者: 杨纯纯    时间: 2018-12-2 16:43
标题: Junit 断言Assert,反射机制
JUnit单元测试:
测试一个方法有没有问题, 是否能否按照我们设计的方式运行 (不用main方法直接运行)


package JunitTest;
//Junit 测试方法是否正确  ,第一步先创建一个方法
//我创建的方法是计算器,计算 有三个方法,分别是 加 减 除
public class Calculator {
    public  int sum(int a , int b){
         //   int c = 3/0;
        return a-b;
    }
    public  int sub(int a , int b){

        return a+b;

    }
    public int chu(int a , int b ){

        return  a/b;
    }
}
package JunitTest;

//正常在测试类中使用它们,即便方法写错 也不会被显示,方法仍会被执行。

public class TestCalculator {
    public static void main(String[] args) {
        int sum = new Calculator().sum(2, 2);
        System.out.println(sum);


        int sub = new Calculator().sub(3, 2);
        System.out.println(sub);

        int chu = new Calculator().chu(6, 3);
        System.out.println(chu);

    }
}
package JunitTest;

import org.junit.Assert;
import org.junit.Test;

public class TestJunit {
    //这个 本应该是加法的方法,但是方法写错了 一样是可以运行的,这个时候只能使用: 断言 来判定方法是否正确
    @Test
    public  void Testsum(){
        int sum = new Calculator().sum(2, 3);
        System.out.println(sum);
       //断言我的期望值,也就是正确方法应该有的值,后一个参数是结果,也就是sum
        Assert.assertEquals(5,sum);
    }
    //这个方法同上面那个方法一样
    @Test
    public  void Testsub(){
        int sub = new Calculator().sub(3, 2);
        System.out.println(sub);
        Assert.assertEquals(1,sub);

    }
    //这个方法是正确的
    @Test
    public  void Thwodchu(){
        int chu = new Calculator().chu(3, 1);
        System.out.println(chu);
    }
    //此测试 方法的盒子中有两个错误的方法,最后一个方法是正确的
}
    //另外两个方法 一个是After, 一个是Before
@After
    public void end(){
    System.out.println("此方法在最后一个执行");
}
@Before
    public void begin(){
    System.out.println("此测试方法方法在第一个被执行");
}

Java(反射)代码的三个阶段:
1.源代码阶段source
包含两种状态 第一种是未被编译处理时候的状态.Java

第二种是被javac编译后生成.class文件的状态

2.Class 类对象阶段
将字节码文件加载进内存,类的加载器,对应Classloader
java中有一个类 叫Class 用来描述所有字节码文件的
字节码文件中都会有一些共有的方法

成员变量                          Field                       类的对象                (封装成对象
构造方法                         Constructor        构造方法类的对象
成员方法                         Method                类的对象
反射: 将类的各个组成部分, 封装为其他对象, 这就是反射机制
好处:
                1. 可以在程序运行过程中, 操作这些对象
                2. 可以解耦, 提高程序的可扩展性


3.运行时阶段Runtime
创建对象阶段 new




反射: 获取字节码对象的3种方式
获取一个类的字节码对象的3种方式:
        1. Class.forName("全类名")
                将字节码文件加载进内存,返回Class对象
                适用场景: 多用于配置文件,将类名定义在配置文件中. 读取文件, 加载类
        2. 类名.class                         .
                通过类名的属性class获取
                适用场景: 多用于参数的传递  getConstructor(String.class, int.class)
        3. 对象.getClass()
                getClass()方法在Object类中定义
                适用场景: 多用于对象的获取字节码的方式 p.getClass()

反射: Class的方法概述


java.lang.Class<T>类: 表示一个类的字节码对象, 其中包含该类中定义的内容
// 成员方法
// 1. 获取成员变量们
        Field[] getFields(): 获取所有 public 的成员变量
        Field getField(String name): 获取指定名称的 public 的成员变量
        Field[] getDeclaredFields(): 获取所有的成员变量, 不考虑权限修饰符
        Field getDeclaredField(String name): 获取指定名称的成员变量, 不考虑权限修饰符
// 2. 获取构造方法们
        Constructor<?>[] getConstructors(): 获取所有 public 的构造方法
        Constructor<T> getConstructor(Class<?>... parameterTypes): 获取指定的 public 构造方法
        Constructor<?>[] getDeclaredConstructors(): 获取所有的构造方法, 不考虑权限修饰符
        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 获取指定的构造方法, 不考虑权限修饰符
// 3. 获取成员方法们:
        Method[] getMethods(): 获取所有 public 的成员方法
        Method getMethod(String name, Class<?>... parameterTypes) : 获取指定的 public 成员方法
        Method[] getDeclaredMethods(): 获取所有的成员方法, 不考虑权限修饰符
        Method getDeclaredMethod(String name, Class<?>... parameterTypes): 获取指定的成员方法, 不考虑权限修饰符
// 4. 获取Class对象代表的类的全类名
        String getName(): 获取当前Class对象代表的类的全类名
// 5. 创建对象
        T newInstance(): 使用当前类的空参构造, 创建一个对象
反射: 利用反射实现可以运行任意类的任意方法的框架案例
补充:

java.lang.Class<T>
        // 成员方法
        ClassLoader getClassLoader(): 返回该类的类加载器
       
java.lang.ClassLoader: 类加载器 加载.class文件到内存的方法区中, 其他类型文件.properties
        // 成员方法
        InputStream getResourceAsStream(String name): 读取相对于 out/production/模块名 目录中的文件, 返回一个字节流

       
使用类加载器加载配置文件
        // 随便获取一个类的字节码对象
        Class clazz = 类名.class;
        // 用字节码对象获取类加载器, 可加载bin目录中编译的文件
        ClassLoader classLoader = clazz.getClassLoader();
        // 使用类加载器加载一个文件, 返回一个字节流
        InputStream is = classLoader.getResourceAsStream("相对于src目录的相对路径");
        // 有了字节流, 就可以使用Properties的load(InputStream in)方法读取配置
        Properties p = new Properties();
        p.load(is);
        String value = p.getProperty("key");
需求:
写一个"框架"程序, 框架写好后"不需改动框架的任何代码", 只需修改配置文件中的类名和方法名, 就能让框架程序帮我们创建配置文件中指定的类的对象, 并且执行其中指定的方法

分析:
        实现技术:
                1. 配置文件: Properties 加载配置信息
                2. 反射: 使用配置文件中的全类名, 获取该类的字节码对象, 创建该类对象, 调用指定的方法
步骤:
        建类Person:
                定义成员方法 public void eat(){}. 方法中输出"吃东西"
        建类Student:
                定义成员方法 public void sleep(){} 方法中输出"睡觉"
        在src目录下创建一个文件: pro.properties
                填写以下键值对:
                className=Person类的全名
                methodName=eat
        建类Test, 定义main方法, 在main方法中:
                创建Properties对象
                获取当前Test类的字节码对象, 使用字节码对象调用getClassLoader()获取一个类加载器对象
                使用类加载器对象调用 getResourceAsStream("pro.properties") 获取一个InputStream对象
                使用Properties对象的load方法加载InputStream对象
                        // 获取类加载器
            ClassLoader cl = 当前类名.class.getClassLoader();
            InputStream is = cl.getResourceAsStream("pro.properties");
            // 创建Properties对象, 加载流
            Properties properties = new Properties();
            properties.load(is);
                从Properties对象中根据键"className"和"methodName"分别获取类名字符串和方法名字符串
                使用Class.forName("类的全名")方法传入读取到的类名, 获取该类字节码对象
                通过字节码对象创建该类对象, 并获取指定方法名的Method对象
                通过Method对象的invoke方法调用该方法, 查看是否调用吃饭方法
        修改配置文件中的类名为Student类的全名, 方法为sleep, 重新运行代码查看是否调用了睡觉方法
【代码】:
# 配置文件 src目录下的pro.properties
className=com.itheima.test03.Student
methodName=sleep

public class Person {

    public void eat() {
        System.out.println("人吃饭");
    }
}

public class Student {

    public void sleep() {
        System.out.println("学生睡觉");
    }
}

/*
模拟实现框架
    目的: 加载配置文件中指定的类, 并调用指定的方法
*/
public class Test {
    public static void main(String[] args) throws Exception {
        /*
            加载配置文件:
                通过类加载器加载配置文件变成字节输入流
                然后让Properties加载流中的数据
         */
        ClassLoader classLoader = Test.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        Properties properties = new Properties();
        properties.load(is);

        /*
            获取配置信息:
                通过Properties的键, 获取值
         */
        // 通过配置文件中的类名key获取真正的类名的value
        String className = properties.getProperty("className");
        // 通过配置文件中的方法名key获取真正的方法名的value
        String methodName = properties.getProperty("methodName");

        /*
            通过反射技术创建指定类的对象, 并调用方法:
         */
        //  先通过读取到的全类名, 获取字节码对象.
        Class cls = Class.forName(className);
        // 通过字节码对象创建该类的对象
        Object obj = cls.newInstance();
        // 通过字节码对象获取指定的方法对象
        Method method = cls.getDeclaredMethod(methodName);
        // 调用方法
        method.invoke(obj);
    }
}












欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2