JAVA_TWENTY—ONE_单元测试+注解+反射

发布于:2025-07-31 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

一、单元测试

概念:

Junit单元测试框架

概念:

优点:

JUnit 4注解说明

JUnit 5注解说明

二、反射

认识反射,获取类

反射概念:

反射获取类的信息

反射获取类的三种方法

获取类的结构

获取类的构造器的方法

newInstance

setAccessible

获取成员变量

获取成员方法

反射的作用和应用场景

三、注解

注解

概念:

自定义注解

原理

元注解

概念:

@Target注解

@Retention注解

注解的解析

概念:

解析注解的常用方法

应用场景

动态代理

如何使用java动态代理


一、单元测试

概念:

就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试

Junit单元测试框架

概念:

可以用来对方法进行测试,第三方公司开源出来的(很多开发工具已经集成了Junit单元测试框架,如 Idea)

优点:

1、可以灵活的编写测试代码,可以针对某个测试方法执行测试,也支持一键完成对全部方法的自动化测试,且一键独立

2、不需要程序员去分析测试的结果会自动生成测试报告

JUnit 4注解说明

注解 说明
@Test: 标识一条测试用例。 (A) (expected=XXEception.class)   (B) (timeout=xxx)
@Ignore: 忽略的测试用例。
@Before: 每一个测试方法之前运行。
@After : 每一个测试方法之后运行。
@BefreClass 所有测试开始之前运行。
@AfterClass 所有测试结果之后运行。

JUnit 5注解说明

注解 说明
@Test: 标识一条测试用例。 (A) (expected=XXEception.class)   (B) (timeout=xxx)
@Ignore: 忽略的测试用例。
@BeforeEach: 每一个测试方法之前运行。
@AfteEachr : 每一个测试方法之后运行。
@BefreClassAll 所有测试开始之前运行。
@AfterClassAll 所有测试结果之后运行。
  • Junit框架的简单单元测试

    public class StringUtil {
        //求名字长度
        public static void printString(String name) {
            if(name==null){
                System.out.println("name为空,请输入测试值");
            }else {
                System.out.println("名字长度:" + name.length());
            }
        }
    
        /*
        求字符串的最大索引
        */
        public static int getMaxIndex(String data){
            if(data==null){
                return -1;
            }
            return data.length()-1;
        }
    
    }
    
    
    public class TestStringUtil {
        @BeforeEach //	每一个测试方法之前运行
        public void test1(){
            System.out.println("------test1方法执行了-------");
        }
    
        @BeforeAll //每一个测试方法之后运行
        public static void test3(){
            System.out.println("------test3方法执行了-------");
        }
    
        @AfterEach // 所有测试开始之前运行,只执行一次
        public void test2(){
            System.out.println("------test2方法执行了-------");
        }
        @AfterAll // 所有测试结果之后运行,只执行一次
        public static void test4(){
            System.out.println("------test4方法执行了-------");
        }
    
        @Test //测试方法
        public void testPrintString(){
            StringUtil.printString("13231231");
            StringUtil.printString("");
            StringUtil.printString(null);
        }
    
        @Test  //测试方法
        public void TestGetMaxIndex(){
            int index1=StringUtil.getMaxIndex("abcde");
            System.out.println(index1);
            int index2=StringUtil.getMaxIndex(null);
            System.out.println(index2);
    
            //断言机制,测试员可以通过预测方法的结果
            Assert.assertEquals("方法内部出现Bug",4,index1);
        }
    
    }
    
    //输出
    /*
    ------test3方法执行了-------
    ------test1方法执行了-------
    名字长度:8
    名字长度:0
    name为空,请输入测试值
    ------test2方法执行了-------
    ------test1方法执行了-------
    4
    -1
    ------test2方法执行了-------
    ------test4方法执行了-------
    */
    

二、反射

认识反射,获取类

反射概念:

反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量,方法,构造器)

