【自用】JavaSE--junit单元测试、反射、注解、动态代理
junit单元测试
Java中的最小功能单元是方法
具体步骤:
必须声明@Test注解,输入后同时按下alt+回车,选择合适的版本即可(这里选择4)
测试出bug示例:
暂无bug示例:
断言机制
程序员可以通过自带的 Assert类中的方法 预测业务结果,只要预测值与实际值不同,就会认定出现bug
示例如下图,方法中直接返回字符串的长度,有bug出现
常见注解
温故知新 ---> 静态方法与实例方法
- 静态方法:要用static修饰,不需要创建对象就能使用
- 实例方法:无需static修饰,创建之后需要新建对象调用
示例代码(在上面的代码下加入):
@BeforeClass
public static void testBeforeClass(){System.out.println("--------BeforeClass--------\r\n");
}@AfterClass
public static void testAftertClass(){System.out.println("--------AfterClass--------");
}@Before
public void testBefore(){System.out.println("-----before执行了-----");
}@After
public void testAfter(){System.out.println("-----After执行了-----\r\n");
}
运行结果:
反射
获取类
反射第一步:获取类的class对象
示例:
//第一种方法: 类名.class
Class c1 = Student.class;
System.out.println(c1.getName());//全类名
System.out.println(c1.getSimpleName());//简名//第二种方法: Class.forName(全类名)
Class c2 = Class.forName("com.CJ.reflect_test.Student");//与c1是一样的,指向同一个对象
System.out.println(c1==c2);//第三种方法: 先创建对象,再利用对象的方法获得 对象名.getClass()
Student stu = new Student();
Class c3 = stu.getClass(); //c1 c2 c3 均指向同一个对象
System.out.println(c3==c2);
获取类的构造器
示例:
public class test {public static void main(String[] args) throws Exception {//第一步:先获取类的class对象Class c = Student.class;//获取全部构造器: 采用c.getConstructors仅能获取public修饰的构造器,下面使用的方法可以获取全部构造器Constructor[] constructors = c.getDeclaredConstructors();for(Constructor cons : constructors){System.out.println(cons.getName()+"构造器共"+cons.getParameterCount()+"个参数");}System.out.println("----------");//获取单个构造器: 需要将参数一一对应Constructor c1 = c.getDeclaredConstructor(String.class, String.class, double.class);System.out.println("获取有参构造器:"+c1.getName()+"共"+c1.getParameterCount()+"个参数");Constructor c2 = c.getDeclaredConstructor();System.out.println("获取无参构造器:"+c2.getName()+"共"+c2.getParameterCount()+"个参数");}
}//学生类如下
public class Student {String name;String id;double score;public Student() {}public Student(String name, String id, double score) {this.name = name;this.id = id;this.score = score;}
}
获取类的构造器后,可以使用 newInstance 方法创建对象(至于为什么会这么做,以后慢慢就懂了,现在先知道有这么个玩法)
setAccessible(true) 可以强制绕过访问控制(暴力反射)
示例:
public class test {public static void main(String[] args) throws Exception {//第一步:先获取类的class对象Class c = Student.class;//获取有参构造器Constructor c1 = c.getDeclaredConstructor(String.class, String.class, double.class);//创建对象,由于获取的构造器c1并不知道自己是什么类型,所有new出来的对象是Object类型,需强转c1.setAccessible(true);//由于有参构造器是private,无法正常访问,通过该方法可强制绕过访问控制Student s1 = (Student)c1.newInstance("大肥猪","666",30);System.out.println(s1);//获取无参构造器Constructor c2 = c.getDeclaredConstructor();Student s2 = (Student)c2.newInstance();}
}//学生类如下
public class Student {String name;String id;double score;public Student() {System.out.println("无参构造器执行了");}private Student(String name, String id, double score) {this.name = name;this.id = id;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", id='" + id + '\'' +", score=" + score +'}';}
}
获取成员变量
整体方法、特点与获取构造器差不多
代码示例:
public class test {public static void main(String[] args) throws Exception {//反射第一步:先获取类的class对象Class c = Student.class;//获取全部成员变量Field[] fields = c.getDeclaredFields();for(Field f : fields){System.out.println(f.getName()+"--->"+f.getType());}System.out.println();//获取单个成员变量Field fName = c.getDeclaredField("name");System.out.println(fName.getName()+"--->"+fName.getType());//为成员变量赋值Student stu = new Student(); //后续对stu赋值fName.setAccessible(true);//name变量是private,采用暴力反射即可访问fName.set(stu,"大肥猪");//设置stu的name为大肥猪System.out.println(stu);}
}//学生类
public class Student {private String name;String id;double score;public Student() {}private Student(String name, String id, double score) {this.name = name;this.id = id;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", id='" + id + '\'' +", score=" + score +'}';}
}
获取成员方法
依然与上面类似,同样支持暴力反射,同样是也是declared取所有
代码示例:
public class test {public static void main(String[] args) throws Exception {//反射第一步:先获取类的class对象Class c = Student.class;//获取全部方法Method[] methods = c.getDeclaredMethods();for(Method m : methods){System.out.println("方法名:"+m.getName()+" 参数个数:"+m.getParameterCount()+" 返回值类型:"+m.getReturnType());}System.out.println();//获取单个方法 括号中需填方法名与参数,无参数则不填Method study1 = c.getDeclaredMethod("study");Method study2 = c.getDeclaredMethod("study", String.class);System.out.println("方法名:"+study1.getName()+" 参数个数:"+study1.getParameterCount()+" 返回值类型:"+study1.getReturnType());System.out.println("方法名:"+study2.getName()+" 参数个数:"+study2.getParameterCount()+" 返回值类型:"+study2.getReturnType());System.out.println();//执行Student stu = new Student();study1.invoke(stu);study2.setAccessible(true);//带参数的study方法为private修饰,需要暴力反射一下study2.invoke(stu,"英语");}
}//学生类
public class Student {private String name;String id;double score;public Student() {}private Student(String name, String id, double score) {this.name = name;this.id = id;this.score = score;}public void eat(){System.out.println("学生吃饭");}public void study(){System.out.println("今天要学习");}private void study(String name){System.out.println("开始学习"+name);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", id='" + id + '\'' +", score=" + score +'}';}
}
反射的作用
温故知新: +" "
应用场景示例
代码示例:
public class test {public static void main(String[] args) throws Exception {Teacher teacher = new Teacher("张三","001");Student student = new Student("小明","202501",98.5);ObjectFrame.saveObeject(teacher);ObjectFrame.saveObeject(student);}
}
public class ObjectFrame {public static void saveObeject(Object obj) throws Exception {//创建IO流,用于写出文件,采用打印流PrintStream ps = new PrintStream(new FileOutputStream("D:\\AJava\\code\\javasepro\\HighLevel_java\\src\\data.txt",true));//获取传入对象的class对象Class c = obj.getClass();//获取全部成员变量Field[] fields = c.getDeclaredFields();//先将类名写入文件,再遍历全部变量,将其写入文件ps.println("------"+c.getSimpleName()+"------");for(Field f : fields){f.setAccessible(true); //暴力反射,private修饰的也能获取String name = f.getName();String value = f.get(obj)+"";ps.println(name + "=" + value);}ps.close();}
}
//学生类
public class Student {private String name;String id;double score;public Student() {}public Student(String name, String id, double score) {this.name = name;this.id = id;this.score = score;}public void eat(){System.out.println("学生吃饭");}public void study(){System.out.println("今天要学习");}private void study(String name){System.out.println("开始学习"+name);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", id='" + id + '\'' +", score=" + score +'}';}
}//教师类
public class Teacher {public String name;private String id;public Teacher() {}public Teacher(String name, String id) {this.name = name;this.id = id;}
}
运行结果:
注解
自定义注解
在新建java是直接选中Annotation,这就是一个注解
示例:
注解的原理
元注解
@Target也可以一次性传入多个参数,比如 @Target({ElementType.TYPE, ElementType.METHOD}) 表示可以在 类、接口、成员方法中使用这个接口,如下图
注解的解析
案例:
代码示例:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation_test3 {String value();double aaa() default 100;String[] bbb();
}
@MyAnnotation_test3(value = "123",aaa = 59.5,bbb = {"我是","GGBond"})
public class Demo {@MyAnnotation_test3(value = "456",bbb = {"我是","一头大肥猪"})public void test1(){}
}
public class test {@Test //解析类的注解public void parseClass() throws Exception {//取得类的class对象Class c = Demo.class;//判断该类上是否有注解if(c.isAnnotationPresent(MyAnnotation_test3.class)){//取到注解,由于这个注解一定是MyAnnotation_test3,可以直接取这个作为类型MyAnnotation_test3 a = (MyAnnotation_test3)c.getDeclaredAnnotation(MyAnnotation_test3.class);System.out.print("value = "+a.value());System.out.print(" aaa = "+a.aaa());System.out.println(" bbb = "+Arrays.toString(a.bbb()));}}@Test //解析方法的注解public void parseMethod() throws Exception {//取得方法的class对象Class c = Demo.class;Method m = c.getMethod("test1");//判断该方法上是否有注解if(m.isAnnotationPresent(MyAnnotation_test3.class)){//取到注解,由于这个注解一定是MyAnnotation_test3,可以直接取这个作为类型MyAnnotation_test3 a = (MyAnnotation_test3)m.getDeclaredAnnotation(MyAnnotation_test3.class);System.out.print("value = "+a.value());System.out.print(" aaa = "+a.aaa());System.out.println(" bbb = "+Arrays.toString(a.bbb()));}}
}
运行结果:
应用场景
应用场景之一:用来标注某个程序,然后让其他程序根据注解信息来决定怎么对待他们
案例:利用注解与反射做框架,是否有注解就代表着是否执行
代码示例:
@Target(ElementType.METHOD)//只有方法能用
@Retention(RetentionPolicy.RUNTIME)//一直存在至程序结束
public @interface Mytest {
}
public class Myjunit_test {@Mytestpublic void test1(){System.out.println("---test1---");}//@Mytestpublic void test2(){System.out.println("---test2---");}//@Mytestpublic void test3(){System.out.println("---test3---");}@Mytestpublic void test4(){System.out.println("---test4---");}public static void main(String[] args) throws Exception {//执行方法的invoke方法必须调入对象,因此先创建一个Myjunit_test a = new Myjunit_test();//先利用反射取到class对象Class c = Myjunit_test.class;//取得所有方法Method[] methods = c.getDeclaredMethods();//遍历所有方法,有@MyTest注解的就执行for (Method method : methods) {if (method.isAnnotationPresent(Mytest.class)){//执行a中的method方法method.invoke(a);}}}
}
运行结果:
动态代理
创建代理的方法:
代码示例:
public class ProxyUtil {//代理创建代理public static Star createProxy(BigStar bigStar){/*newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)第一个参数:类加载器,用来加载类,写法一般是固定的,用当前类的类加载器第二个参数:指定生成的代理长什么样,也就是代理有那些方法,目前只有一个Star接口(也有可能有多个接口来说明)说明有sing与dance方法,只要将其包装成只有一个的数组即可第三个参数:指定的代理对象要做什么事情,使用匿名内部类的方式创建后重写invoke方法假设之后在主程序会写下面两行代码:Star starProxy = ProxyUtil.createProxy(s);starProxy.sing("好日子");*/Star starProxy = (Star)Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class}, new InvocationHandler() {@Override//第一个参数:传进来的代理 第二个参数:方法 第三个参数:方法中的参数//以starProxy.sing("好日子")为例,sing会调用下面的invoke方法//第一个参数:starProxy 第二个参数:sing 第三个参数:"好日子"public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//代理要做的事写在这里if(method.getName().equals("sing")){System.out.println("准备场地,收钱50");}else if(method.getName().equals("dance")){System.out.println("准备场地,收钱100");}//这里的invoke是反射的invoke,需要传入执行该方法的对象与需要传入该方法的参数return method.invoke(bigStar,args);}});return starProxy;}
}
//主函数
public class Test {public static void main(String[] args) {//创建明星BigStar star = new BigStar("GGbond");//创建代理Star bigStar = ProxyUtil.createProxy(star);String rs = bigStar.sing("奇迹再现");System.out.println(rs);bigStar.dance();}
}
//明星接口
public interface Star {public String sing(String name);public void dance();
}
//巨星类,实现明星接口
public class BigStar implements Star{String name; //明星的名字public BigStar(String name) {this.name = name;}public String sing(String name){System.out.println(this.name+"正在唱:"+name);return "谢谢大家!";}public void dance(){System.out.println(this.name+"正在跳舞");}
}
运行结果:
案例:
代码:
//代理
public class ProxyUtil_mine {public static UserService createProxy(UserServiceImpl userService){UserService service = (UserService) Proxy.newProxyInstance(ProxyUtil_mine.class.getClassLoader(),new Class[]{UserService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();Object us=null;if(method.getName().equals("login") || method.getName().equals("deleteUsers") || method.getName().equals("selectUsers")){us = method.invoke(userService,args);}else{us = method.invoke(userService,args);}long endTime = System.currentTimeMillis();System.out.println(method.getName()+"方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");return us;}});return service;}
}
//接口
public interface UserService {// 登录功能void login(String loginName,String passWord) throws Exception;// 删除用户void deleteUsers() throws Exception;// 查询用户,返回数组的形式。String[] selectUsers() throws Exception;
}
//接口的实现类
//将所有算时间的代码全都交给代理来做了
public class UserServiceImpl implements UserService{@Overridepublic void login(String loginName, String passWord) throws Exception {//long startTime = System.currentTimeMillis();if("admin".equals(loginName) && "123456".equals(passWord)){System.out.println("您登录成功,欢迎光临本系统~");}else {System.out.println("您登录失败,用户名或密码错误~");}Thread.sleep(1000);//long endTime = System.currentTimeMillis();//System.out.println("方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");}@Overridepublic void deleteUsers() throws Exception{//long startTime = System.currentTimeMillis();System.out.println("成功删除了1万个用户~");Thread.sleep(1500);//long endTime = System.currentTimeMillis();//System.out.println("方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");}@Overridepublic String[] selectUsers() throws Exception{//long startTime = System.currentTimeMillis();System.out.println("查询出了3个用户");String[] names = {"张全蛋", "李二狗", "牛爱花"};Thread.sleep(500);//long endTime = System.currentTimeMillis();//System.out.println("方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");return names;}
}
//主函数
public class Test {public static void main(String[] args) throws Exception{// 1、创建用户业务对象。//仅需修改下面一行的代码//UserService userService = new UserServiceImpl();UserService userService = ProxyUtil_mine.createProxy(new UserServiceImpl());// 2、调用用户业务的功能。userService.login("admin", "123456");System.out.println("----------------------------------------------------");userService.deleteUsers();System.out.println("----------------------------------------------------");String[] names = userService.selectUsers();System.out.println("查询到的用户是:" + Arrays.toString(names));System.out.println("----------------------------------------------------");}
}