Joe1sn's Cabinet

JavaWeb笔记-Junit与反射

Junit单元测试与反射

黑盒-白盒

junit使用:白盒测试

步骤:

  1. 定义一个测试类
    • 测试类名:被测试类+Test CalculatorTest
    • 包名:xxx.xxx.xxx.test
  2. 定义测试方法:可以独立执行,建议
    • 方法名:test测试方法名 testAdd()
    • 返回值:void(独立运行)
    • 参数列表:空参
  3. 方法+@test注解
  4. 导入Junit依赖环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package test;

import junit.Calculator;
import org.junit.Test;

public class CalculatorTest {
//测试add方法
@Test
public void testAdd(){
//System.out.printf("Excuted");
//创建计算器对象
Calculator c = new Calculator();
//2.调用add方法
int result = c.add(1,2);
System.out.println(result);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package junit;
//计算器
public class Calculator {
//加法
public int add(int a, int b){
return a+b;
}
//减法
public int sub(int a, int b){
return a-b;
}
}

image-20220315202108710

在add添加除0异常

1
2
3
4
5
6
7
8
9
10
11
12
13
package junit;
//计算器
public class Calculator {
//加法
public int add(int a, int b){
int i = 3/0;
return a+b;
}
//减法
public int sub(int a, int b){
return a-b;
}
}

image-20220315202216277

假设原方法中add写成了-,使用断言报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test;

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

public class CalculatorTest {
//测试add方法
@Test
public void testAdd(){
//System.out.printf("Excuted");
//创建计算器对象
Calculator c = new Calculator();
//2.调用add方法
int result = c.add(1,2);
//断言assert
Assert.assertEquals(3, result);
}

}

image-20220315202757465

同理可以得到sub的测试函数

1
2
3
4
5
6
7
//测试sub方法
@Test
public void testSub(){
Calculator c = new Calculator();
int result = c.sub(2,1);
Assert.assertEquals(3,result);
}

注解补充

@Before

  • 初始化方法

  • 用于资源的申请,所有测试方法执行之前都会先执行该方法

  • 在测试方法之前被自动执行

@After

  • 释放资源

  • 在所有测试方法执行完后都会自动执行该方法

  • 测试方法执行之后自动执行

1
2
3
4
5
6
7
8
9
10
11
12
13
//初始化方法
//用于资源的申请,所有测试方法执行之前都会先执行该方法
@Before
public void init(){
System.out.println("init...");
}

//释放资源
//在所有测试方法执行完后都会自动执行该方法
@After
public void close(){
System.out.println("closed");
}

image-20220315203550168

反射

框架设计的灵魂

框架:半成品软件。可以在框架基础上继续开发,简化编码

反射:将类的各个组成部分封装为其他对象(反射机制

  • 好处:
    • 在程序运行中操作这些对象
    • 可以解耦,提高程序可拓展性

Java代码运行阶段

获取Class对象方式

  1. Source:

    对用于配置文件,将类名定义在配置文件中。读取文件,加载类

    Class.forName(“全类名”):将字节码文件加载进内存返回class对象

    image-20220315212602999

  2. Class:

    多用于参数传递

    依据类名属性class,获取class对象

    image-20220315212737256

  3. Runtime

    多用于对象的获取字节码的方式

    对象.getClass():方法封装在Object中,被所有对象继承了

    image-20220315212901262

同一个字节码文件在同一次程序运行中,只会被加载一次;不论通过那种方式取得,都是同一个对象

每个类对象都不同

image-20220315213311200

使用class对象

功能:

  • 获取:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package LearnJunit.domain;

    public class Person {
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {
    }

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

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

    public String getName() {
    return name;
    }

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

    public int getAge() {
    return age;
    }

    @Override
    public String toString() {
    return "Person{" +
    "name='" + name + '\'' +
    ", age=" + age +
    ", a='" + a + '\'' +
    ", b='" + b + '\'' +
    ", c='" + c + '\'' +
    ", d='" + d + '\'' +
    '}';
    }
    }
    • 获取成员变量们

      • Field[] getFields():获取所有public成员变量

        1
        2
        3
        4
        Field[] fields = personClass.getFields();
        for (Field field: fields){
        System.out.println(field);
        }
      • Field getField(String name):获取所有指定名称public成员变量

        1
        Field a = personClass.getField("a");
      • Field[] getDeclaredFields()

        1
        2
        3
        4
        5
        //获取所有成员变量,不考虑修饰
        Field[] DeclaredFields = personClass.getDeclaredFields();
        for (Field getDeclaredField: DeclaredFields){
        System.out.println(getDeclaredField);
        }
      • Field getDeclaredField(String name)

        反射私有、保护成员变量,要设置忽略访问权限的修饰符安全监测

        1
        2
        3
        4
        5
        6
        7
        //1.4获取单个私有
        System.out.println("---------------");
        Field d = personClass.getDeclaredField("d");
        //1.4.1忽略安全访问
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);
    • 获取构造方法们

      • Constructor<?>[] getConstructors()

        1
        2
        Constructor constructor =  personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
      • Constructor<T> getConstructor(String name)

      • Constructor<?>[] getDeclaredConstructors()

      • Constructor<T> getDeclaredConstructor(String name)

    • 获取成员方法们

      • method[] getMethods()

        1
        2
        3
        4
        5
        //获取所有public修饰方法
        Method[] funcs = personClass.getMethods();
        for (Method func:funcs){
        System.out.println(func);
        }
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        public java.lang.String LearnJunit.domain.Person.toString()
        public java.lang.String LearnJunit.domain.Person.getName()
        public void LearnJunit.domain.Person.setName(java.lang.String)
        public int LearnJunit.domain.Person.getAge()
        public void LearnJunit.domain.Person.setAge(int)
        public void LearnJunit.domain.Person.eat()
        public void LearnJunit.domain.Person.eat(java.lang.String)
        public final void java.lang.Object.wait() throws java.lang.InterruptedException
        public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
        public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
        public boolean java.lang.Object.equals(java.lang.Object)
        public native int java.lang.Object.hashCode()
        public final native java.lang.Class java.lang.Object.getClass()
        public final native void java.lang.Object.notify()
        public final native void java.lang.Object.notifyAll()

        因为Person类继承的是Object对象,所以有些隐藏函数,即从Object继承下来的函数

      • method getMethod(String name)

        1
        2
        3
        4
        5
            //获取指定方法名称
        //名字,参数列表匹配
        Method eat = personClass.getMethod("eat");
        Method eat2 = personClass.getMethod("eat",String.class);
        eat2.invoke(p,"fish");
      • method[] getDeclaredMethods()

      • method getDeclaredMethod(String name)

    • 获取类名

      • String getName()
  • 操作

    • Field成员变量

      设置值(get)

      1
      2
      3
      4
      5
      Field a = personClass.getField("a");
      //获取成员变量a的值
      Person p = new Person();
      Object value = a.get(p);
      System.out.println(value);

      获取值(set)

      1
      2
      a.set(p,"John");
      System.out.println(p);

      image-20220316104826899

    • 暴力反射:.setAccessible(true); 对所有反射都有效

      搭配getDeaclearedXXX使用

      这仅仅是打印名称和比较,不需要设置;但是如果要对对象进行相关操作,必须设置;不设置的话操作受限

    • Constructor构造方法

      创建对象 T newInstance(Object... initargs)

      1
      2
      3
      //1.2创建对象
      Object person = constructor.newInstance("Jonh",5);
      System.out.println(person);

      如果构造使用空参创造对象,可简化 class对象的 newInstance

      1
      2
      3
      Object o = personClass.newInstance();
      System.out.println(o);
      System.out.println("-------------");
    • Method成员对象

      执行方法

      1
      2
      3
      4
      5
      6
      //获取指定方法名称
      Method eat = personClass.getMethod("eat");
      //创建方法对象
      Person p = new Person();
      //执行方法
      eat.invoke(p);

      获取方法名称

      1
      System.out.println(func.getName());
    • 获取类名

      1
      2
      3
      //获取类名
      String classname = personClass.getName();
      System.out.println(classname);

反射案例

案例:

需求:一个框架,可以帮我们创建任意类的对象,并且执行任意方法

不能改变该类的任何代码,
创建任意对象执行任意方法

一般方式:

1
2
3
4
5
6
7
8
9
10
package LearnJunit.reflect;
import LearnJunit.domain.Person;
//假设框架类
public class ReflectFramework {
public static void main(String[] args) {
//可以创建任意类对象,可以执行任意方法
Person p = new Person();
p.eat();
}
}

这里的框架是提前学好的,不能改变;若要使用student类,则代码会改变

实现

  • 配置文件
  • 反射

步骤

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中

    src中创建配置文件

    1
    2
    className=LearnJunit.domain.Person
    methodName=eat
  2. 在程序中加载读取配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //1.加载配置文件
    //1.1创建配置对象
    Properties pro = new Properties();
    //1.2加载配置文件,转换为集合
    //1.2.1获取class目录下配置文件
    ClassLoader classLoader = ReflectFramework.class.getClassLoader();//获得当前类路径
    InputStream is = classLoader.getResourceAsStream("config.properties");//读取路径下的config.properties
    pro.load(is);
    //2.获取配置文件中定义的数据
    String className = pro.getProperty("className");
    String methodName = pro.getProperty("methodName");
  3. 使用反射技术来加载类文件进内存,创建对象并执行方法

    1
    2
    3
    4
    5
    6
    7
    //3.加载类进内存
    Class cls = Class.forName(className);
    //4.创建对象
    Object obj = cls.newInstance();
    //5.获取方法对象
    Method method = cls.getMethod(methodName);
    method.invoke(obj);

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package LearnJunit.reflect;

import LearnJunit.domain.Person;
import LearnJunit.domain.Student;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.stream.IntStream;

//假设框架类
public class ReflectFramework {
public static void main(String[] args) throws
IOException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException,
NoSuchMethodException {
//可以创建任意类对象,可以执行任意方法
//不能改变该类的任何代码,
//创建任意对象执行任意方法

//1.加载配置文件
//1.1创建配置对象
Properties pro = new Properties();
//1.2加载配置文件,转换为集合
//1.2.1获取class目录下配置文件
ClassLoader classLoader = ReflectFramework.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("config.properties");
pro.load(is);

//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");

//3.加载类进内存
Class cls = Class.forName(className);

//4.创建对象
Object obj = cls.newInstance();

//5.获取方法对象
Method method = cls.getMethod(methodName);
method.invoke(obj);
}
}

这样只需要修改配置文件就可以了

image-20220316204511093

image-20220316204553895