JAVA学习笔记14——Junit单元测试,反射,注解

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】

猜你喜欢

转载自blog.csdn.net/qq_44708714/article/details/107198614
今日推荐