反射和注解
1. 类加载器
1.1 类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化。
类的加载:
- 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
- 任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接:
- 验证阶段:用于检验被加载的类是否具有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化:
- 在该阶段,主要就是对类变量进行初始化
类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统一次执行这些初始化语句
注意:在执行第二个步骤时,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建每个类或者接口对应的java.lang.Class对象
- 初始化每个类的子类
- 直接使用java.exe命令来运行某个主类
1.2 类加载器
类加载器的作用:负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
ClassLoader:负责加载类的对象
-
Java运行时具有以下内置类加载器:
-
Bootstrap class loader: 它是虚拟机的内置类加载器,通常表示为
null
,并且没有父级。 -
Platform class loader: 平台类加载器可以看到所有平台类 ,它们可以用作
ClassLoader
实例的父ClassLoader
。 平台类包括Java SE平台API,它们的实现类以及特定于JDK的运行时类。 -
System class loader:它也称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于在应用程序类路径,模块路径和JDK特定工具上定义类。
平台类加载器是系统类加载器的父级或祖先,内置类加载器时平台类加载器的父级或祖先
-
- ClassLoader中的两个方法
- static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
- ClassLoader getParent():返回父类加载器进行委派
package com.Study;
/**
* @ClassName ClassLoaderDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/10 16:17
* @Version 1.0
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
//static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
ClassLoader scl = ClassLoader.getSystemClassLoader();
System.out.println(scl);//AppClassLoader
//ClassLoader getParent():返回父类加载器进行委派
ClassLoader p = scl.getParent();
System.out.println(p);//PlatformClassLoader
ClassLoader p1 = p.getParent();
System.out.println(p1);//null
}
}
2. 反射
2.1 反射描述
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期仍然可以扩展
2.2 获取Class类的对象
我们想要通过反射去使用一个类,首先我们要获取到类的字节码文件对象,也就是类型为Class类型的对象
这里我们提供三种方式获取Class类型的对象
-
使用类的class属性来获取该类对应的Class对象。举例:Student.class将会返回Student类对应的Class对象
-
调用对象的getClass()方法,返回该对象所属类对应的Class对象
该方法时Object类中的方法,所有的Java对象都可调用该方法
-
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
package com.Study02;
/**
* @ClassName RefelectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/10 16:33
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;//最简易
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1.equals(c2));
System.out.println("---------------");
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s=new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1.equals(c3));
System.out.println("---------------");
//使用Class类中的静态方法
Class<?> c4 = Class.forName("com.Study02.Student");//最常用
System.out.println(c1.equals(c4));
}
}
package com.Study02;
/**
* @ClassName Student
* @Description TODO
* @Author Mark
* @Date 2022/7/10 16:33
* @Version 1.0
*/
public class Student {
//三个成员变量:一个私有、一个默认、一个公共
private String name;
int age;
public String address;
//构造方法:一个私有、一个默认、一个公共
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
//成员方法:一个私有,四个公共
private void function() {
System.out.println("function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + ',' + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
",age=" + age +
",address='" + address + '\'' +
'}';
}
}
2.3 反射获取构造方法并使用
package com.Study03;
import com.Study02.Student;
import com.sun.security.jgss.GSSUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/10 16:50
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取Class对象
Class<?> c = Class.forName("com.Study02.Student");//得到了该类的字节码文件c
// getConstructors() 返回一个包含Constructor构造器对象的数组,Constructor构造器对象反映了此Class对象所表示的类的所有公共构造函数。
Constructor<?>[] cons = c.getConstructors();//所有的公共构造方法的数组
for(Constructor con:cons){
System.out.println(con);
}
System.out.println("---------------");
//getDeclaredConstructors() 返回一个反映由该Class对象表示的类声明的所有构造函数的Constructor对象的数组
Constructor<?>[] cons1 = c.getDeclaredConstructors();//所有构造方法的数组
for (Constructor con:cons1){
System.out.println(con);
}
System.out.println("---------------");
//getConstructor(类<?>... parameterTypes) 返回一个Constructor对象,该对象反映由Class对象表示的类的指定的公共构造函数
//getDeclaredConstructor(类<?>... parameterTypes) 返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数。
//参数表示的意思是:你要获取的构造方法的参数个数和数据类型对应的字节码文件对象
Constructor<?> con = c.getConstructor();//通过c获取单个无参构造方法对象con
// Student s=new Student();
// System.out.println(s);
//Constructor提供了一个类的单个构造函数的信息和访问权限
//T newInstance(Object... initargs) 使用此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
Object obj = con.newInstance();//通过无参构造方法对象con的newInstance()创建了一个对象
System.out.println(obj);
}
}
Class类中用于获取构造方法的方法
Constructor<?>[] getConstructors()
:返回所有的公共构造方法对象的数组Constructor<?>[] getDeclaredConstructors()
:返回所有的构造方法对象的数组Constructor<T>[] getConstructor(Class<?>... parameterTypes)
:返回单个公共构造方法对象Constructor<T>[]getDeclaredConstructor(Class<?>... parameterTypes)
:返回单个构造方法对象
Constructor类中用于创建对象的方法
T newInstance(Object... initargs)
:根据指定的构造方法创建对象
2.4 反射获取构造方法并使用练习
练习一
通过反射实现如下操作
- Student s=new Student("王昭君",25,"西安");
- System.out.println(s);
package com.Study04;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/10 17:22
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("com.Study02.Student");
//public Student(String name, int age, String address)
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
//基本数据类型也可以通过.class得到对应的Class类型
Object obj = con.newInstance("王昭君", 25, "西安");
System.out.println(obj);
}
}
基本数据类型也可以通过.class得到对应的Class类型
练习二
- Student s=new Student("王昭君");
- System.out.println(s);
package com.Study05;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/10 22:13
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("com.Study02.Student");
//private Student(String name)
Constructor<?> con = c.getDeclaredConstructor(String.class);
//私有构造方法这里无法创建对象
//使用暴力反射方式:
//public void setAccessible(boolean flag):值为true,取消访问检查
con.setAccessible(true);
//此时就可创建对象了
Object obj = con.newInstance("王昭君");//java.lang.IllegalAccessException
System.out.println(obj);
}
}
当构造方法为私有时,无法创建对象
使用暴力反射方式:public void setAccessible(boolean flag)
:当值为true时,取消访问检查
此时就可创建对象了
2.5 反射获取成员变量并使用
Class类中获取成员变量的方法:
Field[] getFields()
:返回所有公共成员变量对象的数组Field[] getDeclaredFields()
:返回所有成员变量对象的数组Field getField(String name)
:返回单个公共成员变量对象Field getDeclaredField(String name)
:返回单个成员变量对象
成员变量的使用:
- 获取Class对象
- 获取Field字段
- 获取构造方法创建对象
- 使用Field类中方法
set(Object obj,Object value)
给obj对象的成员变量赋值为value
package com.Study06;
import com.Study02.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/11 12:33
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取Class对象
Class<?> c = Class.forName("com.Study02.Student");
//getFields() 返回一个包含Field字段对象的数组,Field字段对象反映此Class类对象所表示的类或接口的所有可访问公共字段。
//getDeclaredFields() 返回Field字段对象的数组,反映由此Class类对象表示的类或接口声明的所有字段。
Field[] f = c.getFields();
for (Field field:f){
System.out.println(field);
}
System.out.println("--------------");
Field[] f2 = c.getDeclaredFields();
for (Field field1:f2){
System.out.println(field1);
}
System.out.println("--------------");
// Student s=new Student();
// s.address="西安";
//getField(String name) 返回Field字段对象,该对象反映此Class类对象表示的类或接口的指定公共成员字段。
//getDeclaredField(String name) 返回Field字段对象,该对象反映此Class类对象表示的类或接口的指定声明字段。
Field addressField = c.getField("address");
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//void set(Object obj, Object value) 将指定对象参数上此Field字段对象表示的字段设置为指定的新值。
addressField.set(obj,"西安");//给obj的成员变量addressField赋值为“西安”
System.out.println(obj);
}
}
2.6 反射获取成员变量并使用练习
练习
通过反射实现如下操作:
- Student s=new Student();
- s.name="王昭君";
- s.age=25;
- s.address="西安";
- System.out.println(s);
package com.Study07;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/11 12:56
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取Class对象
Class<?> c = Class.forName("com.Study02.Student");
Field addressField = c.getField("address");
Field nameField = c.getDeclaredField("name");
Field ageField = c.getDeclaredField("age");
nameField.setAccessible(true);
ageField.setAccessible(true);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
nameField.set(obj,"王昭君");
ageField.set(obj,25);
addressField.set(obj,"西安");
System.out.println(obj);
}
}
2.7 反射获取成员方法并使用
Class类中用于获取成员方法的方法:
Method getMethods()
:返回所有公共成员方法对象的数组,包括继承的Method getDeclaredMethods()
:返回所有成员方法对象的数组,不包括继承的Method getMethod(String name,Class<?>...paraneterTypes)
:返回单个公共成员方法对象Method getDeclaredMethod(String name,Class<?>...paraneterTypes)
:返回单个成员方法对象
成员方法的使用:
- 获取Class对象
- 获取成员方法
- 获取构造方法创建对象
- 使用Method类中的方法
Object invoke(Object obj, Object... args)
调用成员方法
package com.Study08;
import com.Study02.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @ClassName ReflectDemo
* @Description TODO
* @Author Mark
* @Date 2022/7/11 13:09
* @Version 1.0
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("com.Study02.Student");
// getMethods() 返回一个包含方法对象的数组,方法对象反映此Class类对象所表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超接口继承的类
// getDeclaredMethods() 返回一个包含方法对象的数组,方法对象反映此Class类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
Method[] ms = c.getDeclaredMethods();
for (Method method:ms){
System.out.println(ms);
}
System.out.println("-----------");
// Method[] ms2 = c.getDeclaredMethods();
// for (Method method:ms2){
// System.out.println(ms2);
// }
//Method getMethod(String name, 类<?>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法。
//Method getDeclaredMethod(String name, 类<?>... parameterTypes)返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
//public void method1()
Method m = c.getMethod("method1");
// Student s=new Student();
// s.method1();
//获取无参构造对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Method提供有关类和接口上单一方法的信息和访问权限
//Object invoke(Object obj, Object... args) 在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
//Object:返回值类型 obj:调用方法的对象 args:方法需要的参数
m.invoke(obj);
//public void method2(String s) {
// System.out.println("method:" + s);
// }
Method m2 = c.getMethod("method2", String.class);
m2.invoke(obj,"sss");
//public String method3(String s, int i) {
// return s + ',' + i;
// }
Method m3 = c.getDeclaredMethod("method3", String.class, int.class);
Object d = m3.invoke(obj, "ddd", 2);
System.out.println(d);
//private void function() {
// System.out.println("function");
// }
Method f = c.getDeclaredMethod("function");
f.setAccessible(true);
f.invoke(obj);
}
}
2.8 反射练习
练习一
有一个ArrayList<Integer>
集合,在这个集合中添加一个字符串数据
package com.Test01;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @ClassName ReflectTest
* @Description TODO
* @Author Mark
* @Date 2022/7/12 18:32
* @Version 1.0
*/
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> arrayList=new ArrayList<>();
// arrayList.add(10);
// arrayList.add(20);
Class<? extends ArrayList> c = arrayList.getClass();
Method m = c.getMethod("add", Object.class);
m.invoke(arrayList,"Hello");
m.invoke(arrayList,"Java");
m.invoke(arrayList,"World");
System.out.println(arrayList);
}
}
反射可以越过泛型检查获取到原始方法所需要的参数类型
练习二
通过配置文件运行类中的方法
package com.Test02;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @ClassName ReflectTest
* @Description TODO
* @Author Mark
* @Date 2022/7/12 18:42
* @Version 1.0
*/
public class ReflectTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// Student s=new Student();
// s.study();
// Teacher t=new Teacher();
// t.teach();
/*
* class.txt
* className=xxx
* methodName=xxx
* */
//加载数据
Properties prop=new Properties();
//对象序列化流
FileReader fr=new FileReader("myReflect\\src\\com\\Test02\\class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//通过反射使用
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);//Study
m.invoke(obj);
}
}
关于Properties的介绍:
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
public class PropertiesDemo {
public static void main(String[] args) {
Properties prop=new Properties();
prop.put("Class","Student");
prop.put("Method","study");
prop.put("Method1","play");
Set<Object> KeySet = prop.keySet();
for (Object key: KeySet){
Object value = prop.get(key);
System.out.println(key+","+value);
}
}
}
Properties作为集合的特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key,String value) |
设置集合的键和值,都是String类型,底层调用Hashtable方法put |
String getProperty(String key) |
使用此属性列表中指定的键搜索属性 |
Set<String> stringPropertyNames() |
从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
3. 注解
3.1 注解概述
注解:Annotation
-
Annotation是JDK5.0引入的新技术
-
Annotation作用:
- 不是程序本身,可以对程序做出解释(这一点和comment注释没区别)
- 可以被其他程序(如编译器等)读取
-
Annotation格式:
-
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:
@SuppressWarnings(value="unchecked")
-
-
Annotation用处:
- 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
3.2 内置注解
@Override
:定义在 java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法的声明@Deprecated
:定义在java.lang.Deprecated中,此注解可以用于修饰方法、属性、类,表示不鼓励程序员使用,通常是因为它很危险或者存在更好的选择@SuppressWarnings
:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。- 与前两个注解有所不同,你需要添加一个参数才能正确使用,这些参数都是定义好了的,只需要我们选择性使用
- @SuppressWarnings("all")
- @SuppressWarnings("unchecked")
- @SuppressWarnings(value={"unchecked","deprecation"})
- ...
- 与前两个注解有所不同,你需要添加一个参数才能正确使用,这些参数都是定义好了的,只需要我们选择性使用
3.3 元注解
- 元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
- 这些类型和他们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)
- @Target:用于描述注解的使用范围(即被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- @Document:说明该注解被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
3.4 自定义注解
- 使用
@interface
自定义注解,自动继承了java.lang.annotation.Annotation接口- @interface用来声明一个注释,格式:
public @interface 注解名{定义内容}
- 其中每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为calue
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
- @interface用来声明一个注释,格式:
public class AnnotationDemo {
@myAnnotation(/*name="hello"*/)
public void testMethod(){}
@myAnnotation1("省略value")
public void testMethods(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
//注解的参数:参数类型+参数名()
String name() default "";//如果没有默认值,在使用注解时必须为注解赋值
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在
String[] companies() default {"阿里巴巴","Tencent","字节跳动"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation1{
String value();//如果参数名为value,在赋值时可以不写value=xxx
}
4. Junit单元测试
4.1 测试分类
- 黑盒测试:不需要写代码,给输入值,看程序能否输出期望值
- 白盒测试:需要写代码。关注程序具体的执行流程
4.2 Junit的使用步骤
-
定义一个测试类(测试用例)
- 建议:
- 测试类名:被测试的类后加Test CalculatorTest
- 包名:xxx.xxx.xxx.test com.myJunit.test
- 建议:
-
定义测试方法:可以独立运行
- 建议:
- 方法名:test测试的方法名 testAdd()
- 返回值:void
- 参数列表建议空参
- 建议:
-
给方法加注解@Test
-
导入Junit的依赖环境
package com.test; import com.myJunit.Calculator; import org.junit.Assert; import org.junit.Test; /** * @ClassName CalculatorTest * @Description TODO * @Author Mark * @Date 2022/7/16 23:38 * @Version 1.0 */ public class CalculatorTest { //测试add方法 @Test public void testAdd() { //System.out.println("我被执行了"); Calculator c = new Calculator(); int result = c.add(1, 2); // System.out.println(result); //如果add方法中改为-,虽然运行成功,但是与实际不符,因此在Test中使用断言进行测试 //断言:断言结果为3 Assert.assertEquals(3, result); } //测试sub方法 @Test public void testSub(){ Calculator c = new Calculator(); int result =c.sub(3,2); Assert.assertEquals(1,result); } }
4.3 Junit结果判定
-
红色:失败
-
绿色:成功
-
一般我们使用断言操作来处理结果
Assert.assertEquals(3,result);
4.4 @Before和@After
补充:
- @Before:修饰的方法会在测试方法之前自动被执行
- @After:修饰的方法会在测试方法之后自动被执行
package com.test;
import com.myJunit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @ClassName CalculatorTest
* @Description TODO
* @Author Mark
* @Date 2022/7/16 23:38
* @Version 1.0
*/
public class CalculatorTest {
//初始化方法
//用于资源申请,所有测试方法在执行之前都会执行该方法
@Before
public void init(){
System.out.println("init...");
}
//释放资源方法
//在所有测试方法执行完后,都会自动执行该方法
@After
public void close(){
System.out.println("close...");
}
//测试sub方法
@Test
public void testSub(){
Calculator c = new Calculator();
int result =c.sub(3,2);
System.out.println("testSub...");
Assert.assertEquals(1,result);
}
}
init...
testSub...
close...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)