1. 测试分类
- 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
- 白盒测试:需要写代码的。关注程序具体的执行流程。
2.Junit测试(白盒测试)
//所在包
package cn.qiang.junit;
public class Calculator { ... }
import cn.qiang.junit.Calculator;//导入junit依赖环境
...
//所在包
package cn.itcast.test;
public class CalculatorTest {
//初始化方法:用于资源申请,所有测试方法在执行之前都会先执行该方法
@Before
public void init(){ System.out.println("init..."); }
//释放资源方法:在所有测试方法执行完后,都会自动执行该方法
@After
public void close(){ System.out.println("close..."); }
//测试add方法
@Test
public void testAdd(){
System.out.println("testAdd...");
Calculator c = new Calculator();
int result = c.add(1, 2);
Assert.assertEquals(3,result);断言这个结果是3
}
@Test
public void testSub(){
Calculator c = new Calculator();
int result = c.sub(1, 2);
System.out.println("testSub....");
Assert.assertEquals(-1,result);
}
}
命名建议:
- 测试类名:CalculatorTest
- 测试方法名:testAdd( ) void 空参 @Test
- 包名:cn.itcast.test
- 导入junit依赖环境
3.反射
- 反射机制:将类的各个组成部分封装为其他对象
- 好处:
1. 可以在程序运行过程中,操作这些对象。
2. 可以解耦,提高程序的可扩展性。
①获取Class对象
//1.Class.forName("全类名") 字节码文件加载进内存,返回Class对象
Class cls1 = Class.forName("cn.itcast.domain.Person");//用于配置文件
System.out.println(cls1);
//2.类名.class 通过类名的属性class获取
Class cls2 = Person.class;//用于参数的传递
System.out.println(cls2);
//3.对象.getClass() Object类方法
Person p = new Person();
Class cls3 = p.getClass();//用于对象的获取字节码的方式
System.out.println(cls3);
//== 比较三个对象
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
注意
:同一个字节码文件(*.class)在一次程序运行过程中只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个
public class Person {
private String name;
private int age;
public String a;
protected String b;
default String c;
private String d;
...
public Person() { }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() System.out.println("eat...");
public void eat(String food) System.out.println("eat..."+food);
private void play() System.out.println("play...");
private void play(String game) System.out.println("play..."+game);
}
②使用Class对象—获取成员变量
1. 获取成员变量
public final class Class<T> {
public Field getField(String name)
//获取指定public修饰的成员变量
public Field[] getFields()
//获取所有public修饰的成员变量
public Field getDeclaredField(String name)
//获取指定成员变量,非public要打开权限
public Field[] getDeclaredFields()
//获取所有的成员变量,不考虑修饰符
}
public final class Field{
public void set(Object obj, Object value)
public Object get(Object obj)
}
//获取Person的Class对象
Class personClass = Person.class;
//获取public成员变量a
Field a = personClass.getField("a");
//获取到成员变量a之后可以用Field成员方法操作他
Person p = new Person();
System.out.println("默认的变量值:"+a.get(p));
a.set(p,"张三");//设置a的值
System.out.println("getField设置后的成员变量:"+a.get(p));
//获取所有public成员
Field[] fields = personClass.getFields();
for (Field field : fields)
System.out.println("getFields方法获取的成员变量:"+field);
//获取非public成员变量d
Field d = personClass.getDeclaredField("d");
d.setAccessible(true);//忽略访问权限修饰符的安全检查,暴力反射
//获取到成员变量d之后可以用Field成员方法操作他
System.out.println("默认的变量值:"+d.get(p));
d.set(p,"林俊杰");//设置d的值
System.out.println("getDeclaredField设置后的成员变量:"+d.get(p));
//获取所有非public成员变量
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields)
System.out.println(declaredField);
③使用Class对象—获取构造方法
2. 获取构造方法们
public final class Class<T> {
public Constructor<T> getConstructor(Class<?>... parameterTypes)
}
public final class Constructor<T>{
public T newInstance(Object ... initargs)
}
Class personClass = Person.class;
Constructor constructor1 = personClass.getConstructor();
Constructor constructor2 = personClass.getConstructor(String.class, int.class);
System.out.println(constructor1);
System.out.println(constructor2);
Object person1 = constructor1.newInstance();
Object person2 = constructor2.newInstance("张三", 23);
System.out.println(person1);
System.out.println(person2);
④使用Class对象—获取成员方法
3. 获取成员方法们
public final class Class<T> {
public Method getMethod(String name, Class<?>... parameterTypes)
//获取指定public修饰的成员方法
public Method[] getMethods()
//获取所有public修饰的成员方法
Method[] getDeclaredMethods()
//获取指定成员方法,非public要打开权限
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
//获取所有的成员方法,不考虑修饰符
}
public final class Method{
public Object invoke(Object obj, Object... args)
//执行方法
public String getName() { return name; }
//获取方法名
}
Class personClass = Person.class;
Method eat_method1 = personClass.getMethod("eat");
Method eat_method2 = personClass.getMethod("eat", String.class);
Method[] methods1 = personClass.getMethods();
Method play_method1=personClass.getDeclaredMethod("play");
Method play_method2=personClass.getDeclaredMethod("play",String.class);
Method[] methods2 = personClass.getDeclaredMethods();
Person p = new Person();
eat_method1.invoke(p); //执行方法
eat_method2.invoke(p,"饭");
play_method1.setAccessible(true);
play_method2.setAccessible(true);
play_method1.invoke(p); //执行方法
play_method2.invoke(p,"lol");
System.out.println("仅公有");
for (Method method : methods1) {
System.out.print(method+" ");
System.out.println(method.getName());
}
System.out.println("有私有");
for (Method method : methods2)
System.out.println(method);
String className = personClass.getName(); //获取类名
System.out.println(className);//cn.itcast.domain.Person
4.反射案例
步骤:
- 在配置文件中写入类名和方法
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象并执行方法
框架类:配置文件+反射
public class ReflectTest {
public static void main(String[] args) throws Exception {
//前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
//1.....
//2.加载配置文件
//创建Properties对象
Properties pro = new Properties();
//加载配置文件,转换为一个集合
//通过类加载器获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
//获取字节码文件,并获取对应的类加载器,把ReflectTest加载进内存
classLoader.getResource("C:\\Users\\QIANG\\Desktop\\day01_基础加强\\代码\\day01_基础加强\\src\\pro.properties");
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);//需要传入一个字节流或者字符流
//3.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//4.加载该类进内存
Class cls = Class.forName(className);
//5.创建对象
Object obj = cls.newInstance();
//6.获取方法对象
Method method = cls.getMethod(methodName);
//7.执行方法
method.invoke(obj);
}
}
5.注解
①预定义注解
@Override
@Deprecated 注解标注的内容,表示已过时
@SuppressWarnings(value="all") 压制警告
@Override
public String toString() { return super.toString(); }
@Deprecated//过期注解
public void show1(){ //有缺陷 }
public void show2(){ //替代show1方法 }
②自定义注解
1. 格式和本质:注解本质是一个接口,继承自Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
元注解
public @interface 注解名称{
属性列表;
}
2. 属和元注解:
- 属性:接口中的抽象方法
- 元注解:用于描述注解的注解
public enum Person { P1,P2,P3; }
public @interface MyAnno {
int value();
String show();
String[] strs();//字符串数组
String name() default "张三";//默认赋值
Person per();//枚举
MyAnno2 anno2();//注解
}
* 属性只能是:基本数据类型 + String + 枚举 + 注解 + 以上类型的数组
* 使用default修饰属性,可以不进行属性的赋值
* 一个属性且属性名为value,可直接定义@SuppressWarnings("all")
* 数组中只有一个值时{ }可以省略
@MyAnno(value=12,show="迪迦",strs={"bbb","aaa"},per = Person.P3,anno2 = @MyAnno2)
public class Worker {
public String name = "aaa";
public void show()
}
* @Target:描述注解能够作用的位置
* ElementType取值:
* TYPE:可以作用于类上
* METHOD:可以作用于方法上
* FIELD:可以作用于成员变量上
* @Retention:描述注解被保留的阶段
* @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
* @Documented:描述注解是否被抽取到api文档中
* @Inherited:描述注解是否被子类继承
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno {
}
@MyAnno
public class Worker {
@MyAnno
public String name = "aaa";
@MyAnno
public void show()
}