1.单元测试
-
Junit单元测试框架:可以用来对方法进行测试,它是由Junit公司开源出来的
-
优点:⑴可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
⑵不需要程序员去分析测试的结果,会自动生成测试报告出来。
具体步骤
①将Junit框架的jar包导入到项目中(资料中已经提供)
②为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
③测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;
④开始测试:选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色
- 断言:断言是一种非常重要的功能,它用于验证测试中的一些条件是否为真。如果条件为真,那么测试通过;如果条件为假,那么测试失败。
-
常见断言
assertEquals([String message],expected, actual):验证预期结果与实际结果是否相等。
assertNotEquals([String message], expected, actual):验证预期结果与实际结果是否不相等
-
参数说明:
message(可选):将会在发生错误时报告这个消息。
expected(必填):期望值,通常都是用户指定的内容。
actual(必填):是被测试的代码返回的实际值。
如: Assert.assertEquals("equals","1","1");
-
具体步骤
① 编写测试方法,在测试方法中调用 getMaxIndex 方法②使用Assert.assertEquals进行断言处理
注解 | 说明 |
@Test | 测试类中的方法必须用它修饰才能成为测试方法,才能启动执行 |
@Before | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 |
@AfterClass | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 |
2.反射
●获取Class对象的三种方式
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 1. 直接使用类名.class获取:Class c1 = 类名.class
Class<Cat> catClass = Cat.class;
System.out.println(catClass);
// 2. 调用Class提供的方法:Class c2 = Class.forName("全类名")
Class<?> aClass = Class.forName("com.itheima.b_反射.Cat");
System.out.println(aClass);
// 3. 调用Object提供的方法:Class c3 = 对象.getClass()
Cat cat = new Cat();
Class<? extends Cat> aClass1 = cat.getClass();
System.out.println(aClass1);
System.out.println("==============");
System.out.println(aClass1.getName());
System.out.println(aClass1.getSimpleName());
}
}
-
获取构造器[下面是Class的方法]
Constructor<?>[] getConstructors() 获取所有的公共构造器(只能获取public修饰的)
Constructor<?>[] getDeclaredConstructors() 获取全部构造器(只要存在就能拿到)
Constructor<T> getConstructor(Class<?>... parameterTypes) 获取某个公共构造器(只能获取public修饰的)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取某个构造器(只要存在就能拿到)
-
使用构造器(创建对象)[下面是Constructor的方法]
T newInstance(Object... initArgs) 调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
public void setAccessible(boolean flag) 设置为true,表示禁止检查访问控制(暴力反射) -
注意
使如果想使用private修饰构造器反射创建对象,需要暴力反射(禁止JVM检查构造方法的访问权限)
public class Demo2 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 第一步 获取类Class对象
Class<Cat> catClass = Cat.class;
// Constructor<?>[] getConstructors() 获取所有的公共构造器(只能获取public修饰的)
Constructor<?>[] constructors = catClass.getConstructors();
// System.out.println(constructors.length);
// Constructor<?>[] getDeclaredConstructors() 获取全部构造器(只要存在就能拿到)
Constructor<?>[] declaredConstructors = catClass.getDeclaredConstructors();
// System.out.println(declaredConstructors.length);
// for (Constructor<?> declaredConstructor : declaredConstructors) {
// System.out.println(declaredConstructor.getName());
// System.out.println(declaredConstructor.getParameterCount());
// }
// Constructor<T> getConstructor(Class<?>... parameterTypes) 获取某个公共构造器(只能获取public修饰的)
Constructor<Cat> constructor = catClass.getConstructor();
// System.out.println(constructor.getParameterCount());
Constructor<Cat> constructor1 = catClass.getConstructor(String.class);
// System.out.println(constructor1.getParameterCount());
// Constructor<Cat> constructor2 = catClass.getConstructor(int.class,String.class);
// System.out.println(constructor2.getParameterCount());
// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取某个构造器(只要存在就能拿到)
Constructor<Cat> declaredConstructor = catClass.getDeclaredConstructor(String.class, int.class);
// System.out.println(declaredConstructor.getParameterCount());
// 创建Cat对象,使用构造器对象,公共无参
Cat cat = constructor.newInstance();
System.out.println(cat);
System.out.println("=============");
// 使用私有构造器对象,创建对象
// 暴力反射
declaredConstructor.setAccessible(true);
Cat cat1 = declaredConstructor.newInstance("招财猫", 3);
System.out.println(cat1);
}
}
-
获取成员变量[Class提供]
public Field[] getFields() 获取类的所有公共成员变量(只能获取public修饰的)
public Field[] getDeclaredFields() 获取类的全部成员变量(只要存在就能拿到)
public Field getField(String name) 获取类的某个公共成员变量(只能获取public修饰的)
public Field getDeclaredField(String name) 获取类的某个成员变量(只要存在就能拿到) -
使用成员变量(赋值和取值) [Field提供]
public void set(Object obj, Object value): 赋值
public Object get(Object obj): 取值
public void setAccessible(boolean flag): 设置为true,表示禁止检查访问控制(暴力反射) -
注意
使如果想使用private修饰的变量,需要暴力反射
public class Demo3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 第一步。获取class对象
Class<Cat> catClass = Cat.class;
// public Field[] getFields() 获取类的所有公共成员变量(只能获取public修饰的)
// public Field[] getDeclaredFields() 获取类的全部成员变量(只要存在就能拿到)
Field[] declaredFields = catClass.getDeclaredFields();
// System.out.println("变量个数="+declaredFields.length);
// for (Field declaredField : declaredFields) {
// System.out.println(declaredField.getName()+",类型="+declaredField.getType());
// }
// public Field getField(String name) 获取类的某个公共成员变量(只能获取public修饰的)
// public Field getDeclaredField(String name) 获取类的某个成员变量(只要存在就能拿到)
Field aField = catClass.getDeclaredField("a");
System.out.println(aField.getName()+"-"+aField.getType());
Field nameField = catClass.getDeclaredField("name");
System.out.println(nameField.getName()+"-"+nameField.getType());
Field counField = catClass.getDeclaredField("COUNTRY");
System.out.println(counField.getName()+"-"+counField.getType());
// 创建对象
Cat cat = new Cat();
System.out.println(cat);
int a = (int)aField.get(cat);
System.out.println(a);
String country = (String)counField.get(cat);
System.out.println(country);
// 暴力反射
nameField.setAccessible(true);
String name = (String)nameField.get(cat);
System.out.println(name);
aField.set(cat,100);
int a1= (int)aField.get(cat);
System.out.println(a1);
nameField.set(cat,"小花");
String name1 = (String)nameField.get(cat);
System.out.println(name1);
}
}
-
获取成员方法[Class提供]
Method[] getMethods() 获取类的全部公共成员方法(只能获取public修饰的)
Method[] getDeclaredMethods() 获取类的全部成员方法(只要存在就能拿到)
Method getMethod(String name, Class<?>... parameterTypes) 获取类的某个公共成员方法(只能获取public修饰的)
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取类的某个成员方法(只要存在就能拿到) -
使用成员方法(执行方法)[Method提供]
public Object invoke(Object obj, Object... args) 触发某个对象的该方法执行。
public void setAccessible(boolean flag) 设置为true,表示禁止检查访问控制(暴力反射) -
注意
使如果想使用private修饰的成员方法,需要暴力反射
public class Demo4 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// Method[] getMethods() 获取类的全部公共成员方法(只能获取public修饰的)
// Method[] getDeclaredMethods() 获取类的全部成员方法(只要存在就能拿到)
Class<Cat> catClass = Cat.class;
Method[] declaredMethods = catClass.getDeclaredMethods();
// System.out.println(declaredMethods.length);
// for (Method method : declaredMethods) {
// System.out.println("方法名字;"+method.getName()+",返回类型="+method.getReturnType()+",参数个数="+method.getParameterCount());
// }
// Method getMethod(String name, Class<?>... parameterTypes) 获取类的某个公共成员方法(只能获取public修饰的)
// Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取类的某个成员方法(只要存在就能拿到)
Method eatMethod = catClass.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod.getReturnType());
Class<?>[] parameterTypes = eatMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
Cat cat = new Cat();
// 暴力反射
eatMethod.setAccessible(true);
String str = (String)eatMethod.invoke(cat, "小鱼");
System.out.println(str);
}
}
反射案例
对于任意一个对象,该框架都可以把对象的字段名和对应的值,然后打印在控制台
public class Demo5 {
public static void main(String[] args) throws IllegalAccessException {
//1. 准备两个对象
Student student = new Student("柳岩", 40, '女', 167.5, "女星");
Teacher teacher = new Teacher("播妞", 6000);
//2.调用方法
Demo5.printObj(student);
Demo5.printObj(teacher);
}
/**
* 打印对象变量
* @param obj
* @throws IllegalAccessException
*/
public static void printObj(Object obj) throws IllegalAccessException {
// 1.获取对象的class对象
Class<?> clazz = obj.getClass();
// 获取类的名字
String simpleName = clazz.getSimpleName();
System.out.println("================"+simpleName+"==================");
// 2.获取对象的所有变量
Field[] declaredFields = clazz.getDeclaredFields();
// 3.遍历遍历数组,获取内容
for (Field field : declaredFields) {
// 暴力反射
field.setAccessible(true);
// 4.输出打印内容
System.out.println(field.getName()+"="+field.get(obj));
}
}
}
class Student{
public Student(String name, int age, char sex, double height, String hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
this.hobby = hobby;
}
private String name;
private int age;
private char sex;
private double height;
private String hobby;
}
class Teacher {
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
private String name;
private double salary;
}
3.annotation(注解)
- 就是Java代码里的特殊标记,比如:@Override、@Test等
- 这些标记会被特定的注解解析器所发现,进而决定应该怎样去编译或者执行被注解标记出来的程序
- 元注解:修饰注解的注解,可以指定注解的标注位置和保留阶段
- 如何解析注解:
- 指导思想:要解析谁上面的注解,就应该先拿到谁
- 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解
- 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解
- Class 、 Method 、 Field , Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) //标记作用位置
@Retention(RetentionPolicy.RUNTIME) //存活阶段
public @interface ClassNameCheck {
public String[] author() default "heima";
public String value();
}
public class ClassNameCheckParser {
/**
* 注解的解析方法
*/
public void checkClassName(){
// 创建存放不符合要求的类名的集合
Set<String> nameSet = new HashSet<>();
// 传入包名字,获取包下的所有类对象集合
Set<Class> clazzSet = ClassUtil.getClasses("com.itheima.c_annotation.example");
// 遍历类,判断是否有ClassNameCheck注解
for (Class clazz : clazzSet) {
// 判断类对象上是否有注解
boolean b = clazz.isAnnotationPresent(ClassNameCheck.class);
if (b) {
// 获取类的名字
String className = clazz.getSimpleName();
// 判断类名字是否符合规范,如果不符合,就放入集合
if(!className.startsWith("Heima")){
// 使用反射获取 注解对象
ClassNameCheck cnc = (ClassNameCheck) clazz.
getDeclaredAnnotation(ClassNameCheck.class);
// 使用注解对象获取属性值
String[] author = cnc.author();
String value = cnc.value();
nameSet.add(className+",作者:"+ Arrays.toString(author)+",作用:"+value);
}
}
}
// 检查集合是否有内容,如果有,就报错
if(nameSet.size()>0){
for (String name : nameSet) {
System.out.println("不符合规范的类名:"+name);
}
throw new RuntimeException("类名不符合规范");
}
}
}
public class ClassUtil {
private ClassUtil() {
}
/**
* 获取某个包下的所有类
*/
public static Set<Class> getClasses(String packageName) {
try {
Set<Class> classSet = new HashSet<>();
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
if (protocol.equals("file")) {
String packagePath = url.getPath().replaceAll("%20", " ");
addClass(classSet, packagePath, packageName);
} else if (protocol.equals("jar")) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
if (jarURLConnection != null) {
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
return classSet;
} catch (Exception e) {
throw new RuntimeException("包名错误");
}
}
private static void addClass(Set<Class> classSet, String packagePath, String packageName) {
File[] files = new File(packagePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String className = fileName.substring(0, fileName.lastIndexOf("."));
if (StringUtils.isNotEmpty(packageName)) {
className = packageName + "." + className;
}
doAddClass(classSet, className);
} else {
String subPackagePath = fileName;
if (StringUtils.isNotEmpty(packagePath)) {
subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtils.isNotEmpty(packageName)) {
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet, subPackagePath, subPackageName);
}
}
}
/**
* 加载类
*/
private static Class loadClass(String className, boolean isInitialized) {
Class cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return cls;
}
/**
* 加载类(默认将初始化类)
*/
private static Class loadClass(String className) {
return loadClass(className, true);
}
private static void doAddClass(Set<Class> classSet, String className) {
Class cls = loadClass(className, false);
classSet.add(cls);
}
}
public class HeimaTeacher {
private String name;
private Integer age;
public String sayHello() {
return "hello";
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //生成变量的set get方法
@NoArgsConstructor //无参构造器
@AllArgsConstructor //全参构造器
@ClassNameCheck(author = {"a,b"},value = "学生类")
public class Student {
private String name;
private Integer age;
public String sayHello() {
return "hello";
}
}
public class Test {
public static void main(String[] args) {
// ClassNameCheckParser parser = new ClassNameCheckParser();
// parser.checkClassName();
Student student = new Student();
student.setName("java");
System.out.println(student.getName());
HeimaTeacher heimaTeacher = new HeimaTeacher();
System.out.println("测试结束");
}
}