Java:Junit、反射、注解

xiaoxiao2021-03-01  6

1、Junit单元测试-Junit概述

1)、单元测试:在开发中编写一个功能,而这个功能需要启动系统,并经过一系列的操作才能达到这个功能,这样为了测试这个功能就带来了麻烦。“单元测试”是指将“一部分”代码进行单独测试。可以大大提高程序测试的效率

2)、JUnit是第三方公司实现的一个“单元测试”的工具包,是基于“注解”的。

2、Junit单元测试-Junit的使用

编写代码后,在需要单元测试的方法之上加上@Test

3、Junit单元测试-@Test注解

1)、@Test注解:Junit中用于测试方法的注解,可以在想要测试的方法上添加此注解

2)、在意个类中可以定义多个使用@Test注解的测试方法,每个方法可以独立运行,也可以一起运行(按照方法名升序排列运行)

public class Student {//右键-->Run Student @Test public void show2(){//右键-->Run show2 System.out.println("show2"); } @Test public void show3(){ System.out.println("show3"); } @Test public void show1(){ System.out.println("show1"); } }

注意:测试方法必须是公有、无参、无返回值、非静态的方法。

4、Junit单元测试-其他注解

1)、@Before:在@Test方法之前运行,可以定义多个@Before方法,在每个@Test方法运行前,所有@Before都会以方法名倒叙运行

2)、@After:在@Test方法之后运行,可以定义多个@After方法,在每个@Test方法运行之后,所有@After都会以方法名升序运行

3)、@BeforeClass:用于测试静态方法。在所有的注解之前只运行一次

4)、@AfterClass:用于测试静态方法。在所有的注解之后只运行一次

