当前位置: 首页 > news >正文

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

目录

一、单元测试

概念:

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;}@Overridepublic 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());}}//对其进行操作@Testpublic 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;}@Overridepublic 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 {@Testpublic 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;}@Overridepublic 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 {@Testpublic 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;}@Overridepublic 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+valuePs.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 {@MyTestpublic void test1(){System.out.println("test1执行了");}//    @MyTestpublic void test2(){System.out.println("test2执行了");}@MyTestpublic void test3(){System.out.println("test3执行了");}
    //    @MyTestpublic 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 {@Overridepublic 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;}@Overridepublic 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方法创建即可,而不需要其他的改动了。

http://www.lryc.cn/news/604125.html

相关文章:

  • 在 Cloudflare 平台上完整部署 GitHub 项目 MoonTV 实现免费追剧流程
  • vite + chalk打印输出彩色命令行
  • 并查集介绍及典型应用和编程题
  • Python爬虫01_Requests第一血获取响应数据
  • __getattr__和 __getattribute__ 的用法
  • Docker学习相关视频笔记(二)
  • linux内核报错汇编分析
  • 云原生周刊:2025年的服务网格
  • JSON-RPC 2.0 规范
  • fastjson反序列化时_id的处理
  • WebRTC 2025全解析:从技术原理到商业落地
  • MC0241防火墙
  • 16大工程项目管理系统对比:开源与付费版本
  • 牛客网之华为机试题:密码验证程序
  • python-网络编程
  • Qt 移动应用性能优化策略
  • 板凳-------Mysql cookbook学习 (十二--------7)
  • Android User版本默认用test-keys,如何改用release-keys
  • 北方公司面试记录
  • 前端数据库:IndexedDB从基础到高级使用指南
  • 基于Prophet、滑动平均、加权平均的地铁客流量预测与可视化系统的设计与实现
  • Java【代码 17】httpclient PoolingHttpClientConnectionManager 连接池使用举例
  • 无穿戴动作捕捉技术:驱动历史活化、乐园叙事与教育沉浸的文旅利器
  • [Linux入门] Linux 部署本地 APT 仓库及 NFS 共享服务全攻略
  • 算法精讲:二分查找(一)—— 基础原理与实现
  • 7.28学习日志
  • ICT模拟零件测试方法--晶体管测试
  • 智能Agent场景实战指南 Day 23 : Agent安全与隐私保护
  • k8s搭建nfs共享存储
  • Ubuntu20.04安装和配置Samba实现Win11下共享文件夹