用于调试器,解释器,对象检查器,类浏览器等应用程序,以及需要访问目标对象的公共成员(基于其运行时类)的对象序列化和JavaBean等服务

反射获取类的信息

1、反射的第一步:加载类,获取类的字节码:Class对象

2、获取类的构造器:Constructor对象

3、获取类的成员变量:Filed对象

4、获取类的成员方法:Method对象

反射获取类的三种方法

方式一: 如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取: String s = “Hello”; Class cls = s.getClass();

方式二: 如果知道一个class的完整类名,可以通过静态方法Class.forName()获取: Class cls = Class.forName(“java.lang.String”);

方式三: 直接通过一个class的静态变量class获取: Class cls = String.class;

获取类的结构

获取类的构造器的方法

Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器

public Constructor<?>[] getConstructors():该方法只能获取当前Class所表示类的public修饰的构造器 public Constructor<?>[] getDeclaredConstructors():获取当前Class所表示类的所有的构造器,和访问权限无关

public Constructor<T> getConstructor(Class<?>... parameterTypes) :获取当前Class所表示类中指定的一个public的构造器

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) :获取当前Class所表示类中指定的一个的构造器

newInstance

创建此对象表示的类的新实例。 该类实例化为new带有空参数列表的表达式。 如果尚未初始化,则初始化该类。

setAccessible

方法:public  void setAccessible (AccessibleObject[] array, boolean flag) 禁止检查访问权限(暴力反射)

  • 获取类的构造器的方法

    public class Cat {
        private String name;
        private String age;
    
        private Cat() {
        }
    
        public Cat(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\\'' +
                    ", age='" + age + '\\'' +
                    '}';
        }
    }
    
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.params.shadow.com.univocity.parsers.common.processor.ObjectColumnProcessor;
    
    import java.lang.reflect.Constructor;
    
    /*
    * 目标:获取类的构造器,并对其进行操作
    * */
    public class TestClass {
    
        //获取构造器
        @Test  //测试方法
        public void textGetConstructors(){
         //1.反射第一步,必须先得到这个类的Class对象
    //        Class c=new Cat().getClass();
             Class c= Cat.class;
            //2.获取类的全部构造器,只能获取public构造器
    //        Constructor[] constructors=c.getConstructors();
            //可以获取全部的构造器
            Constructor[] constructors=c.getDeclaredConstructors();
            //3.遍历数组中的每个构造器对象
            for (Constructor constructor : constructors) {
                System.out.println(constructor.getName() + "----->"
                        + constructor.getParameterCount());
            }
        }
        
        //对其进行操作
        @Test
        public void testGetConstructor() throws Exception {
            //1.反射第一步,必须先得到这个类的Class对象
            Class c= Cat.class;
            //2.获取某个构造器
    //        Constructor constructor=c.getConstructor();
            //可以不管修饰符权限,获取构造器
            Constructor constructor=c.getDeclaredConstructor();
            constructor.setAccessible(true); //禁止检查访问权限
            Cat o1 = (Cat) constructor.newInstance();
            System.out.println(o1);
            //3.获取有参构造器
            Constructor constructor1 =
                    c.getDeclaredConstructor(String.class, String.class);
            System.out.println(constructor1.getName() + "----->"
                    + constructor1.getParameterCount());
    
            //获取对象
            Cat o = (Cat) constructor1.newInstance("小多啦","12"); //进行强转
            System.out.println(o);
    
        }
    
    }
    
    //输出
    /*
    Cat{name='null', age='null'}
    org.example.reflect.Cat----->2
    Cat{name='小多啦', age='12'}
    org.example.reflect.Cat----->0
    org.example.reflect.Cat----->2
    */
    

