Java --> 单元测试、反射、注解、动态代理
温馨提示:由于图片中内容较多,字节很小,选中图片右键选择 ”在新标签页中打开图片“,在新的页面中可放大查看~
- 步骤:单元测试的快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:
- 将JUnit的jar包导入到项目中
- IDEA通常整合好了JUnit框架,一般不需要导入
- 如果IEDA没有整合好,需要自己手工导入如下hamcrest-core-1.3.jar library root、junit-4.12.jar library root
2.编写测试方法:改测试方法必须是 公开的、无参数、无返回值的 非静态方法
3.在测试方法上使用@Test注解 :标注改方法是一个测试方法
4.在测试方法中完成被测试方法的预期正确性测试
5.选中测试方法,选择”JUnit运行“,如果测试良好则是绿色、测试失败是红色
1 //Junit单元测试
2 public class UserService {
3 public String loginName(String loginName, String password){
4 String ret = "登录成功";
5 if ("admin".equals(loginName) && "123456".equals(password)){
6 return ret;
7 }else {
8 return "登录失败";
9 }
10 }
11 public void selectNames(){
12 System.out.println(10 / 0);
13 System.out.println("查询用户名完成");
14 }
15 }
1 import org.junit.*;
2
3 //测试类
4 public class TestUserService {
5 //修饰实例方法的
6 @Before
7 public void before(){
8 System.out.println("-------before()执行一次--------");
9 }
10 @After
11 public void after(){
12 System.out.println("-----------after()执行一次----------------");
13 }
14
15 //修饰静态犯法
16 @BeforeClass
17 public static void beforeClass(){
18 System.out.println("-----------beforClass()执行一次------------");
19 }
20 @AfterClass
21 public static void afterClass(){
22 System.out.println("-----------afterClass()执行一次----------------");
23 }
24
25
26 /** 1、测试方法必须:公开、无参、无返回值、非静态
27 2、测试方法必须使用@Test注解标记
28 */
29 @Test //第一次导入建议联网【alt + 回车】
30 public void testLoginName(){
31 UserService userService = new UserService();
32 String ret = userService.loginName("admin","123456");
33
34 //预期结果正确性测试结果 断言
35 Assert.assertEquals("您的登录功能可能出现问题","登录成功",ret);
36 }
37
38 @Test
39 public void testSelectName(){
40 UserService userService = new UserService();
41 userService.selectNames();
42 }
43 }
- 反射:获取Class类对象
1 //反射的第一步:获取Class类对象
2 public class Test {
3 public static void main(String[] args) throws Exception{
4 //包名 + 类名 ==> 全限名
5 //1、使用Class类中的静态方法:forName()
6 Class c = Class.forName("com.companyName.d2_reflect_class.Student");
7 System.out.println(c);
8
9 //2、类名.class
10 Class c1 = Student.class;
11 System.out.println(c1);
12
13 //3、对象.getClass() : 获取对象对应类的Class对象
14 Student student = new Student();
15 Class c2 = student.getClass();
16 System.out.println(c2);
17 }
18 }
public class Student {
}
从运行结果也可以看出,这三个类对象c、c1、c2均为同一个类对象,因为它们指向同一个地址
- 使用反射获取构造器对象并使用
- 获取class类对象
- 获取Constructo对象
- 创建对象【构造器是用来创建对象的】
1 public class Student {
2 private String name;
3 private int age;
4
5 private Student() {
6 }
7
8 public Student(String name, int age) {
9 this.name = name;
10 this.age = age;
11 }
12
13 public String getName() {
14 return name;
15 }
16
17 public void setName(String name) {
18 this.name = name;
19 }
20
21 public int getAge() {
22 return age;
23 }
24
25 public void setAge(int age) {
26 this.age = age;
27 }
28
29 @Override
30 public String toString() {
31 return "Student{" +
32 "name='" + name + '\'' +
33 ", age=" + age +
34 '}';
35 }
36 }
1 import org.junit.Test;
2
3 import java.lang.reflect.Constructor;
4
5 //使用反射获取类对象中的构造器
6 public class TestStudentDemo1 {
7 @Test
8 public void getConstructors(){
9 //1、获取类对象
10 Class c1 = Student.class;
11 //2、提取类中全部的构造器对象【只能拿到public修饰的构造器】
12 Constructor[] constructors = c1.getConstructors();
13 //遍历构造器
14 for (Constructor constructor : constructors) {
15 System.out.println(constructor.getName() + "-->" + constructor.getParameterCount());
16 }
17 }
18
19 @Test
20 public void getConstructor() throws Exception{
21 //1、获取类对象
22 Class c1 = Student.class;
23 //2、提取类中某个指定的构造器对象【只能拿到public修饰的构造器】
24 Constructor constructor = c1.getConstructor(String.class,int.class);
25 //2.1 获取无参数构造器
26 Constructor constructor2 = c1.getConstructor(); //无法拿到私有的构造器
27 //打印构造器
28 System.out.println(constructor.getName() + "-->" + constructor.getParameterCount());
29 System.out.println(constructor2.getName() + "-->" + constructor2.getParameterCount());
30 }
31
32 @Test
33 public void getDeclaredConstructor() throws Exception{
34 //1、获取类对象
35 Class c1 = Student.class;
36 //2、提取类中某个指定的构造器对象【无权限限制】
37 Constructor constructor = c1.getDeclaredConstructor(String.class,int.class);
38 //2.1 获取无参数构造器
39 Constructor constructor2 = c1.getDeclaredConstructor();
40 //打印构造器
41 System.out.println(constructor.getName() + "-->" + constructor.getParameterCount());
42 System.out.println(constructor2.getName() + "-->" + constructor2.getParameterCount());
43 }
44 }
1 import org.junit.Test;
2
3 import java.lang.reflect.Constructor;
4
5 //拿到构造器后创建对象
6 public class TestStudentDemo2 {
7 @Test
8 public void getDeclaredConstructor() throws Exception{
9 Class c = Student.class;
10 Constructor constructor = c.getDeclaredConstructor();
11 //如果构造器私有则打开权限【暴力反射】<仅仅打开这一次>
12 constructor.setAccessible(true);
13 Student student = (Student) constructor.newInstance(); //返回Student类型的对象
14 System.out.println(student);
15
16 //公开的构造器无需打开权限
17 Constructor constructor1 = c.getDeclaredConstructor(String.class,int.class);
18 Student student1 = (Student) constructor1.newInstance("大熊", 6);
19 System.out.println(student1);
20 }
21 }
1 public class Student {
2 private String name;
3 private int age;
4 public static String schoolName;
5 public static final String COUNTRY = "China";
6
7 public Student() {
8 }
9
10 public Student(String name, int age) {
11 this.name = name;
12 this.age = age;
13 }
14
15 public String getName() {
16 return name;
17 }
18
19 public void setName(String name) {
20 this.name = name;
21 }
22
23 public int getAge() {
24 return age;
25 }
26
27 public void setAge(int age) {
28 this.age = age;
29 }
30
31 public static String getSchoolName() {
32 return schoolName;
33 }
34
35 public static void setSchoolName(String schoolName) {
36 Student.schoolName = schoolName;
37 }
38
39 @Override
40 public String toString() {
41 return "Student{" +
42 "name='" + name + '\'' +
43 ", age=" + age +
44 '}';
45 }
46 }
1 import org.junit.Test;
2
3 import java.lang.reflect.Field;
4
5 public class FieldDemo1 {
6 @Test
7 public void getDeclaredFields(){
8 //获取Class类对象
9 Class c = Student.class;
10 //获取全部成员变量
11 Field[] fields = c.getDeclaredFields();
12 //遍历数组
13 for (Field field : fields) {
14 System.out.println(field.getName() + "-->" + field.getType());
15 }
16 }
17
18 @Test
19 public void getDeclaredField() throws Exception{
20 //获取Class类对象
21 Class c = Student.class;
22 //获取某个指定的成员变量
23 Field field = c.getDeclaredField("name");
24 field.setAccessible(true);
25 System.out.println(field.getName() + "-->" + field.getType());
26
27 //赋值
28 Student student = new Student();
29 field.set(student,"张楠");
30 System.out.println(student);
31
32 //取值
33 String name = (String) field.get(student);
34 System.out.println(name);
35 }
36 }
public class Dog {
private String name;
public Dog(){
}
public Dog(String name){
this.name = name;
}
public void eat(){
System.out.println("狗吃骨头");
}
private String eat(String name){
System.out.println(name + "吃骨头");
return "吃的很开心";
}
public static void inAddress(){
System.out.println("北京市朝阳区平安街32号宠物诊所");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1 import org.junit.Test;
2
3 import java.lang.reflect.Method;
4
5 public class MethodDemo {
6 @Test
7 public void getDeclaredMethods(){
8 //获取类对象
9 Class d = Dog.class;
10 //提取全部的方法【包括私有的】
11 Method[] methods = d.getDeclaredMethods();
12 //遍历全部方法
13 for (Method method : methods) {
14 //拓展:方法的名称、返回值类型
15 System.out.println("方法名:" + method.getName() + "-->" + "返回值类型:" + method.getReturnType()
16 + ",参数个数:" + method.getParameterCount());
17 }
18 }
19 @Test
20 public void getDeclaredMethod() throws Exception{
21 Class d = Dog.class;
22 //拿到某个指定的方法
23 Method method = d.getDeclaredMethod("eat"); //拿到无参构造器
24 Method method1 = d.getDeclaredMethod("eat", String.class); //拿到有参构造器
25
26 //触发方法的执行
27 Dog dog = new Dog();
28 Object ret = method.invoke(dog);
29 System.out.println(ret);
30
31 method1.setAccessible(true);
32 Object ret2 = method1.invoke(dog,"小汪");
33 System.out.println(ret2);
34 }
35 }
- 反射的作用:
一、绕过编译阶段为集合添加数据
- 反射是作用在运行时的技术,此时集合的泛型将不能产生数据了,此时可以为集合存入其它任意类型的元素的。
- 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段但的时候,其真实类型都是ArrayList了,泛型相当于被擦出了。
1 import java.lang.reflect.Method;
2 import java.util.ArrayList;
3
4 //反射实现绕过编译阶段在集合中添加数据
5 public class ReflectDemo {
6 public static void main(String[] args) {
7 ArrayList<String> list = new ArrayList<>();
8 ArrayList<Integer> list2 = new ArrayList<>();
9 System.out.println(list.getClass());
10 System.out.println(list2.getClass());
11
12 System.out.println(list.getClass() == list2.getClass());
13
14 list.add("张一栋");
15 list.add("李斯");
16 // list.add(32); //报错
17
18 //获取list的Class类对象
19 Class c = list.getClass();
20 //public boolean add(E e)
21 //定位c类中的add方法
22 try { //方法名 任意类型
23 Method addMethod = c.getDeclaredMethod("add",Object.class);
24 addMethod.invoke(list,32);
25 System.out.println(list);
26 } catch (Exception e) {
27 e.printStackTrace();
28 }
29
30 //其实,不使用反射也是可以做到的
31 ArrayList list3 = new ArrayList<>();
32 list3 = list;
33 list3.add(89);
34 System.out.println(list3);
35 }
36 }
- 案例:反射做通用框架
- 需求:给你任意一个对象,在不清楚对象字段的情况可以把对象的字段和对应值存储到文件中去
分析:
- 定义一个方法,可以接受任意对象
- 每次收到一个对象,解析这个对象的全部成员变量名称
- 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量呢?【只有反射可以解决】
- 使用反射获取对象的Class类对象,然后获取全部成员变量信息
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值
- 存入成员变量名称和值到文件中去即可
两个类就只截取较为关键的字段信息
1 import java.io.FileOutputStream;
2 import java.io.PrintStream;
3 import java.lang.reflect.Field;
4
5 public class MybatisUtil {
6 public static void save(Object obj){
7 try(
8 FileOutputStream fileOutputStream = new FileOutputStream("junit-reflect-annotation-proxy-app/src/data.txt",true);
9 PrintStream printStream = new PrintStream(fileOutputStream);
10 ){
11 //提取这个对象的全部成员变量【只有反射可以解决】
12 Class c = obj.getClass();
13 //获取当前类名
14 System.out.println("============" + c.getSimpleName() + "===============");
15 //获取它的全部成员变量对象
16 Field[] fields = c.getDeclaredFields();
17 //获取成员变量的信息
18 for (Field field : fields) {
19 //字段名
20 String name = field.getName();
21 //属性
22 /** 源码解释
23 * Returns:
24 * the value of the represented field in object obj;
25 * 对象obj中表示字段的值
26 */
27 field.setAccessible(true); //暴力反射
28 String value = field.get(obj) + ""; //都要存入文件中,将其变为字符串即可
29 printStream.println(name + "==" + value);
30 System.out.println(name + "==" + value);
31 }
32 } catch (Exception e) {
33 e.printStackTrace();
34 }
35 }
36 }
1 //提供通用框架可以保存所有对象的具体信息
2 public class ReflectDemo {
3 public static void main(String[] args) {
4 Student student = new Student("老六",36,'男',"大二管理学三班",176);
5 MybatisUtil.save(student);
6 Teacher teacher = new Teacher("张梅",32,"2015060213");
7 MybatisUtil.save(teacher);
8 }
9 }
- 注解
- 自定义注解
- 注解解析的案例
步骤:
- 定义注解Book,要求如下:包含属性(String title 书名、double price 价格、String[ ] authors 作者),限制注解的使用范围:类、成员方法上,指定注解的有效范围:RUNTIME
- 定义BookStore类,在类和成员方法上使用Book注解
- 定义AnnotationDemo3测试类获取Book注解上的数据
1 import java.lang.annotation.ElementType;
2 import java.lang.annotation.Retention;
3 import java.lang.annotation.RetentionPolicy;
4 import java.lang.annotation.Target;
5
6 @Target({ElementType.TYPE,ElementType.METHOD})
7 @Retention(RetentionPolicy.RUNTIME)
8 public @interface BookDemo {
9 String title();
10 double price() default 100;
11 String[] authors();
12 }
1 @BookDemo(title = "《平凡的世界》",price = 45.5,authors = {"路遥","Tom"})
2 public class BookStore {
3 @BookDemo(title = "《活着》",price = 25.5,authors = {"余华"})
4 public void delete(){
5
6 }
7 }
1 import org.junit.Test;
2
3 import java.lang.reflect.Method;
4 import java.util.Arrays;
5
6 public class AnnotationDemo3 {
7 @Test
8 public void parseClass() throws Exception{
9 //得到类对象
10 Class c = BookStore.class;
11 //判断是否存在注解
12 if (c.isAnnotationPresent(BookDemo.class)){
13 //获取注解对象
14 BookDemo bookDemo = (BookDemo) c.getDeclaredAnnotation(BookDemo.class); //拿子类独有的属性
15 System.out.println(bookDemo.title());
16 System.out.println(Arrays.toString(bookDemo.authors()));
17 System.out.println(bookDemo.price());
18
19 System.out.println("-----------拿方法-----------");
20
21 Method method = c.getDeclaredMethod("delete");
22 BookDemo bookDemo2 = method.getDeclaredAnnotation(BookDemo.class); //拿子类独有的属性
23 System.out.println(bookDemo2.title());
24 System.out.println(Arrays.toString(bookDemo2.authors()));
25 System.out.println(bookDemo2.price());
26 }
27 }
28 }
- 注解的应用场景:模拟Junit框架
需求:定义若干个方法,只要加了MyTest注释,就可以在启动时被触发执行
- 自定义一个注解MyTest,只能注解方法,存活范围为一致都在
- 定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行
1 import java.lang.reflect.Method;
2
3 //模拟Junit框架
4 public class SimulationJunitDemo {
5 @MyTest
6 public void test1(){
7 System.out.println("=======test1=========");
8 }
9
10 public void test2(){
11 System.out.println("=======test2=========");
12 }
13 @MyTest
14 public void test3(){
15 System.out.println("=======test3=========");
16 }
17
18 //启动有被注解标志的
19 public static void main(String[] args) throws Exception{
20 SimulationJunitDemo sjd = new SimulationJunitDemo();
21 //1、获取类对象
22 Class c = SimulationJunitDemo.class;
23 //2、获取所有的方法
24 Method[] methods = c.getDeclaredMethods();
25 //3、遍历,选择具有注解标志的方法执行
26 for (Method method : methods) {
27 if (method.isAnnotationPresent(MyTest.class)){
28 method.invoke(sjd);
29 }
30 }
31 }
32 }
- 动态代理
1 //接口:定义明星必须有表演、唱歌的技能
2 public interface Skill {
3 void act(); //演戏
4 void sing(); //唱歌
5 }
1 //实现类,创建具体的明星对象,改明星类里必须实现接口定义的方法
2 public class Star implements Skill{
3
4 private String name;
5 public Star(String name){
6 this.name = name;
7 }
8
9 @Override
10 public void act() {
11 System.out.println(name + "演戏");
12 }
13
14 @Override
15 public void sing() {
16 System.out.println(name + "唱歌");
17 }
18 }
1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public class StarAgent {
6 //设计一个方法返回一个明星对象的代理对象
7 public static Skill getProxy(Star star){
8 /*
9 * public static Object newProxyInstance(
10 * ClassLoader loader, 类加载器【底层会用自己生成的类加载器加载代理类】
11 Class<?>[] interfaces, 对象实现的接口【对象必须要做的行为在其接口中,用于告诉代理具体执行那些方法】
12 InvocationHandler h) 代理的核心处理逻辑
13 * */
14 return (Skill) Proxy.newProxyInstance(star.getClass().getClassLoader(),
15 star.getClass().getInterfaces(), new InvocationHandler() {
16 @Override
17 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
18 System.out.println("对接额外工作,如收首付款");
19 //让明星真正地行使自己的技能:演戏、唱歌
20 //method : 表示正在调用的方法
21 Object ret = method.invoke(star,args); //对象:star、 args:实现类中方法的参数
22 System.out.println("对接额外工作完成,收尾款");
23 return ret;
24 }
25 });
26 }
27 }
1 //学习开发出一个代理对象、理解动态代理的执行流程
2 public class Test {
3 public static void main(String[] args) {
4 //1、创建要给对象,对象必须实现接口
5 Star star = new Star("胡歌");
6 //为star对象找一个dialing对象【经纪人】
7 Skill star2 = StarAgent.getProxy(star);
8
9 star2.act();
10 star2.sing();
11 }
12 }
这样,就实现了功能的分化与细化,即胡歌只用关注自己本身的技能 - - 表演和唱歌,而经纪人负责对其额外的工作。
- 案例:模拟企业业务开发,并完成每个功能的性能统计
- 模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询等功能,并要统计每个功能的耗时
分析:
- 定义一个UserService表示用户业务接口,规定必须完成用户登录、删除、查询等功能
- 定义一个实现类UserServiceImpl实现UserService,并完成相关功能,且统计每个功能的耗时
- 定义测试类,创建类对象,调用方法
1 public interface UserService {
2 String login(String loginName, String password);
3 void deleteUser();
4 String selectUsers();
5 }
1 //使用代理,可以将注释部分的(即相同业务逻辑功能的)代码交给代理(工具类)来处理
2 //可以看出,代理对于做通用功能的框架技术是很有优势的
3 public class UserServiceImpl implements UserService{
4 @Override
5 public String login(String loginName, String password) {
6 // long startTime = System.currentTimeMillis();
7 System.out.println("正在登录中...");
8 String str = "登录失败";
9 if ("admin".equals(loginName) && "123".equals(password)){
10 str = "登录成功";
11 }
12 try {
13 Thread.sleep(1500);
14 } catch (Exception e) {
15 e.printStackTrace();
16 }
17 // long endTime = System.currentTimeMillis();
18 System.out.println("登录方法调用完毕......");
19 // System.out.println("login方法耗时:" + (endTime - startTime) / 1000.0 + "s");
20 return str;
21 }
22
23 @Override
24 public void deleteUser() {
25 // long startTime = System.currentTimeMillis();
26 System.out.println("正在删除用户中......");
27 try {
28 System.out.println("正在删除用户~");
29 Thread.sleep(2500);
30 } catch (Exception e) {
31 e.printStackTrace();
32 }
33 System.out.println("删除用户操作完成......");
34 // long endTime = System.currentTimeMillis();
35 // System.out.println("deleteUser方法耗时:" + (endTime - startTime) / 1000.0 + "s");
36 }
37
38 @Override
39 public String selectUsers() {
40 // long startTime = System.currentTimeMillis();
41 System.out.println("正在查询中...");
42 String str = "登录失败";
43 try {
44 Thread.sleep(3000);
45 } catch (Exception e) {
46 e.printStackTrace();
47 }
48 System.out.println("查询方法调用完毕......");
49 // long endTime = System.currentTimeMillis();
50 // System.out.println("selectUsers方法耗时:" + (endTime - startTime) / 1000.0 + "s");
51 return str;
52 }
53 }
1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public class ProxyUtil {
6 //通过一个静态方法为用户的业务对象返回代理对象
7 public static UserService getProxy(UserServiceImpl userService){
8 return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
9 userService.getClass().getInterfaces(), new InvocationHandler() {
10 @Override
11 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
12 long startTime = System.currentTimeMillis();
13 Object obj = method.invoke(userService,args);
14 long endTime = System.currentTimeMillis();
15 System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s") ;
16 return obj;
17 }
18 });
19 }
20 }
1 public class Test {
2 public static void main(String[] args) {
3 // UserService userService = new UserServiceImpl(); //多态
4 // System.out.println(userService.login("admin", "123"));
5 // userService.deleteUser();
6 // userService.selectUsers();
7
8 UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
9 userService.login("admin","123");
10 userService.deleteUser();
11 userService.selectUsers();
12 }
13 }
使用泛型可以接收任意类型的实现类对象做代理: