学习目标:
- 学会使用单元测试对程序进行测试
- 理解什么是反射,并会使用反射
- 熟练使用注解,能够自定义注解
学习内容:
一、junit单元测试
1、测试分类
- 黑盒测试:不需要编写代码,只需要提供输入,观察得到的是不是期望的输出
- 白盒测试:需要编写代码,关注的是程序执行过程
2、Junit(白盒测试)使用
- 步骤
- 定义一个测试类
测试类名:被测试的类名Test CaculatorTest
包名:xxx.xx.xx.test 1.2.test - 定义测试方法:可以独立运行
方法名:test测试的方法名 testAdd()
返回值:void
参数:空参 - 给方法加@Test
- 导入Junit依赖环境
- 定义一个测试类
- 判定结果
- 红色:失败
- 绿色:成功
- 一般我们使用断言操作来处理结果
Assert.assertEqual(期望的结果,运算的结果)
- appendix
- @Before
修饰的方法会在测试方法执行之前被自动执行 - @After
修饰的方法会在测试方法执行之后被自动执行
- @Before
二、反射:框架设计的灵魂
1、框架
- 半成品软件,可以在框架的基础上进行软件开发,简化编码
2、反射
- 将类的各个组成部分封装为其他对象,就是反射机制
3、优势
- 可以在程序运行过程中,操作这些对象
- 可以解耦,提高程序的可扩展性
4、获取class对象的方式
- Class.forName(“全类名”); 将字节码文件加载进内存,返回class对象
- 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 类名.class; 通过类名的属性class获取
- 多用于参数的传递
- 对象.getClass(); getClass方法在Object类中定义着
- 多用于对象获取字节码的方式
- 结论:
- 同一个字节码文件(*.class)在一次程序运行的过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个。
5、使用class对象
- a、获取类型的方法
- 获取变量
- Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 - Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 - Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 - Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
- Field getField(String name)
- 使用成员变量(通过获得的变量对象调用)
- void set(Object obj, Object value);
设置值 - get(Object obj);
获取值 - setAccessable(true);
忽略访问权限修饰符的安全检查,暴力反射
- void set(Object obj, Object value);
- 获取构造方法
- Constructor getConstructor(Class<?>… parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 - Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 - Constructor getDeclaredConstructor(Class<?>… parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 - Constructor<?>[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
- Constructor getConstructor(Class<?>… parameterTypes)
- 使用构造方法
- T newInstance(Object… initargs)
获取有参构造并创建实例,使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 - T newInstance()
无参构造时可以不进行获取直接创建此 Class 对象所表示的类的一个新实例。
- T newInstance(Object… initargs)
- 获取成员方法
- Method getMethod(String name, Class<?>… parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 - Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 - Method getDeclaredMethod(String name, Class<?>… parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 - Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
- Method getMethod(String name, Class<?>… parameterTypes)
- 执行成员方法
- Object invoke(Object obj, Object… args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 - String getName()
获取方法名,以 String 形式返回此 Method 对象表示的方法名称。
- Object invoke(Object obj, Object… args)
- 获取类名
- String getName()
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
- String getName()
- 获取变量
三、注解
1、简介
- 概念
- JDK1.5之后的新特性
- 说明程序的
- 使用注解:@注解名称
- 作用分类
- 编写文档:通过代码里的标识的注解生成文档(生成DOC文档,Javadoc Java文件)
- 代码分析:通过代码里标识的注解对代码进行分析(使用反射)、
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查(Override)
2、JDK中预定义的一些注解
- @Override
- 检查是否对父类进行重写
- @Deprecated
- 标定该方法已经过时
- @SuppressWarnings(“all”)
- 压制所有的异常信息
3、自定义注解
- 格式
- 元注解
- public @interface 注解名称{}
- 本质
- 注解本质上是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java.lang.annotation.Annotation {}
- 属性
接口中的抽象方法- 返回值类型
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性在使用时需要赋值
- 如果定义属性时,如果使用default关键字初始化,则不必进行赋值
- 只有一个属性需要赋值,并名称为value,可以不写属性名直接传值
- 数组赋值时如果只有一个值,可以省略{}
- 返回值类型
- d、元注解
描述注解的注解- @Target(value={}) 描述注解作用的位置
- ElementType.TYPE 作用于类上
- ElementType.METHOD 作用于方法上
- ElementType.FIELD 作用于成员变量上
- @Retention() 描述注解保留到哪一阶段
- RetentionPolicy.RUNTIME 保留到运行阶段
- RetentionPolicy.SOURCE 保留到源码阶段
- RetentionPolicy.CLASS 保留到编码阶段
- @Documented 描述注解在生成API文档时是否被保留
- @Inherited 描述注解在子类中是否会被继承
- @Target(value={}) 描述注解作用的位置
4、在程序中使用(解析)注解
- 获取注解定义的位置的对象 (class,method,field)
- 获取指定的注解
- getAnnotation(Class) 在内存中生成一个该注解接口的子类实现对象
- 调用注解中的抽象方法获取配置的属性值
5、小结
- 以后大多数时候,我们会使用注解,而不是自定义注解
- 注解给谁用:
- 编译器
- 解析程序
- 注解不是程序的一部分,可以理解为注解就是一个标签
学习产出:
1、 Junit
package xuefeng.cn.demo01Junit;
/**
* Design a caculator
*/
public class Caculator {
/**
* add
* @param a
* @param b
* @return
*/
public int Add(int a, int b){
return a+b;
}
/**
* sub
* @param a
* @param b
* @return
*/
public int Sub(int a, int b){
return a-b;
}
}
package xuefeng.cn.demo02test;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import xuefeng.cn.demo01Junit.Caculator;
public class CaculatorTest {
@Before
public void init(){
System.out.println("init...");
}
@Test
public void testAdd(){
Caculator c = new Caculator();
int result = c.Add(1, 2);
Assert.assertEquals(3,result);
}
@Test
public void testSub(){
Caculator c = new Caculator();
int result = c.Sub(2, 1);
Assert.assertEquals(1,result);
}
@After
public void Close(){
System.out.println("close...");
}
}
2、Reflect
- 获取class对象的方式
package xuefeng.cn.demo03Reflect;
import xuefeng.cn.domain.Person;
public class Demo01GetClass {
public static void main(String[] args) throws Exception {
// 第一种方式:直接用class的forName方法获取
Class cls1 = Class.forName("xuefeng.cn.domain.Person");
System.out.println(cls1);
// 第二种方式,类名.class获取
Class cls2 = Person.class;
System.out.println(cls2);
// 第三种方式,通过对象调用getClass()方法获得
Person person = new Person();
Class cls3 = person.getClass();
System.out.println(cls3);
}
}
- 获取变量
package xuefeng.cn.demo03Reflect;
import xuefeng.cn.domain.Person;
import java.lang.reflect.Field;
public class Demo02GetField {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class<Person> cls = Person.class;
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("------------");
Field a = cls.getField("a");
Object val1 = a.get(p);
System.out.println(val1);
a.set(p,"Hello");
Object val2 = a.get(p);
System.out.println(val2);
System.out.println("===============");
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("--------------");
Field d = cls.getDeclaredField("d");
d.setAccessible(true);
Object val3 = d.get(p);
System.out.println(val3);
}
}
- 获取构造方法
package xuefeng.cn.demo03Reflect;
import xuefeng.cn.domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Demo03GetConstructor {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class<Person> cls = Person.class;
Constructor<Person> constructor = cls.getConstructor();
Person person = constructor.newInstance();
System.out.println(person);
System.out.println("------------");
Constructor<Person> constructor1 = cls.getConstructor(String.class, int.class);
Person person1 = constructor1.newInstance("Jim", 18);
System.out.println(person1);
System.out.println("------------");
Constructor<Person> constructor2 = cls.getConstructor();
Person person2 = constructor2.newInstance();
System.out.println(person2);
System.out.println("--------------");
}
}
- 获取成员方法
package xuefeng.cn.demo03Reflect;
import xuefeng.cn.domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Demo04GetMethod {
public static void main(String[] args) throws Exception {
Class<Person> cls = Person.class;
Person person = new Person();
Method method = cls.getMethod("eat");
method.invoke(person);
System.out.println("------------");
Method method1 = cls.getMethod("drink", String.class);
method1.invoke(person, "bear");
System.out.println("------------");
Method[] methods = cls.getMethods();
for (Method method2 : methods) {
method2.setAccessible(true);
System.out.println(method2);
System.out.println(method2.getName());
}
System.out.println("--------------");
}
}
- 练习:
package xuefeng.cn.demo03Reflect;
/**
* 需求:写一个“框架”类不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
* 实现:
* 1、配置文件
* 2、反射
* 步骤:
* 1、将需要创建的对象的全类名和需要执行的方法定义在配置文件中
* 2、在程序中加载读取配置文件
* 3、使用反射技术加载类文件到内存
* 4、创建对象
* 5、执行方法
*/
import xuefeng.cn.domain.Person;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class Demo05Training {
public static void main(String[] args) throws Exception {
// 1、加载配置文件
// 1.1创建properties对象
Properties properties = new Properties();
// 1.2加载配置文件,转换成一个集合
// 1.2.1获取class目录下的配置文件
ClassLoader classLoader = Demo05Training.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("Pro.Properties");
properties.load(is);
// 2、获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
// 3、加载该类进内存
Class aClass = Class.forName(className);
// 4、创建对象
Object obj = aClass.newInstance();
// 5、获取方法对象
Method method = aClass.getMethod(methodName);
// 6、执行方法
method.invoke(obj);
}
}
3、 注解
- 编写文档
package xuefeng.cn.demo04Annotation;
/**
* ע��javadoc��ʾ
* @author xuefeng
* @version 1.0
* @since 1.5
*/
public class Demo01Doc {
/**
* ����������֮��
* @param a ����
* @param b ����
* @return ����֮��
*/
public int add(int a, int b){
return a+b;
}
}
- JDK中预定义的一些注解
package xuefeng.cn.demo04Annotation;
@SuppressWarnings("all")
public class Demo02Anno {
@Override
public String toString() {
return "Demo02Anno{}";
}
@Deprecated
public void show(){
System.out.println("111");
}
public void show1(){
System.out.println("222");
}
}
- 自定义注解、属性赋值
package xuefeng.cn.demo04Annotation;
public enum Enum {
p1,p2;
}
package xuefeng.cn.demo04Annotation;
@SuppressWarnings("all")
public @interface Anno {
}
package xuefeng.cn.demo04Annotation;
public @interface Demo03MyAnno {
public abstract String show();
int age();
Enum en();
Anno anno();
String[] strs();
}
package xuefeng.cn.demo04Annotation;
@Demo03MyAnno(show = "hello",age = 12,
en = Enum.p1,anno = @Anno(),strs = {"12","ads"})
public class Demo04Test {
}
- 元注解
package xuefeng.cn.demo04Annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Demo05MyAnno1 {
}
- 练习一
package xuefeng.cn.demo04Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
package xuefeng.cn.demo04Annotation;
/**
* 需求:写一个“框架”类不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
* 实现:
* 1、配置文件
* 2、反射
* 步骤:
* 1、将需要创建的对象的全类名和需要执行的方法定义在配置文件中
* 2、在程序中加载读取配置文件
* 3、使用反射技术加载类文件到内存
* 4、创建对象
* 5、执行方法
*/
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
@Pro(className = "xuefeng.cn.domain.Student",methodName = "sleep")
public class Demo06Training {
public static void main(String[] args) throws Exception {
// 1、加载配置文件
Class<Demo06Training> trainingClass = Demo06Training.class;
Pro pro = trainingClass.getAnnotation(Pro.class);
// 2、获取配置文件中定义的数据
String className = pro.className();
String methodName = pro.methodName();
// 3、加载该类进内存
Class aClass = Class.forName(className);
// 4、创建对象
Object obj = aClass.newInstance();
// 5、获取方法对象
Method method = aClass.getMethod(methodName);
// 6、执行方法
method.invoke(obj);
}
}
- 练习二
package xuefeng.cn.demo04Annotation;
public class Caculator {
@CheckTest
public int add(){
return 1+0;
}
@CheckTest
public int sub(){
return 1-0;
}
@CheckTest
public int pub(){
return 1*0;
}
@CheckTest
public int div(){
return 1/0;
}
public void show(){
System.out.println("Achieve......");
}
}
package xuefeng.cn.demo04Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckTest {
}
package xuefeng.cn.demo04Annotation;
/*
测试框架
*/
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
public class Demo07Check {
public static void main(String[] args) throws IOException {
Caculator caculator = new Caculator();
Class<? extends Caculator> aClass = caculator.getClass();
Method[] methods = aClass.getMethods();
int number = 0;
BufferedWriter log = new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
if (method.isAnnotationPresent(CheckTest.class)){
try {
method.invoke(caculator);
} catch (Exception e) {
number++;
log.write(method.getName());
log.newLine();
log.write("The Exception Name:" + e.getCause().getClass().getSimpleName());
log.newLine();
log.write("The Exception Reason:" + e.getCause().getMessage());
log.newLine();
log.write("--------------------");
log.newLine();
}
}
}
log.write("After the test check, find "+number+" faults!!!");
log.flush();
log.close();
}
}
4、附件
package xuefeng.cn.domain;
public class Person {
private String Name;
private int age;
public String a;
protected String b;
String c;
private String d;
public void eat(){
System.out.println("eat...");
}
public void drink(String sth){
System.out.println("drink..."+sth);
}
public Person() {
}
public Person(String name, int age) {
Name = name;
this.age = age;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "Name='" + Name + '\'' + ", age=" + age + ", a='" + a + '\'' + ", b='" + b + '\'' + ", c='" + c + '\'' + ", d='" + d + '\'' + '}';
}
}
package xuefeng.cn.domain;
public class Student {
public void sleep(){
System.out.println("Sleep...");
}
}