Junit,反射,注解学习笔记

  Junit单元测试

黑盒测试:不需要写代码,只关心输入输出

白盒测试:需要关注内部代码

Junit单元测试是白盒测试的一种。

步骤:

1.定义一个测试类。类名建议为:被测试的类名Test。包名建议为:xxx.xxx.xx.test

2.定义测试方法:建议名为:test测试方法名,无返回值,空参

3.给方法加@Test

4.导入Junit依赖环境

一般不打印结果,而是通过断言来判断结果是否正确:Assert.assertEquals(预期值,实际值);

被@Before注解的方法会在所有测试方法执行前执行。

被@After注解的方法会在所有测试方法执行后执行。

 1 public class CalculationTest {
 2   @Before
 3   public void init(){
 4     System.out.println("初始化中");
 5   }
 6   @Test
 7   public void TestAdd(){
 8     Calculator c = new Calculator();
 9     int result = c.add(1,2);
10     Assert.assertEquals(3,result);
11   }
12   @After
13   public void close(){
14     System.out.println("关闭中");
15   }
16 
17 }
View Code

  反射

框架设计的灵魂

框架:半成品软件。可以在框架的基础上进行软件开发,简化编码

Java代码经历的三个阶段:

1.java文件(成员变量,构造方法,成员方法)——>class文件(成员变量,构造方法,成员方法)

2.由Classloader类加载器加载为class类对象(成员变量对象数组Field[ ] fields,构造方法对象数组Constructor[ ] cons,成员方法对象数组Method[ ] methods)

3.创建普通的对象

反射:将类的各个组成部分封装为其他对象,这就是反射机制。

优点:

1.可以在程序运行过程中(阶段2)操作这些对象。

例:我们在编程时常常会自动将可用的方法提示在一个列表中,这就是阶段2中的成员方法对象数组Method[ ] methods。

2.可以解耦,提高程序的可扩展性

  要获取类的各个组成部分对象,第一步首先是获取这个类的class对象,有三种方式:

1.处于阶段1时(字节码文件还未加载进内存):Class.forName("包名.类名")   将字节码文件加载进内存,返回Class对象

多用于配置文件,将类名定义在配置文件中,读取文件,加载类

2.处于阶段2时:类名.class 通过类名的属性class获取

多用于参数传递

3.处于阶段3时:对象.getclass( )

多用于对象的获取字节码的方式

 1         //阶段1
 2         Class cls1 = Class.forName("reflect.Person");
 3         System.out.println(cls1);
 4         //阶段2
 5         Class cls2 = Person.class;
 6         System.out.println(cls2);
 7         //阶段3
 8         Person p = new Person();
 9         Class cls3 = p.getClass();
10         System.out.println(cls3);
View Code

得到Class对象后,我们就可以从中获取:

1.成员变量们 getField(),getDeclaredField()

 1 //先获得Class对象
 2         Class cls = Person.class;
 3         //获取所有public修饰的成员变量
 4         Field[] fields = cls.getFields();
 5         for(Field field:fields){
 6             System.out.println(field);
 7         }
 8         System.out.println("-------------");
 9         //指定需要获取的成员变量名字,再获取该成员变量对象
10         Field salary = cls.getField("salary");
11         Person p = new Person();
12         Object value1 = salary.get(p);
13         System.out.println(value1);
14         salary.set(p,5000);
15         System.out.println("-------------");
16         //获取所有成员变量
17         Field[] declaredFields = cls.getDeclaredFields();
18         for(Field declaredField:declaredFields){
19             System.out.println(declaredField);
20         }
21         System.out.println("-------------");
22         //指定需要获取的成员变量名字,再获取该成员变量对象
23         Field age = cls.getDeclaredField("age");
24         age.setAccessible(true);//暴力反射
25         Object value2 = age.get(p);
26         System.out.println(value2);
View Code

2.构造方法们getConstructor(),getDeclaredConstructor()

唯一目的:创建真正的对象newInstance()

1         //获取class类对象
2         Class cls = Person.class;
3         //从类对象中获取constructor对象
4         Constructor constructor = cls.getConstructor(String.class,int.class,int.class);
5         System.out.println(constructor);
6         Object person = constructor.newInstance("小兰",23,5000);
7         System.out.println(person);
View Code

3.成员方法们getMethods(),getMethod(),getDeclaredMethod()

 1         //创建class类的对象
 2         Class cls = Person.class;
 3         //获取指定的无参的成员方法对象,并执行方法
 4         Method method1 = cls.getMethod("eat") ;
 5         Person p = new Person();
 6         method1.invoke(p);
 7         //获取指定的有参的成员方法对象,并执行方法
 8         Method method2 = cls.getMethod("eat",String.class) ;
 9         method2.invoke(p,"noodles");