public class Student { @BeforeClass public static void bc(){ System.out.println("before class...."); } //-----------------------------------------------// @Before public void b1(){ System.out.println("b1"); } @Before public void b2(){ System.out.println("b2"); } @Before public void b3(){ System.out.println("b3"); } //-----------------------------------------------// @Test public void show2(){ System.out.println("show2"); } @Test public void show3(){ System.out.println("show3"); } @Test public void show1(){ System.out.println("show1"); } //-----------------------------------------------// @After public void a1(){ System.out.println("a1"); } @After public void a2(){ System.out.println("a2"); } @After public void a3(){ System.out.println("a3"); } //-----------------------------------------------// @AfterClass public static void ac(){ System.out.println("after class...."); } }

5、反射-反射的概念及演示

反射:“反向加载”要使用的“类”,之前使用某个类都是直接创建对象(存在依赖,存在依赖就会有问题存在,被依赖的类出现修改,那么依赖者就也要更改自己的代码)

class Demo{ public static void main(String[] args){ Student stu = new Student();//正向加载 } } //使Demo类产生了对Student类的依赖,如果Student类发生修改,Demo类就需要修改。

反射就可以很好的解决这个问题

例:(模拟游戏更新地图演示)

//旧版本地图:(当需要更新地图时就要重新创建一个地图类,那么测试类中也必须要对地图类进行更改,很麻烦) public class Game { public void paoTu(){ System.out.println("第一个版本的跑图...."); } } //新版本的地图 public class Game2 { public void paoTu2(){ System.out.println("第二个版本的跑图...."); } }

测试类:(使用反射,脱离依赖)

public class Demo { public static void main(String[] args) throws Exception { /*Game2 g = new Game2();//更新一次地图就要在这里修改一次,麻烦 g.paoTu2();*/ Class c = Class.forName(getValue("className")); Object o = c.getConstructor().newInstance(); Method m = c.getDeclaredMethod(getValue("methodName")); m.invoke(o); } //编写方法,根据某个键,读取它对应的值 public static String getValue(String key) { Properties pro = new Properties(); try (FileReader in = new FileReader("game.txt")) { pro.load(in); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return pro.getProperty(key); } }

在项目根目录下创建game.txt文件,内容如下:

className = cn.itheima.demo03_反射的概念及演示.Game2 methodName = paoTu2

这样就使用反射避免了测试类对地图类的依赖,当有新的地图进行修改时,只需要对game.txt文件内容进行修改即可

6、反射-运行时class文件的加载

注意:JVM在第一次使用任何类时,都会在内存中创建一个这个类的Class对象,而针对每个类,都只有一个Class对象

反射工作机制:

7、反射-获取Class对象的对象的三种方式

1)、getClass()方法-Object类中定义的;

2)、任何数据类型(基本类型、引用类型)都有一个静态属性:class

3)、Class类的静态方法:forName(全名限定的类名);比较常用

注意:以上三种获取的方式都是先判断此class是否存在,不存在就创建

代码:

public static void main(String[] args) throws ClassNotFoundException { //1.getClass() Student stu = new Student(); Class c1 = stu.getClass(); //2.任何的"数据类型(基本类型,引用类型",都有一个静态的 属性:class) Class c2 = Student.class; //3.使用Class的静态方法:forName(全名限定的类名) Class c3 = Class.forName("cn.itheima.demo05_获取Class对象的三种方式.Student"); System.out.println(c1 == c2);//true System.out.println(c1 == c3);//true }

Class对象方法拓展:

String getSimpleName(); //获得简单类名,只是类名,没有包 String getName(); //获取完整类名,包含包名+类名 T newInstance() ;//创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法

8、反射-获取构造方法并创建对象

Constructor getConstructor(Class... parameterTypes) 根据参数类型获取构造方法对象,只能获得public修饰的构造方法。 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。

Constructor getDeclaredConstructor(Class... parameterTypes) 根据参数类型获取构造方法对象,包括private修饰的构造方法。 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。

Constructor[] getConstructors() 获取所有的public修饰的构造方法

Constructor[] getDeclaredConstructors() 获取所有构造方法,包括privat修饰的(公有、受保护、默认、私有)

示例代码:

public static void main(String[] args) throws Exception { //1.获取Student的Class对象 Class c = Class.forName( "cn.itheima.demo06_反射_获取构造方法并创建对象.Student"); //2.获取所有构造方法 Constructor[] cArray = c.getDeclaredConstructors(); for (Constructor con : cArray) { System.out.println(con); } //3.获取某个构造方法: System.out.println("获取公有、无参的构造方法:"); Constructor con = c.getDeclaredConstructor(); //4.创建对象 Object obj = con.newInstance();//相当于:Object obj = new Student(); System.out.println("获取私有、double和String参数的构造方法:"); Constructor con2 = c.getDeclaredConstructor(double.class, String.class); //由于是私有的构造方法,所以要先设置暴力访问 con2.setAccessible(true); Object obj2 = con2.newInstance(3.14, "张学友"); }

Constructor类中常用方法:

1. T newInstance(Object... initargs);//根据指定参数创建对象。 2. void setAccessible(true);//暴力反射,设置为可以直接访问私有类型的构造方法。

9、反射-获取成员属性并赋值和取值

Field是属性类,类中的每一个属性(成员变量)都是Field的对象,通过Field对象可以给对应的成员变量赋值和取值。

Field getDeclaredField(String name) 根据属性名获得属性对象,包括private修饰的

Field getField(String name) 根据属性名获得属性对象,只能获取public修饰的

Field[] getFields() 获取所有的public修饰的属性对象,返回数组。

Field[] getDeclaredFields() 获取所有的属性对象,包括private修饰的,返回数组。

示例代码:

1、Student类:

public class Student { public String name; protected int age; String sex; private String address; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }

2、测试类:

public static void main(String[] args) throws Exception { //1.获取Class Class c = Class.forName( "cn.itheima.demo07_反射_获取成员属性并赋值和取值.Student"); //创建两个Student对象 Object obj1 = c.getDeclaredConstructor().newInstance(); Object obj2 = c.getDeclaredConstructor().newInstance(); //2.获取所有属性 Field[] fields = c.getDeclaredFields(); for (Field f : fields) { System.out.println(f); } System.out.println("------------------------------"); //3.获取某个属性 System.out.println("获取name属性:"); Field f = c.getDeclaredField("name"); System.out.println("获取address属性:"); Field f2 = c.getDeclaredField("address"); System.out.println("为第一个Student对象的name赋值为:刘德华,address赋值为:香港:"); f.set(obj1, "刘德华"); //私有的要先设置暴力访问 f2.setAccessible(true); f2.set(obj1,"香港"); System.out.println("为第二个Student对象的name赋值为:章子怡,address赋值为:北京:"); f.set(obj2, "章子怡"); //设置暴力访问 f2.setAccessible(true); f2.set(obj2,"北京"); System.out.println(obj1); System.out.println(obj2); }

10、反射-获取成员方法并调用

Method getMethod("方法名", 方法的参数类型... 类型) 根据方法名和参数类型获得一个方法对象,只能是获取public修饰的

Method getDeclaredMethod("方法名", 方法的参数类型... 类型) 根据方法名和参数类型获得一个方法对象,包括private修饰的

Method[] getMethods() (了解) 获取所有的public修饰的成员方法,包括父类中。(成员方法)

Method[] getDeclaredMethods() (了解) 获取当前类中所有的方法,包含私有的,不包括父类中。(成员方法)

示例代码:

1、Student类:

public class Student { public void show1(){ System.out.println("公有、无参、无返回值的show1()..."); } private int show2(String a, int b) { System.out.println("私有、有参,有返回值的show2()..."); return 20; } }

2、测试类:

public static void main(String[] args) throws Exception { //1.先获取Class对象 Class c = Class.forName( "cn.itheima.demo08_反射_获取成员方法并调用.Student"); //2.创建对象 Object obj = c.getDeclaredConstructor().newInstance(); //3.获取所有方法 Method[] methods = c.getDeclaredMethods(); for (Method m :methods) { System.out.println(m); } System.out.println("获取公有、无参、无返回值的show1()"); Method m = c.getDeclaredMethod("show1"); //调用方法--必须要先创建对象 m.invoke(obj); System.out.println("获取私有、有参、有返回值的show2()"); Method m2 = c.getDeclaredMethod("show2", String.class, int.class); //调用方法 m2.setAccessible(true); Object result = m2.invoke(obj, "刘德华", 20); System.out.println("返回值:" + result); }

Method类中常用方法:

1. Object invoke(Object obj, Object... args);//根据参数args调用对象obj的该成员方法,如果obj=null,则表示该方法是静态方法 2. void setAccessible(boolean flag);//暴力反射,设置为可以直接调用私有修饰的成员方法

11、注解-注解的概念及作用

1)、在代码中我们曾看到@Override,@FunctionalInterface就是“注解”。

2)、作用:用在“源码”中,做“标记”的。用来告诉“编译器”怎样编译“下面的代码”。也可以像JUnit的@Test注解一样,单独启动一个“注解解析器”,读取注解,并做一些事情。

注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种 标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。

12、注解-JDK内部注解

1)、@Override(重写)

class Student{ @Override public String toString(String s){ return "..."; } }

2)、@FunctionalInterface(定义函数式接口)

@FunctionalInterface interface IA{ //必须要有,且只有一个抽象方法 public void show(); }

13、注解-自定义注解的基本格式

1)、自定义注解格式:

public @interface 注解名{}

2)、给自定义注解添加属性

