单元测试&反射&注解
目录
一、单元测试
测试的方法必须是公共的,无参的,无返回值
@Test:测试类中的方法必须用它修饰才能成为测试方法,才能启动执行
@Before:用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次
@After:用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次
@BeforeClass:(必须修饰的是静态方法)用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次
@AfterClass:(必须修饰的是静态方法)用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次
代码如下(示例):
public class StringUtilTest {
@BeforeClass
public static void beforeClassPrint(){
System.out.println("测试类开始");
}
@AfterClass
public static void afterClassPrint(){
System.out.println("测试类结束");
}
@Before
public void beforePrint(){
System.out.println("测试开始");
}
@After
public void afterPrint(){
System.out.println("测试结束");
}
@Test
public void testPrintNumber(){
StringUtil.printNumber("helloworld!");
}
@Test
public void testGetMaxIndex(){
int index = new StringUtil().getMaxIndex("黑马程序员");
Assert.assertEquals("有问题",4,index);
System.out.println(index);
}
}
public class StringUtil {
//获取字符串长度
public static void printNumber(String name) {
System.out.println("名字长度:" + name.length());
}
//获取字符串的最大索引
public int getMaxIndex(String data) {
if (data == null) {
return -1;
}
return data.length()-1;
}
}
二、反射
2.1获取字节码对象
反射的第一步是什么 获取Class类对象,如此才可以解析类的全部成分 获取Class对象的三种方式 1. 直接使用类名.class获取:Class c1 = 类名.class 2. 调用Class提供的方法:Class c2 = Class.forName("全类名") 3. 调用Object提供的方法:Class c3 = 对象.getClass()
代码如下(示例):
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
Class class1 = Cat.class;
Class class2 = Class.forName("com.itheima.b_反射.Cat");
Class class3 = new Cat().getClass();
System.out.println(class1 == class2);
System.out.println(class1 == class3);
}
}
2.2获取类的构造器
获取构造器[下面是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 static void main(String[] args) throws Exception {
Class<Cat> aClass = Cat.class;
Constructor[] constructors1 = aClass.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
System.out.println("-------------------------------");
Constructor[] constructors2 = aClass.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("-------------------------------");
Constructor<Cat> constructor3 = aClass.getConstructor(String.class);
System.out.println(constructor3);
System.out.println("-------------------------------");
Constructor<Cat> constructor4 = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor4);
System.out.println("-------------------------------");
Cat cat = constructor3.newInstance("小花");
System.out.println(cat);
System.out.println("-------------------------------");
constructor4.setAccessible(true);
Cat cat1 = constructor4.newInstance("加菲", 4);
System.out.println(cat1);
}
2.3获取类的成员变量
获取成员变量[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 static void main(String[] args) throws Exception {
Class<Cat> aClass = Cat.class;
Cat cat = new Cat();
Field[] fields1 = aClass.getFields();
for (Field field : fields1) {
System.out.println(field);
}
System.out.println("===========================");
Field[] fields2 = aClass.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
System.out.println("===========================");
Field field1 = aClass.getField("a");
System.out.println(field1);
System.out.println("===========================");
Field field2 = aClass.getDeclaredField("name");
System.out.println(field2);
System.out.println("===========================");
field1.set(cat,10);
System.out.println(cat.a);
System.out.println("===========================");
Field field = aClass.getDeclaredField("age");
field.setAccessible(true);
field.set(cat,20);
System.out.println(cat.getAge());
System.out.println(field.get(cat));
System.out.println("===========================");
field2.setAccessible(true);
field2.set(cat,"小花");
System.out.println(cat.getName());
System.out.println("===========================");
System.out.println(field1.get(cat));
System.out.println(field2.get(cat));
}
2.4获取类的成员方法
获取成员方法[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 static void main(String[] args) throws Exception {
Class<Cat> aClass = Cat.class;
Method[] methods1 = aClass.getMethods();
for (Method method : methods1) {
System.out.println(method.getName()+"---"+method.getReturnType()+"---"+method.getParameterCount());
}
System.out.println("=======================================================================================");
Method[] methods2 = aClass.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method.getName()+"---"+method.getReturnType()+"---"+method.getParameterCount());
}
System.out.println("=======================================================================================");
Method method1 = aClass.getMethod("eat");
System.out.println(method1.getName()+"---"+method1.getReturnType());
System.out.println("=======================================================================================");
Method method2 = aClass.getDeclaredMethod("eat", String.class);
System.out.println(method2.getName()+"---"+method2.getReturnType());
System.out.println("=======================================================================================");
Cat cat = new Cat();
Object invoke = method1.invoke(cat);
System.out.println(invoke);
System.out.println("=======================================================================================");
method2.setAccessible(true);
Object invoke1 = method2.invoke(cat, "shi");
System.out.println(invoke1);
}
2.5反射案例
反射案例 对于任意一个对象,该框架都可以把对象的字段名和对应的值,然后打印在控制台
代码如下(示例):
public class Demo5 {
public static void main(String[] args) throws Exception {
//1. 准备两个对象
Student student = new Student("柳岩", 40, '女', 167.5, "女星");
Teacher teacher = new Teacher("播妞", 6000);
//2.调用方法
print(student);
print(teacher);
}
public static void print(Object obj) throws Exception {
Class aClass = obj.getClass();
System.out.println("=============="+aClass.getSimpleName()+"====================");
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
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.1自定义注解
@interface 注解名{
数据类型 属性名();
数据类型 属性名() default 默认值;
}
注意:
- 注解使用时候,若属性没有设置默认值,我们需要给该属性设置值
- 若使用注解的时候,只有一个属性需要赋值且属性名为value的时候,属性名可以省略
- 使用注解的时候,若某个属性的类型为数组类型,但是赋值时只有一个值,"{}"是可以省略的
3.2元注解
@Target:声明被修饰的注解只能再哪些位置使用
1、TYPE:类,接口
2、FIELD:成员变量
3、METHID:成员方法
4、PARAMETER:方法参数
5、CONSTRUCTOR:构造器
6、LOCAL_VARIABLE:局部变量
@Retention:声明注解的保留阶段
1、SOURCE:只作用在源码阶段,字节码文件中不存在
2、CLASS(默认值):保留到字节码文件阶段,运行阶段不存在
3、RUNTIME(开发常用):一直保留到运行阶段
3.3注解解析
isAnnotationPresent(注解.class)
getAnnotation(注解.class)
通过class、method、field都可以调用
案例:
定位到指定包中所有带@ClassNameCheck注解的类,然后判断类名是否是以Heima开头,如果不是,就抛异常
下面分析一下步骤
①定义一个类叫ClassNameCheckParser,在类中定义一个checkClassName方法,用来完成功能
②获取项目中指定包下的所有类(此功能已经在工具类中提供好了方法,可以直接使用)
③遍历得到每一个类,然后判断类上是否有@ClassNameCheck注解
④如果有注解,则获取到类名进行判断,如果不符合规范,则放到一个准备好的集合中
⑤等到遍历结束,查看集合中是否有元素;有就说明存在不符合规范的类名;打印结果,抛出异常
代码如下(示例):
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ClassNameCheck {
String author() default "小明";
String value();
}
import com.itheima.c_annotation.ClassNameCheck;
@ClassNameCheck(value = "老师类")
public class HeimaTeacher {
private String name;
private Integer age;
public String sayHello() {
return "hello";
}
}
import com.itheima.c_annotation.ClassNameCheck;
@ClassNameCheck(value = "学生类", author = "李四")
public class Student {
//@ClassNameCheck
private String name;
private Integer age;
//@ClassNameCheck
public String sayHello() {
return "hello";
}
}
public class ClassNameCheckParser {
public static void checkClassName(){
Set<Class> classes = ClassUtil.getClasses("com.itheima.c_annotation.example");
for (Class aClass : classes) {
if(aClass.isAnnotationPresent(ClassNameCheck.class)){
if(!aClass.getSimpleName().startsWith("Heima")){
ClassNameCheck annotation =(ClassNameCheck) aClass.getDeclaredAnnotation(ClassNameCheck.class);
System.out.println("类"+aClass.getSimpleName()+"的类名不符合规范,作者:"+annotation.author()+" 作用是:"+annotation.value());
}
}
}
}
}
public class Test {
public static void main(String[] args) {
ClassNameCheckParser.checkClassName();
}
}
总结
以上就是今天学习的内容。