获取成员变量

  • 获取成员变量

    public class Cat {
        private String name;
        private String age;
    
       public Cat() {
        }
    
        public Cat(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        public void run(){
            System.out.println("小猫在跑~");
        }
        public void eat(String food){
            System.out.println("小猫在吃"+food);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\\'' +
                    ", age='" + age + '\\'' +
                    '}';
        }
    }
    
    
    import org.example.homework.Cat;
    import org.junit.jupiter.api.Test;
    
    import java.lang.reflect.Field;
    
    public class TestFiled {
        @Test
        public void testGetFields() throws NoSuchFieldException, IllegalAccessException {
            //1.反射第一步,必须得到类的Class对象
            Class cat = Cat.class;
            //2.获取类的全部成员变量
            Field[] fields = cat.getDeclaredFields();
            //3.遍历这个成员变量数组
            for (Field field : fields) {
                System.out.println(field.getName());
            }
    
            //定位某个成员变量
            Field name = cat.getDeclaredField("name");
            System.out.println(name.getName()+"------->"+name.getType().getName());
    
            //赋值
            Cat cat1=new Cat();
            name.setAccessible(true);  //禁止访问控制权限
            name.set(cat1,"猫0");
    
            //取值
            String n =(String)name.get(cat1);
            System.out.println(n);
    
        }
    
    }
    
    //输出
    /*
    name
    age
    name------->java.lang.String
    猫0
    */
    

获取成员方法

  • 获取成员方法

    public class Cat {
        private String name;
        private String age;
    
       public Cat() {
        }
    
        public Cat(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        public void run(){
            System.out.println("小猫在跑~");
        }
        public void eat(String food){
            System.out.println("小猫在吃"+food);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\\'' +
                    ", age='" + age + '\\'' +
                    '}';
        }
    }
    
    
    import org.example.homework.Cat;
    import org.junit.jupiter.api.Test;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class TestMethod {
        @Test
        public void testGetMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //反射第一步,获得类的对象
            Class cat = Cat.class;
    
            //获取全部的方法
            Method[] methods = cat.getDeclaredMethods();
    
            //开始遍历方法
            for (Method method : methods) {
                System.out.println(method.getName()+" "
                +method.getParameterCount()+" "
                +method.getReturnType());
            }
            //获取指定的方法
            Method run = cat.getDeclaredMethod("run");
            System.out.println(run.getName());
    
            Method eat = cat.getDeclaredMethod("eat", String.class);
            System.out.println(eat.getName());
    
            //创建class对象
            Cat cat1=new Cat();
    
            run.setAccessible(true);
            run.invoke(cat1); //调用无参方法
    
            eat.invoke(cat1,"猫粮"); //调用有参方法
    
        }
    }
    
    //输出
    /*
    getName 0 class java.lang.String
    run 0 void
    toString 0 class java.lang.String
    setName 1 void
    eat 1 void
    setAge 1 void
    getAge 0 class java.lang.String
    run
    eat
    小猫在跑~
    小猫在吃猫粮
    */
    

反射的作用和应用场景

作用:

可以得到一个类的全部成分然后进行操作

可以破坏封装性

最重要的功能:适合做Java的框架,基本上,主流的框架都会基于发射设计出一些通用的功能

  • 框架的简单应用

    public class Cat {
        private String name;
        private String age;
    
       public Cat() {
        }
    
        public Cat(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        public void run(){
            System.out.println("小猫在跑~");
        }
        public void eat(String food){
            System.out.println("小猫在吃"+food);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\\'' +
                    ", age='" + age + '\\'' +
                    '}';
        }
    }
    
    
    public class Dog {
        private String name;
        private int age;
    
        public Dog() {
        }
    
        public Dog(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    
    import java.io.FileOutputStream;
    import java.io.PrintStream;
    import java.lang.reflect.Field;
    
    public class ObjectFrame {
    
        public static void saveObject(Object object) throws Exception {
            //获取打印流
            PrintStream Ps = new PrintStream(new FileOutputStream("opp_Junit/src/test/java/org/example/homework/writer.txt",
                    true), true);
            //获取Object的Class的对象
            Class aClass = object.getClass();
            //获取类名,并打印
            Ps.println("------------"+aClass.getSimpleName()+"------------");
            //获取全部实例变量
            Field[] Fields = aClass.getDeclaredFields();
    
            //开始遍历实例变量
            for (Field field : Fields) {
                field.setAccessible(true);  //禁止访问权限
                String name = field.getName();
                System.out.println(name);
                Object value = field.get(object);
                System.out.println(value);
    
                //打印name+value
                Ps.println(name+":"+value);
    
            }
            Ps.close();
        }
    }
    
    
    public class Test {
        public static void main(String[] args) throws Exception{
            Cat cat = new Cat("咪咪", "1");
            Dog dog = new Dog("旺财", 3);
    
            //调用框架
            ObjectFrame.saveObject(cat);
            ObjectFrame.saveObject(dog);
    
        }
    }
    
    

    writer.txt

    • -----------Cat------------
    • name:咪咪
    • age:1
    • -----------Dog------------
    • name:旺财
    • age:3

三、注解

注解

概念:

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

自定义注解

public @interface 注解名称{

public 属性类型 属性名() default 默认值;

}

原理

本质上是一个接口,Java中的所有注解都是继承了Annotation接口的

元注解

概念:

指的是:修饰注解的注解

@Target注解

声明注解属性

public enum ElementType {
TYPE, // 类、接口、枚举类

FIELD, // 成员变量(包括:枚举常量)

METHOD, // 成员方法

PARAMETER, // 方法参数

CONSTRUCTOR, // 构造方法

LOCAL_VARIABLE, // 局部变量

ANNOTATION_TYPE, // 注解类

PACKAGE, // 可用于修饰:包

TYPE_PARAMETER, // 类型参数,JDK 1.8 新增

TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}

@Retention注解

声明注解保留周期

public enum RetentionPolicy {
SOURCE,    // 源文件保留
CLASS,       // 编译期保留,默认值
RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

注解的解析

概念:

就是判断类上、方法上、成员变量上是否存在注解,并把注解的内容解析出来

解析注解的常用方法

通过反射来获得注解,先得到class对象
Class cls = Student. Class;

方法一: Annotation[] annotations = cls.getAnnotations()
作用: 获取所有注解

方法二: Annotation annotation = cls.getAnnotation(MyAnnotation.class)
作用:获MyAnnotation注解,类型是Annotation

方法三: Field field = cls.getDeclaredField(“name”)
annotations = field.getAnnotations()
作用: 获取属性上的注解

方法四:annotation = field.getAnnotation(MyAnnotation.class)
作用:获取属性上的MyAnnotation注解
annotation = field.getAnnotation(MyAnnotation.class);

方法五: Method method = cls.getDeclaredMethod(“show”, int.class);
annotations = method.getAnnotations()
作用:获取方法上的注解

方法六:annotation = method.getAnnotation(MyAnnotation.class)
作用:获取方法上的MyAnnotation注解

方法七:annotation instanceof MyAnnotation
作用:判断annotation是否是MyAnnotation类型

方法八:MyAnnotation ma=(MyAnnotation) annotation
System.out.println(ma.d());
System.out.println(ma.value());
作用: 获取注解上的属性值

方法九:method.isAnnotationPresent(SuppressWarnings.class)
作用: 判断method是否使用了指定的SuppressWarnings注解

应用场景

需求:

定义若干个方法,只要加了MyTest注解,就会触发该方法执行

  • 需求

    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    /*
    定义一个注解
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyTest{
    
    }
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /*
     * 模拟Junit框架的设计
     * */
    
    public class AnnotationTest {
        @MyTest
        public void test1(){
            System.out.println("test1执行了");
        }
    
    //    @MyTest
        public void test2(){
            System.out.println("test2执行了");
        }
        @MyTest
        public void test3(){
            System.out.println("test3执行了");
        }
    //    @MyTest
        public void test4(){
            System.out.println("test4执行了");
        }
    
        public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
            AnnotationTest  a=new AnnotationTest();
            //获取Class对象
            Class at = AnnotationTest.class;
    
            //获取全部方法
            Method[] methods = at.getDeclaredMethods();
            //遍历方法
            for (Method method : methods) {
                //判断方法是否存在MyTest注解
                if(method.isAnnotationPresent(MyTest.class)){
                    method.invoke(a);  //方法执行
                }
            }
        }
    }
    

动态代理

如何使用java动态代理

创建java动态代理需要使用如下类

java.lang.reflect.Proxy

调用其newProxyInstance方法,例如我们需要为Map创建一个代理:

Map mapProxy = (Map) Proxy.newProxyInstance(
        HashMap.class.getClassLoader(),
        new Class[]{Map.class},
        new InvocationHandler(){...}
);

我们接着就来分析这个方法。先查看其签名:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

ClassLoader类型的loader:被代理的类的加载器,可以认为对应4要素中的被代理的对象

Class数组的interfaces:被代理的接口,这里其实对应的就是4要素中的被代理的行为,可以注意到,这里需要传入的是接口而不是某个具体的类,因此表示行为。

InvocationHandler接口的h:代理的具体行为,对应的是4要素中的行为的完全控制,当然也是java动态代理的核心。

最后返回的对象Object对应的是4要素中的代理对象

接着我们来示例用java动态代理来完成记录方法执行时间戳的需求:

首先定义被代理的行为,即接口:

public interface ExecutorInterface {
    void execute(int x, int y);
}

接着定义被代理的对象,即实现了接口的类:

public class Executor implements ExecutorInterface {
    public void execute(int x, int y) {
        if (x == 3) {
            return;
        }
        for (int i = 0; i < 100; i++) {
            if (y == 5) {
                return;
            }
        }
        return;
    }
}

接着是代理的核心,即行为的控制,需要一个实现了InvocationHandler接口的类:

public class TimeLogHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

这个接口中的方法并不复杂,我们还是先分析其签名

Object类型的proxy:最终生成的代理对象

Method类型的method:被代理的方法。这里其实是2个要素的复合,即被代理的对象是如何执行被代理的行为的。因为虽然我们说要对行为完全控制,但大部分时候,我们只是对行为增添一些额外的功能,因此依然是要利用被代理对象原先的执行过程的。

Object数组的args:方法执行的参数

因为我们的目的是要记录方法的执行的时间戳,并且原方法本身还是依然要执行的,所以在TimeLogHandler的构造函数中,将一个原始对象传入,method在调用invoke方法时即可使用。

定义代理的行为如下:

public class TimeLogHandler implements InvocationHandler {
    private Object target;

    public TimeLogHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("start:{}", System.nanoTime());
        Object result = method.invoke(target, args);
        log.info("end:{}", System.nanoTime());
        return result;
    }
}

接着我们来看Invoker如何使用代理,这里为了方便演示我们是在构造函数中实例化代理对象,在实际使用时可以采用依赖注入或者单例等方式来实例化:

public class Invoker {
    private ExecutorInterface executor;

    public Invoker() {
        executor = (ExecutorInterface) Proxy.newProxyInstance(
                Executor.class.getClassLoader(),
                new Class[]{ExecutorInterface.class},
                new TimeLogHandler(new Executor())
        );
    }

    public void invoke() {
        executor.execute(1, 2);
    }
}

此时如果Exector新增了任何方法,那么Invoker和TimeLogHandler将不需要任何改动就可以支持新增方法的的时间戳记录,有兴趣的同学可以自己尝试一下。

另外如果有其他类也需要用到时间戳的记录,那么只需要和Executor一样,通过Proxy.newProxyInstance方法创建即可,而不需要其他的改动了。


网站公告

今日签到

点亮在社区的每一天
去签到