所有笔记个人从以下视频总结得到 https://www.bilibili.com/video/BV1J4411877m
本文转自 个人博客
JUnit是一个Java语言的单元测试框架,Unit有它自己的JUnit扩展生态圈,多数Java的开发环境都已经集成了JUnit作为单元测试的工具。
Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了。
JUnit
1、定义测试类:
测试类名:ClassNameTest
包名:xxx.xxx.test
2、定义方法:
方法名:testMethodName()
返回值:void
参数列表:空参
3、方法添加 @Test
注解,并且导入 JUnit 环境
4、判断测试结果,第一个参数为期望结果,第二个参数为测试结果
Assert.assertEquals(expectedValue, testResult);
6、初始化测试资源与关闭资源,前者依靠 @Before
注解,后者依赖 @After
注解
/* 在测试之前会执行,无论测试是否通过 */
@Before
public void init() {
// Initialization...
}
/* 在测试之后会执行,无论测试是否通过 */
@After
public void close() {
// Termination...
}
反射
1、反射带来的好处:
a. 程序运行中操作对象
b. 解耦,提高扩展性
2、获取 Class 对象方法
Class.forName("全类名"); // 将字节码文件加载进内存,返回class对象
类名.class; // 通过类名的属性class获取
对象.getClass();
第一种方式多用于配置文件,把类名信息放到一个文件中
第二种方式多用于传参
第三种方式就是单纯获取字节码对象
3、Class 类中的方法
这里不废话,主要就是各种 get 方法,会获取到对应的 Field
,Constructor
,和 Method
等数组或对象,然后调用其内部的一些方法
先说 Field:
获取公开的内容:
classObj.getXXX();
获取到全部内容:
classObj.getDeclaredXXX();
忽略访问权限:
classObj.setAccessible(true);
然后是 Constructor:
获取到指定的构造函数:
classObj.getConstructor(String.class, int.class); // 参数传形参的字节码文件
利用构造函数对象创建对象:
constructor.newInstance(...);
/* 获取到的 constructor 是什么传参顺序,这里就用什么顺序
* 比如上面的是先 String,然后再 int,那这里也按照这个顺序填写
* (空参构造一般用 classObj.newInstance() 来创建对象)
*/
其他的 declared 的,也可以通过 setAccessible
来使用到,这里就不废话了
最后是 Method:
获取方法对象:
classObj.getMethod("methodName", ...); // 后面方法对应的参数的字节码文件
调用方法:
method.invoke(obj, ...);
/* 这里,method 是通过字节码获取到的 Method 对象
* 第一个参数为需要调用该方法的实例对象,后面则可以传入实际的参数内容
*/
反射小案例
利用反射来实现一个可以创建任意类,并且可以调用其内部任意方法的小框架
package com.guanyu.reflect;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Properties pro = new Properties();
// 通过 ClassLoader 来获取配置文件路径,并直接转换成 Stream
ClassLoader clsLoader = ReflectTest.class.getClassLoader();
pro.load(clsLoader.getResourceAsStream("pro.properties"));
// 获取类名和方法名
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
// 创建 Class 对象,创建 Method 对象
Class cls = Class.forName(className);
Method m = cls.getMethod(methodName);
// 创建实例对象,并通过 Method 对象来调用该实例对象的方法
Object o = cls.newInstance();
m.invoke(o);
}
}
注解
概念∶说明程序的。给计算机看的
注释∶用文字描述程序的。给程序员看的
注解(Annotation),也叫元数据。一种代码级别的说明。它是JD1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元秦进行说明,注释。
作用:
- 编译检查:让编译器实现基本的编译检查功能(e.g @Override)
- 编写文档:生成 Java 文档所需要的注解 (e.g @author)
- 代码分析:对代码进行分析(e.g 使用反射)
JDK常见自带
检查该方法是够是继承自父级
@Override
久过时弃用的内容,在前面用 @Deprecated 进行注解
@Deprecated
可以压住一些不必要的黄色警告,可以传入一个 all 作为参数
@SuppressWarnings
自定义注解
注解本质是一个 interface,其继承自 java.lang.annotation.Annotation
public interface MyAnno extends java.lang.annotation.Annotation {}
注解格式:
元注解
public @interface 注解名称 {
属性列表;
}
其中注解列表内的返回值类型可以是 Primitive Type,String,Enum,Annotation,以及上述数组。
public @interface MyAnno {
int age();
String name() default "张三";
Person per(); // 这是个枚举类
MyAnno2 anno2();
String[] strs();
}
在使用自己的注解的时候,可以选择赋值,或者在一开始给一个 default 值
@MyAnno(age = 12, name = "李四", ...) // 后面就不展开写了
class Worker {}
当属性列表里,只有一条属性且该属性名叫 value
的时候,赋值时不需要写属性名
元注解
元注解就是解释注解的注解,是 Java 内置的一些固定的注解,比如下面几个例子:
@Target:描述注解能够作用的位置
这个元注解里,有个叫 value() 的 ElementType[] 的一个数组,其中 ElementType 本身是一个枚举;这里要求我们传值的,用来标记该注解能作用在哪些地方。常见的几个取值有:
- TYPE:可以作用在类上
- METHOD:可以作用在方法上
- FIELD:可以作用在成员变量上
// 这里的含义就是该注解可以注解在类上和方法上,另外该处 value 可以省略
@Target(value = {ElementType.TYPE, ElementType.METHOD})
public @interface MyAnno {}
@Retention:描述注解被保留的阶段
这个元注解,和上面的差不多,有一个叫 RetentionPolicy 的枚举类,在使用这个注解的时候,只传一个值,此处不是 RetentionPolicy[],这里有三个值可以填写:
- SOURCE:只在源码阶段有效
- CLASS:一直到字节码期间都有效
- RUNTIME:一直到运行时都有效
// 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {}
@Documented:描述注解是否被抽取到 api 文档中
@Inherited:描述注解是否被子类继承,比如注解描述了 A,然后 B 继承了 A,如果 A 的注解中有该项,则 B 也会继承表述 A 的注解
注解的使用(解析)
下面的第二步骤中,是使用了 getAnnotation()
方法,而不是 getAnnotations()
; 前者可以通过字节码文件获取到特定的注解,后者是获取到所有的
@Pro(className = "com.guanyu.annotation.Demo1", methodName = "show")
public class Reflection {
public static void main(String[] args) {
// 1. 获取该类字节码文件
Class<Reflection> reflectionClass = Reflection.class;
// 2. 通过字节码文件获取到注解对象
Pro an = reflectionClass.getAnnotation(Pro.class);
// 3. 调用注解对象中的抽象方法,获取返回值
String className = an.className();
String methodName = an.methodName();
// (下面略过,见反射小案例)
// ...
}
}
另外,这个注解对象,是在 reflectionClass.getAnnotation()
的时候,创建了一个对象出来,其中有它的方法:
public class ProImpl implements Pro{
public String className(){
// 但是这里不知道这个值从哪里来,感觉似乎是
// 一种封装,或者是一种反射获取到的值
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
小案例
简单的测试框架
CheckAnnotation:
package com.guanyu.annotation;
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 Check {
}
Calculator Class:
package com.guanyu.annotation;
public class AnnCalculator {
@Check
public void add() {
System.out.println("1 + 0 = " + (1 + 0));
}
@Check
public void sub() {
System.out.println("1 - 0 = " + (1 - 0));
}
@Check
public void mul() {
System.out.println("1 * 0 = " + (1 * 0));
}
@Check
public void div() {
System.out.println("1 / 0 = " + (1 / 0));
}
public void show() {
System.out.println("show ...");
}
}
CheckTest:
package com.guanyu.annotation;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* 简单的测试框架
*
* 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,记录到文件中
*/
public class TestChek {
public static void main(String[] args) throws IOException {
// 1. 先创建对象
AnnCalculator c = new AnnCalculator();
Class cls = c.getClass();
// 2. 通过字节码对象获取所有方法
Method[] methods = cls.getMethods();
// 3. 遍历所有方法,如果该方法有对应的注解,则执行
int error = 0;
BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"));
for (Method m : methods) {
if (m.isAnnotationPresent(Check.class)) {
try {
m.invoke(c);
} catch (Exception e) {
// 4. 捕获异常,并且记录到文件中
error++;
bw.write("[ " + m.getName() + " ]" + " 方法出现异常");
bw.newLine();
bw.write("异常名称:" + e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常原因:" + e.getCause().getMessage());
bw.newLine();
bw.write("---------------------------------");
bw.newLine();
}
}
}
bw.write("本次测试共出现异常次数:" + error);
bw.flush();
bw.close();
}
}
小结
小结:
- 以后大多数时候,我们会使用注解,而不是自定义注解
- 注解给谁用?
- 编译器
- 给解析程序用
- 注解不是程序的一部分,可以理解为注解就是一个标签