Junit单元测试
1.基本概念
单元:在Java中,一个类就是一个单元
单元测试:用来对某个类中的某个方法进行功能测试或业务逻辑测试
Junit单元测试框架作用:
用来对类中的方法功能进行有目的的测试,以保证程序的正确性和稳定性
能够独立的测试某个方法或者所有方法的正确性
2.使用步骤
1.模拟业务代码
2.写测试类。测试类命名以Test结尾,以业务类类名开头
3.在测试类中些方法。测试方法的命名以test开头,以业务方法结尾
4.测试方法注意事项:必须是public修饰,没有返回值,没有参数,必须用@Test注解修饰
3.Junit常用注解
@Test:测试方法
@BeforeEach:用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次
@AfterEach:用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次
@BeforeAll:用来静态修饰方法,该方法会在每一个测试方法执行之前执行一次
@AfterAll:用来静态修饰方法,用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次
反射
1.基本概念
反射机制:将类的各个组成部分封装为其他对象
好处:
1.可以在程序的运行过程中操作这些对象
2.可以解耦,提高程序的可扩展性
对于任何一个类,在运行的时候都可以直接得到这个类全部成分
构造器对象(Constructor),成员变量对象(Field),成员方法对象(Method)
注意:反射是工作在运行时的技术,因为只有运行之后才会有class类对象
总结:
反射的核心思想和关键就是得到编译以后的class文件对象
反射是在运行时获取类的字节码文件对象,然后可以解析类中的全部成分
反射是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
2.获取Class类对象
三种方式:
类名.Class
类对象.getClass()
Class.forName(“类的全限名”)
public class Demo01 {
public static void main(String[] args) throws Exception{
//类名.Class
Class c1=Person.class;
//对象名.getClass()
Person p1=new Person();
Class c2=p1.getClass();
//Class.forName("类的权限名")
Class c3=Class.forName("demo01.Person");
System.out.println(c1.getSimpleName()); //获取类本身
System.out.println(c1.getName()); //获取类的全限名
}
}
Class本身也是一个类
Class对象只能由系统建立对象
一个加载的类在JVM中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个Class文件
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
3.获取构造器对象
public class TestPerson {
/*1.getConstructors
获取全部的构造器:只能获取public修饰的构造器
Constructor[] getConstructors()
*/
@Test
public void getConstructors(){
//获取Class类对象
Class c=Person.class;
//定位全部构造器,只能拿public修饰
Constructor[] cons=c.getConstructors();
//遍历这些构造器,其中无参构造为私有,所以只能获取一个构造器
for (Constructor con : cons) {
System.out.println(con.getName()+":"+con.getParameterCount());
}
}
/*
获取全部构造器
*/
@Test
public void getDeclaredConstructors(){
Class c=Person.class;
Constructor[] cons=c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con.getName()+":"+con.getParameterCount());
}
}
/*
获取某个构造,只能拿public修饰的某个构造器
*/
@Test
public void getConstructor() throws Exception{
Class c=Person.class;
//定位某个构造器,根据参数匹配,只能拿public修饰的
Constructor con=c.getConstructor(String.class,int.class);
System.out.println(con.getName()+":"+con.getParameterCount());
}
/*
获取某个构造器,无论权限是否可及
*/
@Test
public void getDeclaredConstructor() throws Exception{
Class c=Person.class;
//定位某个构造器,根据参数匹配,只能拿public修饰的
Constructor con=c.getDeclaredConstructor();
System.out.println(con.getName()+":"+con.getParameterCount());
}
}
总结:
获取全部构造器对象:Constructor[] getDeclaredConstructors[]
获取所有声明的构造器,无所谓权限
获取某个构造器:Constructor getDeclaredConstructor
根据参数匹配获取某个构造器,无所谓权限
4.获取Class类的构造器初始化对象
Constructor的API:
1.T newInstance(Object…initargs):创建对象,注入构造器需要的数据
2.void setAccessible(true):修改访问权限,true代表暴力攻破权限,false表示保留不可访问权限
总结:
可以通过定位类的构造器对象
如果构造器对象没有访问权限可以通过void setAccessible(true)打开权限
构造器可以通过T newInstance(Object…initargs)调用自己传入参数
public class TestPerson02 {
//调用无参构造得到一个类对象
@Test
public void createObj01() throws Exception{
Class c=Person.class;
//定位无参构造
Constructor con=c.getDeclaredConstructor();
//打开私有构造器访问权限
con.setAccessible(true);
//通过无参构造器初始化对象返回
Person p1=(Person) con.newInstance();
System.out.println(p1);
}
}
5.获取Class类的成员变量Field
Field getField(String name):根据变量名获取对应的Field对象,只能获得public修饰
Field getDeclaredField(String name):根据变量名获取对应的Field对象,只要声明了即可
Field[] getFields():获取所有成员变量对应的Field对象,只能获得public
Field[] getDeclaredFields():获取所有的成员变量对应的Field对象,只要声明了即可
总结:
获取全部成员变量:getDeclaredFields
获取某个成员变量:getDeclaredField
public class TestPerson03 {
//获取全部成员变量
@Test
public void getDeclaredFields(){
Class c=Person.class;
Field[] fields=c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName()+" "+field.getType());
}
}
//获取特定成员变量
@Test
public void getDeclaredField() throws NoSuchFieldException {
Class c=Person.class;
Field field=c.getDeclaredField("name");
System.out.println(field);
}
}
6.获取成员变量取值与赋值
void set(Object obj,Object value):给对象注入某个成员变量数据
Object get(Object obj):获取对象的成员变量的值
void setAccessible(true):设置为可以直接访问私有类型的属性
Class getType():获取属性的类型,返回Class对象
String getName():获取属性的名称
public class TestPerson04 {
@Test
public void setField() throws Exception {
Class c=Person.class;
//定位成员变量
Field nameF=c.getDeclaredField("name");
//为这个成员变量赋值
Person p=new Person();
nameF.set(p,"Kobe");
//获取成员变量的值
String value=nameF.get(p)+"";
}
}
7.获取Method方法对象
Method getMethod(String name,Class…args):根据方法名和参数类型获得对应的方法对象,只能获得public。获得本类及其父类的全部public方法
Method getDeclaredMethod(String name,Class…args):根据方法名和参数类型获得对应的方法对象,无所谓权限
Method[] getMethods():获得类中所有方法对象,返回数组,只能获得public修饰
Method[] getDeclaredMethods():获得类中所有的成员方法对象,返回数组。只获得本类声明的
public class TestPerson05 {
@Test
public void getDeclaredMethods() throws Exception{
Class c=Person.class;
//获得全部方法
Method[] methods=c.getDeclaredMethods();
//遍历方法
for (Method method : methods) {
System.out.println(method.getName()+" "+method.getParameterCount());
}
}
@Test
public void getDeclaredMethod() throws Exception{
Class c=Person.class;
//定位某个方法
Method run=c.getDeclaredMethod("setName");
}
}
8.类的初始化
类的主动引用(一定会发生类的初始化)
当虚拟机启动,先初始化main方法所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对类进行反射调用
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
当访问一个静态域,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
9.动态创建对象执行方法
步骤
1.通过Class类的getDeclaredConstructor(Class…parameterTypes)取得奔雷的指定形参类型的构造器
2.向构造器的形参中传递一个数组对象进去,里面包含了构造器中所需的各个参数
3.通过Constructor实例化对象
public class Test {
public static void main(String[] args) throws Exception {
//获得Class对象
Class c1 = Class.forName("demo.User");
//构造一个对象
// User user = (User)c1.getDeclaredConstructor().newInstance();
// System.out.println(user);
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
User user1 = (User) constructor.newInstance("Kobe", 24);
System.out.println(user1);
//通过反射调用普通方法
User user2 = (User) c1.getDeclaredConstructor().newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活,(对象,方法的值)
setName.invoke(user2,"Kobe");
System.out.println(user2.getName());
//通过反射操作属性
User user3 = (User) c1.getDeclaredConstructor().newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
name.setAccessible(true);
name.set(user3,"Jordan");
System.out.println(user3.getName());
}
}
setAccessible
Method和Field,Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查(提高反射效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问)
参数值为false则指示反射的对象应该实施Java语言访问检查
10.反射的作用
反射可以破坏封装性,破坏泛型的约束性
Mybatis框架
注解
1.基本概念
注解是用在类上,方法上,成员变量,构造器等等上对成分进行便以约束,标记等操作的
注解相当于一种标记,是类的组成部分,可以给类携带一些额外的信息
作用:可以被其他程序读取
2.基本语法
自定义注解格式:
修饰符 @interface 注解名{//注解属性}
3.注解的属性
属性格式:
数据类型 属性名();
数据类型 属性名() default 默认值;
总结:
注解可以有属性,属性名必须带()
在用注解的时候,属性必须辅助,除非这个属性有默认值
注解的特殊属性:value
value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写
但是如果有多个属性,且多个属性没有默认值,那么value是不能省略的
4.元注解
@Target
作用:用来表示注解使用的位置,如果没有使用该注解表示,则自定义的注解可以使用在任何位置
可使用的值定义在ElementType枚举类中,常用值如下:
TYPE,类,接口
FIELD,成员变量
METHOD,成员方法
PARAMETER,方法参数
CONSTRUCTOR,构造器
LOCAL_VARIABLE,局部变量
@Retention
作用:用来表示注解的生命周期
可使用的值定义在RetentionPolicy枚举类中,常用值如下
SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIOME:注解作用在源码阶段,字节码文件阶段,运行阶段
总结
@Target约束自定义注解可以标记的范围
@Retention用来约束自定义注解的存货范围
5.注解的解析
public class Demo01 {
@Test
public void pareClass() throws NoSuchMethodException {
//定位Class对象
Class c=BookStore.class;
//定位方法对象
Method run=c.getDeclaredMethod("run");
//判断类上是否使用了某个注解
if(c.isAnnotationPresent(Book.class)){
//获取注解对象
Book book = (Book) c.getDeclaredAnnotation(Book.class);
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
//判断方法上是否使用了某个注解
if(run.isAnnotationPresent(Book.class)){
//获取注解对象
Book book = (Book) run.getDeclaredAnnotation(Book.class);
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
}
@Book(value = "JAVA",price = 200,authors = {
"author1","author2"})
class BookStore{
@Book(value = "C++",price = 150,authors = {
"author3","author4"})
public void run(){
}
}
@Target({
ElementType.TYPE,ElementType.METHOD})//类和成员方法上使用
@Retention(RetentionPolicy.RUNTIME) //注解永久存活
@interface Book{
String value();
double price() default 100;
String[] authors();
}
注解解析原理:
注解在哪个成分上,我们就先拿哪个成分对象
比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
6.注解的作用
1.编写文档:通过代码里标识的注解生成文档【生成doc文档】
2.代码分析:通过代码里标识的注解对代码进行分析【使用反射】
3.编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【override】