格式1:数据类型 属性名();格式2:数据类型 属性名() default 默认值; public @interface Student { String name(); // 姓名 int age() default 18; // 年龄 String gender() default "男"; // 性别 } // 该注解就有了三个属性:name,age,gender

例:

public @interface Book { // 书名 String value(); // 价格 double price() default 100; // 多位作者 String[] authors(); }

编写“注解解析器”,就可以解析注解,然后进行使用

14、注解-元注解

1)、“元注解”也是一种注解,但是只能用在定义注解上,他规定了定义的注解可以用在什么位置(约束自定义注解使用的位置)

2)、元注解分为俩类:

@Target:规定注解可以用在哪些位置:(可以选择的值被定义在:java.lang.annotation.ElementType枚举中。 )

@Target(ElementType.TYPE)//可以用在类,接口中 @Target(ElementType.FIELD)//可以用在字段上(成员属性) @Target(ElementType.METHOD)//可以用在方法上 @Target(ElementType.PARAMETER)//可以用在参数上 @Target(ElementType.CONSTRUCTOR)//可以用在构造方法是哪个 @Target(ElementType.LOCAL_VARIABLE)//用在局部变量上.

@Retention:规定注解会存在于哪里:(可以选择的值被定义在:java.lang.annotaion.RetentionPolicy枚举中。 )

@Retention(RetentionPolicy.SOURCE)//注解只存在于“源码中”,给编译器看的。例如:@Override,@FunctionalInterface @Retention(RetentionPolicy.CLASS)//注解会存在于源码、class中, 给编译器看的。当JVM运行此类时,不会将注解信息读取到内存。 @Retention(RetentionPolicy.RUNTIME)//注解会存在于源码、class、内存中给运行时的“注解解析器”看的。像JUnit的@Test