10         //获取所有public修饰的方法
11         Method[] methods = cls.getMethods() ;
12         for(Method method:methods){
13             System.out.println(method);//这里也可以设置暴力反射
14         }
View Code

唯一目的:执行方法invoke()

4.类名getName()

注意:同一个字节码class文件在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

反射应用案例:

 1.配置文件(将需要创建的对象的全类名和需要执行的方法定义在配置文件中),在程序中加载读取配置文件

className = reflect.Person

methodName =eat

2.利用反射技术来加载类文件进内存

4.创建对象,执行方法

 1         Properties pro = new Properties();
 2         //获取配置文件
 3         ClassLoader classLoader = reflectpro.class.getClassLoader();
 4         InputStream is = classLoader.getResourceAsStream("pro.properties");
 5         pro.load(is);//这样pro对象里就存放了配置的内容
 6         //获取配置文件中的数据
 7         String className = pro.getProperty("className");
 8         String methodName =  pro.getProperty("methodName");
 9         //加载该类进内存
10         Class cls = Class.forName(className);//获得了类对象cls
11         //创建真实对象obj
12         Object obj = cls.newInstance();
13         //获取cls对象中的方法对象
14         Method method = cls.getMethod(methodName);
15         //利用真实对象执行方法
16         method.invoke(obj);
View Code

  注解

作用:对程序进行说明

作用分类:

1.编写文档:通过注解生成文档

将java文件复制一份到一个文件夹中,cmd中输入javadoc.xxx.java命令,文件夹中就会生成API

2.代码分析:通过注解对代码进行分析(使用反射)

3.编译检查:通过注解让编译器能够实现基本的编译检查(Override)

JDK常见的预定义注解:

1.@Override:检测被该注解标注的方法是否继承了父类,如不符合会报错

2.@Deprecated:该注解标注的内容,表示已过时,之后调用就会被划掉(表示不推荐)

3.@SuppressWarning:压制警告,标注后系统将不会提出警告,一般在程序开头写@SuppressWarning("all"),表示压制所有警告

自定义注解

格式:

元注解

public @interface 注解名称{属性列表}

注解的本质是一个接口,默认继承Annotation接口

属性:接口中的成员方法,可以为基本数据类型,String,枚举,注解,以及以上类型的数组

若定义属性,在使用时需要给属性赋值:

1.若default默认初始化值,则使用时不用赋值

2.若只有一个属性需要赋值,且名字为value,则赋值时直接定义值即可,如@SuppressWarning("all")

3.给数组赋值时,值用{}包裹,若数组中只有一个值,{}可省略

元注解:

用于描述注解的注解

1.@Target:描述注解能够作用的位置,TYPE表示可以作用于类上,METHOD表示可以作用于方法上,FIELD表示可以作用于成员变量上

2.@Retention:描述注解被保留的阶段,runtime表示注解会保留到class字节码文件中,并被JVM读取到

3.@Documented:描述注解是否被抽取到api文档中

4.@Inherited:描述注解是否被子类继承,若某类的注解存在该元注解,则继承它的类也会继承这个注解

解析注解:

获取注解中定义的属性值

1.获取注解定义的位置的对象

2.获取指定的注解getAnnotation(Class)

3.调用注解中的抽象方法获取配置的属性值

 1 @Pro(className="annotation.student",methodName = "study")
 2 public class annotationTest {
 3     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
 4         //获取该类的字节码文件对象
 5         Class<annotationTest> aTclass = annotationTest.class;
 6         //获取上面的注解对象,本质上是在内存中生成了一个该注解接口的子类实现对象
 7         Pro an = aTclass.getAnnotation(Pro.class);
 8         //调用注解对象中定义的抽象方法,获得返回值
 9         String className = an.className();
10         String methodName = an.methodName();
11         //加载该类进内存
12         Class cls = Class.forName(className);//获得了类对象cls
13         //创建真实对象obj
14         Object obj = cls.newInstance();
15         //获取cls对象中的方法对象
16         Method method = cls.getMethod(methodName);
17         //利用真实对象执行方法
18         method.invoke(obj);
19 
20     }
View Code

注解代码

1 //元注解
2     @Target({ElementType.TYPE})
3     @Retention(RetentionPolicy.RUNTIME)
4 public @interface Pro {
5     String className();
6     String methodName();
7 }
View Code

 

posted @ 2020-04-19 22:26  菅兮徽音  阅读(156)  评论(0编辑  收藏  举报