单元测试&反射&注解

1.单元测试
就是针对最小的功能单元 ( 方法 ) ,编写测试代码 对其 进行正确性测试。
  • Junit单元测试框架:可以用来对方法进行测试,它是由Junit公司开源出来的

  •         优点:⑴可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。

⑵不需要程序员去分析测试的结果,会自动生成测试报告出来。

具体步骤

Junit框架的jar包导入到项目中(资料中已经提供)

为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值

测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;

开始测试:选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色

  1. 断言:断言是一种非常重要的功能,它用于验证测试中的一些条件是否为真。如果条件为真,那么测试通过;如果条件为假,那么测试失败。
  2. 常见断言

    assertEquals([String message],expected, actual):验证预期结果与实际结果是否相等。

    assertNotEquals([String message], expected, actual):验证预期结果与实际结果是否不相等

  3.  参数说明:

    message(可选):将会在发生错误时报告这个消息。

    expected(必填):期望值,通常都是用户指定的内容。

    actual(必填):是被测试的代码返回的实际值。

    如: Assert.assertEquals("equals","1","1");

  4. 具体步骤

    编写测试方法,在测试方法中调用 getMaxIndex 方法

    使用Assert.assertEquals进行断言处理

注解

说明

@Test

测试类中的方法必须用它修饰才能成为测试方法,才能启动执行

@Before

用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。

@After

用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。

@BeforeClass

用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。

@AfterClass

用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次

 

2.反射

●获取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 {
//        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
  • 这些标记会被特定的注解解析器所发现,进而决定应该怎样去编译或者执行被注解标记出来的程序
  • 元注解:修饰注解的注解,可以指定注解的标注位置和保留阶段
  • 如何解析注解:
  1. 指导思想:要解析谁上面的注解,就应该先拿到谁
  2. 比如要解析类上面的注解,则应该先获取类的Class对象,再通过Class对象解析其上面的注解
  3. 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解
  4. 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("测试结束");



    }
}


 4.lombok

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值