JAVASE——单元测试、发射

一、单元测试

1.1 介绍

单元测试:针对最小的功能单元,编写测试代码对其进行正确性测试。

为了测试更加方便,有一些第三方的公司或者组织提供了很好用的测试框架,给开发者使用。Junit是第三方公司开源出来的,用于对代码进行单元测试的工具(IDEA已经集成了junit框架)。相比于在main方法中测试有如下几个优点。

  • 能灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
  • 不需要程序员分析测试结果,会自动生成测试报告

1668507051101

先准备一个类:

1
2
3
4
5
6
public class StringUtil {
public static void printNumber(String name){
System.out.println("名字长度:"+name.length());
}
}

为之写一个测试类:

1
2
3
4
5
6
7
8
9
10
import org.junit.Test;
public class StringUtilTest {
@Test
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}

}

1.2 单元测试断言

断言:程序员可以预测程序的运行结果,检查程序的运行结果是否与预期一致。

在StringUtil类中新增一个测试方法

1
2
3
4
5
6
public static int getMaxIndex(String data){
if(data == null){
return -1;
}
return data.length();
}

接下来,我们在StringUtilTest类中写一个测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StringUtilTest{
@Test
public void testGetMaxIndex(){
int index1 = StringUtil.getMaxIndex(null);
System.out.println(index1);

int index2 = StringUtil.getMaxIndex("admin");
System.out.println(index2);

//断言机制:预测index2的结果
Assert.assertEquals("方法内部有Bug",4,index2);
}
}

运行测试方法,结果如下图所示,表示我们预期值与实际值不一致

image-20241105182642868

1.3 Junit框架的常用注解

1668508373865

下面进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class StringUtilTest{
@Before
public void test1(){
System.out.println("--> test1 Before 执行了");
}
@BeforeClass
public static void test11(){
System.out.println("--> test11 BeforeClass 执行了");
}
@After
public void test2(){
System.out.println("--> test2 After 执行了");
}
@AfterCalss
public static void test22(){
System.out.println("--> test22 AfterCalss 执行了");
}
}

应用场景:

假设我想在每个测试方法中使用Socket对象,并且用完之后,需要把Socket关闭。代码就可以按照下面的结构来设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StringUtilTest{
private static Socket socket;
@Before
public void test1(){
System.out.println("--> test1 Before 执行了");
}
@BeforeClass
public static void test11(){
System.out.println("--> test11 BeforeClass 执行了");
//初始化Socket对象
socket = new Socket();
}
@After
public void test2(){
System.out.println("--> test2 After 执行了");
}
@AfterCalss
public static void test22(){
System.out.println("--> test22 AfterCalss 执行了");
//关闭Socket
socket.close();
}
}

二、反射

反射技术:指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)。

  • 加载类,获取字节码对象 class对象
  • 获取类的构造器:constructor对象
  • 获取类的成员变量:Filed对象
  • 获取类的成员方法:Method对象

2.1 获取类的字节码

将字节码加载到内存,我们需要获取到的字节码对象。

1668576691591

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import javaday01.Student;
public class Test1Class {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
System.out.println(c1.getName());//类的全名
System.out.println(c1.getSimpleName());//获取简单类名

Class c2 = Class.forName("javaday01.Student");
System.out.println(c1 == c2); //true

Student s = new Student();
Class c3 = s.getClass();
System.out.println(c2==c3); // true
}
}

2.2 获取类的构造器

上一节我们已经可以获取到类的字节码对象了。接下来,我们学习一下通过字节码对象获取构造器,并使用构造器创建对象。

1668577010983

写一个类Cat

1
2
3
4
5
6
7
8
9
10
11
package javaday01;

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

public Cat(){}
public Cat(String name, int age){}

}

  • getDeclaredConstructors()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package javaday01;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class Test2Constructor {
@Test
public void testGetConstructor(){
//1. 获取这个类的class对象
Class c = Cat.class;
//2 .获取这个类的全部构造器(只要存在就能拿到)
Constructor[] constructors = c.getDeclaredConstructors();
// 输出
for(Constructor constructor : constructors){
System.out.println(constructor.getName()+"--->参数个数: "+constructor.getParameterCount());
}
}
}

  • 单个构造器
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
package javaday01;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class Test2Constructor {
@Test
public void testGetConstructor() throws Exception{
//1. 获取这个类的class对象
Class c = Cat.class;
//2 .获取这个类的全部构造器(只要存在就能拿到)
//Constructor[] constructors = c.getDeclaredConstructors();

// // 输出
// for(Constructor constructor : constructors){
// System.out.println(constructor.getName()+"--->参数个数: "+constructor.getParameterCount());
// }
// 获取某个构造器(只能是public)
Constructor constructor1 = c.getConstructor();
System.out.println(constructor1.getName()+"---> 参数个数:"+constructor1.getParameterCount());

// 获取private的
Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName()+"---> 参数个数:"+constructor2.getParameterCount());



}


}

2.3 反射获取构造器的作用

构造器的作用:初始化对象并返回

1668578639149

由于构造器是private修饰的,先需要调用setAccessible(true) 表示禁止检查访问控制,然后再调用newInstance(实参列表) 就可以执行构造器,完成对象的初始化了。

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
package javaday01;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class Test2Constructor {
@Test
public void testGetConstructor() throws Exception{
//1. 获取这个类的class对象
Class c = Cat.class;
//2 .获取这个类的全部构造器(只要存在就能拿到)
//Constructor[] constructors = c.getDeclaredConstructors();

// // 输出
// for(Constructor constructor : constructors){
// System.out.println(constructor.getName()+"--->参数个数: "+constructor.getParameterCount());
// }
// 获取某个构造器(只能是public)
Constructor constructor1 = c.getConstructor();
System.out.println(constructor1.getName()+"---> 参数个数:"+constructor1.getParameterCount());
constructor1.setAccessible(true);
Cat cat = (Cat) constructor1.newInstance();
System.out.println(cat);


// 获取private的
Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName()+"---> 参数个数:"+constructor2.getParameterCount());
constructor2.setAccessible(true);
Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 12);
System.out.println(cat2);




}


}