例:

将注解定义为只能用在方法上,而且要能够进入到内存:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno {}

15、注解-解析注解

Anontation:所有注解类型的公共接口,类似所有类的父类是Object。

AnnotatedElement:定义了与注解解析相关的方法,常用方法以下四个:

boolean isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注解,有则返回true,否则返回false。 T getAnnotation(Class<T> annotationClass); 获得当前对象上指定的注解对象。 Annotation[] getAnnotations(); 获得当前对象及其从父类上继承的所有的注解对象。 Annotation[] getDeclaredAnnotations();获得当前对象上所有的注解对象,不包括父类的。

获取注解数据:

注解作用在那个成员上,就通过反射获得该成员的对象来得到它的注解。

如注解作用在方法上,就通过方法(Method)对象得到它的注解

// 得到方法对象 Method method = clazz.getDeclaredMethod("方法名"); // 根据注解名得到方法上的注解对象 Book book = method.getAnnotation(Book.class);

如注解作用在类上,就通过Class对象得到它的注解

// 获得Class对象 Class c = 类名.class; // 根据注解的Class获得使用在类上的注解对象 Book book = c.getAnnotation(Book.class);

定义注解:

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno {}

使用注解的Student类的定义:

public class Student { @MyAnno public void show3(){ System.out.println("show3()..."); } @MyAnno public void show1(){ System.out.println("show1()..."); } public void show2(){ System.out.println("show2()..."); } }

注解解析器实现:

public class Demo { public static void main(String[] args) throws Exception { //以下用于实现一个"注解解析器" //1.反射获取使用了注解的那个类的Class对象 Class stuClass = Class.forName("cn.itheima.demo10_解析注解.Student"); //2.创建对象 Object obj = stuClass.getDeclaredConstructor().newInstance(); //3.获取它内部的所有的成员方法 Method[] methods = stuClass.getDeclaredMethods(); //4.遍历每个方法 for (Method m : methods) { //5.判断此方法上是否使用的MyAnno注解 if (m.isAnnotationPresent(MyAnno.class)) { //6.执行此方法 m.invoke(obj); } } } }

16、注解-定义内部属性的注解

1)、“注解”中可以定义属性,“注解解析器”根据属性的不同,可以更详细的区分不同的情况,而做不同的事情。

2)、我们先看看怎样定义属性:

格式:数据类型 属性名() [default 值];

对注解进行修改,添加属性:

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { int zIndex(); }

Student类中做出修改:

public class Student { @MyAnno(zIndex = 3) public void show3(){ System.out.println("show3()..."); } @MyAnno(zIndex = 1) public void show1(){ System.out.println("show1()..."); } public void show2(){ System.out.println("show2()..."); } }

注解解析器做出修改:

public class Demo { public static void main(String[] args) throws Exception { //以下用于实现一个"注解解析器" //1.反射获取使用了注解的那个类的Class对象 Class stuClass = Class.forName("cn.itheima.demo10_解析注解.Student"); //2.创建对象 Object obj = stuClass.getDeclaredConstructor().newInstance(); //3.获取它内部的所有的成员方法 Method[] methods = stuClass.getDeclaredMethods(); //4.遍历每个方法 Map<Integer,Method> map = new HashMap<>(); for (Method m : methods) { //5.判断此方法上是否使用的MyAnno注解 if (m.isAnnotationPresent(MyAnno.class)) { //6.获取MyAnno注解对象 MyAnno anno = m.getAnnotation(MyAnno.class); //7.获取里面的zIndex属性的值 int index = anno.zIndex(); //8.存储到map中 map.put(index, m); } } System.out.println(map); //9.遍历map,依次的执行每个方法,按照zIndex的顺序 int index = 1; while (map.size() > 0) { if (map.containsKey(index)) { map.get(index).invoke(obj); map.remove(index); } index++; } } }

复习

能够使用Junit进行单元测试能够通过反射技术获取Class字节码对象能够通过反射技术获取构造方法对象,并创建对象。能够通过反射获取成员方法对象,并且调用方法。能够说出注解的作用能够自定义注解和使用注解能够说出常用的元注解及其作用能够解析注解并获取注解中的数据能够完成注解的MyTest案例
转载请注明原文地址: https://www.6miu.com/read-4150276.html

最新回复(0)