JUnit单元测试:
测试一个方法有没有问题, 是否能否按照我们设计的方式运行 (不用main方法直接运行)
package JunitTest;
//Junit 测试方法是否正确 ,第一步先创建一个方法
//我创建的方法是计算器,计算 有三个方法,分别是 加 减 除 。
public class Calculator {
public int sum(int a , int b){
// int c = 3/0;
return a-b;
}
public int sub(int a , int b){
return a+b;
}
public int chu(int a , int b ){
return a/b;
}
}
package JunitTest;
//正常在测试类中使用它们,即便方法写错 也不会被显示,方法仍会被执行。
public class TestCalculator {
public static void main(String[] args) {
int sum = new Calculator().sum(2, 2);
System.out.println(sum);
int sub = new Calculator().sub(3, 2);
System.out.println(sub);
int chu = new Calculator().chu(6, 3);
System.out.println(chu);
}
}
package JunitTest;
import org.junit.Assert;
import org.junit.Test;
public class TestJunit {
//这个 本应该是加法的方法,但是方法写错了 一样是可以运行的,这个时候只能使用: 断言 来判定方法是否正确
@Test
public void Testsum(){
int sum = new Calculator().sum(2, 3);
System.out.println(sum);
//断言我的期望值,也就是正确方法应该有的值,后一个参数是结果,也就是sum
Assert.assertEquals(5,sum);
}
//这个方法同上面那个方法一样
@Test
public void Testsub(){
int sub = new Calculator().sub(3, 2);
System.out.println(sub);
Assert.assertEquals(1,sub);
}
//这个方法是正确的
@Test
public void Thwodchu(){
int chu = new Calculator().chu(3, 1);
System.out.println(chu);
}
//此测试 方法的盒子中有两个错误的方法,最后一个方法是正确的
}
//另外两个方法 一个是After, 一个是Before
@After
public void end(){
System.out.println("此方法在最后一个执行");
}
@Before
public void begin(){
System.out.println("此测试方法方法在第一个被执行");
}
Java(反射)代码的三个阶段:
1.源代码阶段source
包含两种状态 第一种是未被编译处理时候的状态.Java
第二种是被javac编译后生成.class文件的状态
2.Class 类对象阶段
将字节码文件加载进内存,类的加载器,对应Classloader
java中有一个类 叫Class 用来描述所有字节码文件的
字节码文件中都会有一些共有的方法
成员变量 Field 类的对象 (封装成对象)
构造方法 Constructor 构造方法类的对象
成员方法 Method 类的对象
反射: 将类的各个组成部分, 封装为其他对象, 这就是反射机制
好处:
1. 可以在程序运行过程中, 操作这些对象
2. 可以解耦, 提高程序的可扩展性
3.运行时阶段Runtime
创建对象阶段 new
反射: 获取字节码对象的3种方式
获取一个类的字节码对象的3种方式:
1. Class.forName("全类名")
将字节码文件加载进内存,返回Class对象
适用场景: 多用于配置文件,将类名定义在配置文件中. 读取文件, 加载类
2. 类名.class .
通过类名的属性class获取
适用场景: 多用于参数的传递 getConstructor(String.class, int.class)
3. 对象.getClass()
getClass()方法在Object类中定义
适用场景: 多用于对象的获取字节码的方式 p.getClass()
反射: Class的方法概述
java.lang.Class<T>类: 表示一个类的字节码对象, 其中包含该类中定义的内容
// 成员方法
// 1. 获取成员变量们
Field[] getFields(): 获取所有 public 的成员变量
Field getField(String name): 获取指定名称的 public 的成员变量
Field[] getDeclaredFields(): 获取所有的成员变量, 不考虑权限修饰符
Field getDeclaredField(String name): 获取指定名称的成员变量, 不考虑权限修饰符
// 2. 获取构造方法们
Constructor<?>[] getConstructors(): 获取所有 public 的构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes): 获取指定的 public 构造方法
Constructor<?>[] getDeclaredConstructors(): 获取所有的构造方法, 不考虑权限修饰符
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 获取指定的构造方法, 不考虑权限修饰符
// 3. 获取成员方法们:
Method[] getMethods(): 获取所有 public 的成员方法
Method getMethod(String name, Class<?>... parameterTypes) : 获取指定的 public 成员方法
Method[] getDeclaredMethods(): 获取所有的成员方法, 不考虑权限修饰符
Method getDeclaredMethod(String name, Class<?>... parameterTypes): 获取指定的成员方法, 不考虑权限修饰符
// 4. 获取Class对象代表的类的全类名
String getName(): 获取当前Class对象代表的类的全类名
// 5. 创建对象
T newInstance(): 使用当前类的空参构造, 创建一个对象
反射: 利用反射实现可以运行任意类的任意方法的框架案例
补充:
java.lang.Class<T>
// 成员方法
ClassLoader getClassLoader(): 返回该类的类加载器
java.lang.ClassLoader: 类加载器 加载.class文件到内存的方法区中, 其他类型文件.properties
// 成员方法
InputStream getResourceAsStream(String name): 读取相对于 out/production/模块名 目录中的文件, 返回一个字节流
使用类加载器加载配置文件
// 随便获取一个类的字节码对象
Class clazz = 类名.class;
// 用字节码对象获取类加载器, 可加载bin目录中编译的文件
ClassLoader classLoader = clazz.getClassLoader();
// 使用类加载器加载一个文件, 返回一个字节流
InputStream is = classLoader.getResourceAsStream("相对于src目录的相对路径");
// 有了字节流, 就可以使用Properties的load(InputStream in)方法读取配置
Properties p = new Properties();
p.load(is);
String value = p.getProperty("key");
需求:
写一个"框架"程序, 框架写好后"不需改动框架的任何代码", 只需修改配置文件中的类名和方法名, 就能让框架程序帮我们创建配置文件中指定的类的对象, 并且执行其中指定的方法
分析:
实现技术:
1. 配置文件: Properties 加载配置信息
2. 反射: 使用配置文件中的全类名, 获取该类的字节码对象, 创建该类对象, 调用指定的方法
步骤:
建类Person:
定义成员方法 public void eat(){}. 方法中输出"吃东西"
建类Student:
定义成员方法 public void sleep(){} 方法中输出"睡觉"
在src目录下创建一个文件: pro.properties
填写以下键值对:
className=Person类的全名
methodName=eat
建类Test, 定义main方法, 在main方法中:
创建Properties对象
获取当前Test类的字节码对象, 使用字节码对象调用getClassLoader()获取一个类加载器对象
使用类加载器对象调用 getResourceAsStream("pro.properties") 获取一个InputStream对象
使用Properties对象的load方法加载InputStream对象
// 获取类加载器
ClassLoader cl = 当前类名.class.getClassLoader();
InputStream is = cl.getResourceAsStream("pro.properties");
// 创建Properties对象, 加载流
Properties properties = new Properties();
properties.load(is);
从Properties对象中根据键"className"和"methodName"分别获取类名字符串和方法名字符串
使用Class.forName("类的全名")方法传入读取到的类名, 获取该类字节码对象
通过字节码对象创建该类对象, 并获取指定方法名的Method对象
通过Method对象的invoke方法调用该方法, 查看是否调用吃饭方法
修改配置文件中的类名为Student类的全名, 方法为sleep, 重新运行代码查看是否调用了睡觉方法
【代码】:
# 配置文件 src目录下的pro.properties
className=com.itheima.test03.Student
methodName=sleep
public class Person {
public void eat() {
System.out.println("人吃饭");
}
}
public class Student {
public void sleep() {
System.out.println("学生睡觉");
}
}
/*
模拟实现框架
目的: 加载配置文件中指定的类, 并调用指定的方法
*/
public class Test {
public static void main(String[] args) throws Exception {
/*
加载配置文件:
通过类加载器加载配置文件变成字节输入流
然后让Properties加载流中的数据
*/
ClassLoader classLoader = Test.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
Properties properties = new Properties();
properties.load(is);
/*
获取配置信息:
通过Properties的键, 获取值
*/
// 通过配置文件中的类名key获取真正的类名的value
String className = properties.getProperty("className");
// 通过配置文件中的方法名key获取真正的方法名的value
String methodName = properties.getProperty("methodName");
/*
通过反射技术创建指定类的对象, 并调用方法:
*/
// 先通过读取到的全类名, 获取字节码对象.
Class cls = Class.forName(className);
// 通过字节码对象创建该类的对象
Object obj = cls.newInstance();
// 通过字节码对象获取指定的方法对象
Method method = cls.getDeclaredMethod(methodName);
// 调用方法
method.invoke(obj);
}
}
|
|