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 }
反射
框架设计的灵魂
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
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);
得到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);
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);
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 }
唯一目的:执行方法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);
注解
作用:对程序进行说明
作用分类:
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 }
注解代码
1 //元注解 2 @Target({ElementType.TYPE}) 3 @Retention(RetentionPolicy.RUNTIME) 4 public @interface Pro { 5 String className(); 6 String methodName(); 7 }