2.4 反射获取成员变量&使用

1668579517323

  • Cat类有如下成员变量:

    image-20241105194614849

    执行一下代码获取成员变量

    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
    package javaday01;

    import org.junit.Test;

    import java.lang.reflect.Field;

    public class Test3Field {
    @Test
    public void testGetFiled() throws Exception{
    Class c = Cat.class;
    // 获取所有字段
    Field[] fields = c.getDeclaredFields();
    for (Field filed : fields){
    System.out.println(filed.getName() + "---> " + filed.getType());
    }
    // 获取某个字段
    Field fName = c.getDeclaredField("name");
    System.out.println(fName.getName() + "---> "+ fName.getType());

    Field fAge = c.getDeclaredField("age");
    System.out.println(fAge.getName() + "---> "+ fAge.getType());





    }
    }

  • 给成员变量赋值和获取值的方法

    1668580075962

    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
    package javaday01;

    import org.junit.Test;

    import java.lang.reflect.Field;

    public class Test3Field {
    @Test
    public void testGetFiled() throws Exception{
    Class c = Cat.class;
    // 获取所有字段
    Field[] fields = c.getDeclaredFields();
    for (Field filed : fields){
    System.out.println(filed.getName() + "---> " + filed.getType());
    }
    // 获取某个字段
    Field fName = c.getDeclaredField("name");
    System.out.println(fName.getName() + "---> "+ fName.getType());

    Field fAge = c.getDeclaredField("age");
    System.out.println(fAge.getName() + "---> "+ fAge.getType());

    //赋值
    Cat cat = new Cat();
    fName.setAccessible(true);
    fName.set(cat, "加菲猫");
    System.out.println(cat);

    //取值
    String name = (String) fName.get(cat);
    System.out.println(name);








    }
    }

    image-20241105195125154

2.5 反射获取成员方法

在Java中反射包中,每一个成员方法用Method对象来表示,通过Class类提供的方法可以获取类中的成员方法对象。如下下图所示
1668580761089

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

import java.lang.reflect.Method;

public class Test3Method {
public static void main(String[] args) {
Class c = Cat.class;
Method[] methods = c.getDeclaredMethods();

for (Method method:methods){
System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());
}

}


}

执行上面的代码,运行结果如下图所示:打印输出每一个成员方法的名称、参数格式、返回值类型

image-20241105200325689

在Method类中提供了方法,可以将方法自己执行起来。

1668581800777

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
package javaday01;

import java.lang.reflect.Method;

public class Test3Method {
public static void main(String[] args) throws Exception {
Class c = Cat.class;
Method[] methods = c.getDeclaredMethods();

for (Method method:methods){
System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());
}

Method run = c.getDeclaredMethod("run");
Cat cat = new Cat();
run.setAccessible(true);
Object rs1 = run.invoke(cat);
System.out.println(rs1);

Method eat = c.getDeclaredMethod("eat",String.class);
eat.setAccessible(true);
Object rs2 = eat.invoke(cat,"鱼儿");
System.out.println(rs2);




}


}

image-20241105200905019

2.6 反射的应用

反射的核心作用是用来获取类的各个组成部分并执行他们

1668583255686

让我们写一个框架,能够将任意一个对象的属性名和属性值写到文件中去。不管这个对象有多少个属性,也不管这个对象的属性名是否相同。

1
2
3
4
5
6
7
8
9
1.先写好两个类,一个Student类和Teacher
2.写一个ObjectFrame类代表框本架
ObjectFrame类中定义一个saveObject(Object obj)方法,用于将任意对象存到文件中去
参数:Object obj: 就表示要存入文件中的对象

3.编写方法内部的代码,往文件中存储对象的属性名和属性值
1)参数obj对象中有哪些属性,属性名是什么实现值是什么,中有对象自己最清楚。
2)接着就通过反射获取类的成员变量信息了(变量名、变量值)
3)把变量名和变量值写到文件中去

写一个ObjectFrame表示自己设计的框架,代码如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package javaday01;

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class ObjectFrame {
public static void saveObject(Object obj) throws Exception{
PrintStream ps = new PrintStream(new FileOutputStream("D:\\learn_java\\javaday01\\src\\data.txt", true));
Class c = obj.getClass();
ps.println("---------"+c.getSimpleName()+"---------");
Field[] fields = c.getDeclaredFields();

// 把变量名和变量值写入文件
for(Field field : fields){
String name = field.getName();
//Object value = field.get(obj) + " ";
ps.println(name);
}
ps.close();

}
}

使用自己设计的框架,往文件中写入Student对象的信息和Teacher对象的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package javaday01;

import org.junit.Test;

public class Test5Framw {
@Test
public void save() throws Exception{
Student s1 = new Student(45, "jack");
Teacher t1 = new Teacher("wendy", 30000);
ObjectFrame.saveObject(s1);
ObjectFrame.saveObject(t1);

}

}

三、注解

3.1 定义

Java注解是代码中的特殊标记,比如@Override、@Test等,作用是:让其他程序根据注解信息决定怎么执行该程序。

  • 注解不光可以用在方法上,还可以用在类上、变量上、构造器上等位置。

JAVASE——单元测试、发射
https://wendyflv.github.io/2024/11/05/JAVASE——单元测试、发射/
作者
Wendyflv
发布于
2024年11月5日
许可协议