Java学习日志(二十五): Junit单元测试,反射技术

JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)

Junit单元测试

Junit单元测试:单独执行某一个方法

使用步骤:

  1. 导入Junit的jar包
  2. 在方法上添加一个@Test注解,并导入test注解所在的包
  3. 点击方法左边的绿色三角/右键选择方法,选择run执行方法
    点击类左边的绿色三角/右键选择方法,选择run执行全部被@Test修饰的方法

Junit的注意事项
1.没有被@Test注解修饰的方法,不能使用Junit单独执行
2.Junit只能执行public void 修饰的空参数方法

Junit相关的注解:

  • @Test,用于修饰需要执行的测试方法
  • @Before,修饰的方法会在测试方法之前被自动执行,用于获取资源
  • @After,修饰的方法会在测试方法执行之后自动被执行,用于释放资源

注意:
@Before和@After修饰的方法,不能单独执行

代码示例:

public class Demo03 {
    @Test
    public void test01() {
        System.out.println("test01");
    }

    @Test
    public void test02() {
        System.out.println("test02");
    }

    @Test
    public void test03() {
        System.out.println("test03");
    }

    @Before
    public void myBefore() {
        System.out.println("myBefore");
    }

    @After
    public void myAfter() {
        System.out.println("myAfter");
    }
}

反射

类加载器

在这里插入图片描述

反射的概念与原理

看图就完事了
在这里插入图片描述

获取class文件对象的方式

获取class文件对象的方式(重点):

  1. 可以使用Object类中的方法,获取class文件对象
    Class<?> getClass() 返回此 Object的运行时类。
  2. 可以使用数据类型.class属性,获取class文件对象
    java为每种数据类型,都赋予了一个class属性
    基本数据类型(4类8种):int.class,double.class
    引用数据类型(类,接口,数组):Person.class,String.class,ArrayList<String>.class
  3. 可以使用class类中的静态方法,获取class文件对象
    static Class<?> forName​(String className) 返回与具有给定字符串名称的类或接口关联的类对象。
    参数:String className:全类名(包名+类名:cn.itcast.demo02.Reflect.Person)
    选中类名,右键copyReference即可拷贝包名+类名

注意:class文件对象是由类加载器创建的,无论用哪种方法获取,都是同一个class文件对象,class文件对象只有一个

代码示例:Person类

public class Person {
    private String name;
    private int age;

    private Person(String name) {
        this.name = name;
        System.out.println("Person类的私有构造方法");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person类的满参数构造方法");
    }

    public Person() {
        System.out.println("Person类的空参数构造方法");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private void method(){
        System.out.println("私有方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

测试类

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.可以使用Object类中的方法,获取class文件对象
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);//class cn.itcast.demo02.Reflect.Person
        //2.可以使用数据类型.class属性,获取class文件对象
        /*int.class;
        double.class;
        String.class;*/
        Class c2 = Person.class;
        System.out.println(c2);//class cn.itcast.demo02.Reflect.Person
        //3.可以使用class类中的静态方法,获取class文件对象
        Class c3 = Class.forName("cn.itcast.demo02.Reflect.Person");
        System.out.println(c3);//class cn.itcast.demo02.Reflect.Person
        //class文件对象只有一个
        System.out.println(c1==c2);//true
        System.out.println(c1==c3);//true
        System.out.println(c2==c3);//true
    }
}

Class类中的常用方法

Class类中的常用方法

  • String getSimpleName():获得简单类名,只是类名,没有包
  • String getName():获取全类名,包含包名+类名

在Person类中加入一个静态代码块

static {
    System.out.println("静态代码块");
}

代码示例:获取class文件对象,会执行类中的静态代码块

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取class文件对象,会执行类中的静态代码块-->mysql-->JDBC
        Class clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        String simpleName = clazz.getSimpleName();
        System.out.println(simpleName);//Person
        String name = clazz.getName();
        System.out.println(name);//cn.itcast.demo02.Reflect.Person

    }
}

获取类中的构造方法并创建对象

使用反射技术获取类中的构造方法,并使用获取到的构造方法创建对象

获取构造方法

实现步骤

  1. 获取类的class文件对象
  2. 使用class文件对象中的方法getConstructor/Constructors获取类中的构造方法
  3. 使用Constructor类中的方法newInstance创建对象(实例化对象)

Person类中的三个构造方法:
public Person() :空参构造
private Person(String name) :私有构造
public Person(String name, int age) :满参构造

成员方法

  • 构造器<?>[] getConstructors() 返回一个包含构造器对象的数组,构造器对象反映了此类对象所表示的类的所有公共构造函数
  • 构造器<?>[] getDeclaredConstructors() 返回构造器对象的数组,构造器对象反映由此类对象表示的类声明的所有构造函数(包括私有)
  • 构造器<T> getConstructor​(类<?>... parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数
    参数:类<?>… parameterTypes:可变参数,构造方法参数的class文件对象
  • 构造器<T> getDeclaredConstructor​(类<?>... parameterTypes) 返回一个构造器对象,该对象反映此类对象所表示的类或接口的指定构造函数(包括私有)。

注意:如果类中没有指定的构造方法,会抛出NoSuchMethodException异常

代码示例:获取类中的构造方法

public class Demo03GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.获取类的class文件对象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //2.使用class文件对象中的方法getConstructor/Constructors获取类中的构造方法
        //构造器<?>[] getConstructors()
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //构造器<?>[] getDeclaredConstructors()
        Constructor<?>[] declaredCons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : declaredCons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //构造器<T> getConstructor​(类<?>... parameterTypes)
        //public Person()
        Constructor<?> con1 = clazz.getConstructor();
        System.out.println(con1);//public cn.itcast.demo02.Reflect.Person()
        //public Person(String name, int age)
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);

        //构造器<T> getDeclaredConstructor​(类<?>... parameterTypes)
        //private Person(String name)
        Constructor<?> con3 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con3);

    }
}

newInstance方法创建对象

java.lang.reflect.Constructor :描述构造方法的类
使用Constructor类中的方法newInstance创建对象(实例化对象)

成员方法
T newInstance​(Object... initargs) :实例化对象,创建对象

参数:Object... initargs:可变参数,构造方法创建对象实际使用的参数
返回值:T:创建好的对象,类型使用Object类型

私有的构造方法,没有权限访问,执行的时候有权限检查,会抛出
IllegalAccessException非法访问异常

可以使用Constructor的父类中的方法解决这个问题

Constructor extends AccessibleObject

void setAccessible​(boolean flag) 将此反射对象的 accessible标志设置为指示的布尔值。

  • true:表示反射对象在使用时应禁止检查Java语言访问控制
  • false:表示反射对象在使用时应强制检查Java语言访问控制

代码示例:

public class Demo03GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取类的class文件对象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //2.使用class文件对象中的方法getConstructor/Constructors获取类中的构造方法
        //构造器<?>[] getConstructors()
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //构造器<?>[] getDeclaredConstructors()
        Constructor<?>[] declaredCons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : declaredCons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //构造器<T> getConstructor​(类<?>... parameterTypes)
        //public Person()
        Constructor<?> con1 = clazz.getConstructor();
        System.out.println(con1);//public cn.itcast.demo02.Reflect.Person()
        //public Person(String name, int age)
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);

        //构造器<T> getDeclaredConstructor​(类<?>... parameterTypes)
        //private Person(String name)
        Constructor<?> con3 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con3);

        //3.使用Constructor类中的方法newInstance创建对象(实例化对象)
        /*
            java.lang.reflect.Constructor<T> :描述构造方法的类
            T newInstance​(Object... initargs) :实例化对象,创建对象
            参数:Object... initargs:可变参数,构造方法创建对象实际使用的参数
            返回值:T:创建好的对象,类型使用Object类型

         */
        //public Person()
        Object obj1 = con1.newInstance();//此方法相当于new Person();
        System.out.println(obj1);//Person{name='null', age=0}
        /*Person p = (Person)obj1;
        System.out.println(p);////Person{name='null', age=0}*/

        //public Person(String name, int age)
        Object obj2 = con2.newInstance("老王",18);//此方法相当于new Person("老王",18);
        System.out.println(obj2);
        /*
            私有的构造方法,没有权限访问,执行的时候有权限检查,会抛出
            IllegalAccessException非法访问异常。
            可以使用Constructor的父类中的方法解决这个问题
                Constructor extends AccessibleObject
                void setAccessible​(boolean flag) 将此反射对象的 accessible标志设置为指示的布尔值。
                值true表示反射对象在使用时应禁止检查Java语言访问控制。
                值false表示反射对象在使用时应强制检查Java语言访问控制

            注意:暴力反射不建议使用,破坏了类的封装性
         */
        //private Person(String name)
        con3.setAccessible(true);//取消con3构造方法的权限检查-->暴力反射
        Object obj3 = con3.newInstance("老张");
        System.out.println(obj3);
    }
}

简便写法–>直接创建空参构造

使用反射技术获取构造方法并创建对象的简便方式:

Class类中的成员方法
T newInstance() 直接获取空参构造并创建对象返回,省略了获取Constructor的过程

使用前提:

  1. 类中必须有空参构造
  2. 空参数构造方法的修饰符不能是private,建议使用public

代码示例:

public class Demo04 {
    public static void main(String[] args) throws Exception {
        //获取class文件对象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //直接使用newInstance方法获取对象
        Object obj = clazz.newInstance();
        System.out.println(obj);
        //取代了
        Object obj2 = clazz.getDeclaredConstructor().newInstance();
        System.out.println(obj2);

    }
}

获取类中的成员方法并执行

获取成员方法

使用反射技术获取类中的成员方法,并执行
执行步骤

  1. 获取类的class文件对象
  2. 使用class文件对象中的方法getMethod/getMethods获得成员方法
  3. 使用Method类中的方法invoke执行获取到的成员方法

Person类中的成员方法

  • public String getName()
  • public void setName(String name)
  • private void method()

Class类中的方法:

  • 方法[] getMethods() 获取类中所有公共的成员方法,包括父类和接口中的

  • 方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。

  • 方法 getMethod​(String name, 类<?>... parameterTypes)获取指定的公共成员方法

  • 方法 getDeclaredMethod​(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。

      参数:
      String name:方法名称
      类<?>... parameterTypes:成员方法参数列表的class文件对象
    

注意:类中没有指定的成员方法,会抛出NoSuchMethodException

代码示例:

public class Demo05 {
    public static void main(String[] args) throws NoSuchMethodException {
        //1.获取类的class文件对象
        Class clazz = Person.class;
        //2.使用class文件对象中的方法getMethod/getMethods获得成员方法
        /*
            方法[] getMethods() 获取类中所有公共的成员方法,包括父类和接口中的
            方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。

         */
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        /*
            方法 getMethod​(String name, 类<?>... parameterTypes)获取指定的公共成员方法
            方法 getDeclaredMethod​(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
            参数:String name:方法名称
                 类<?>... parameterTypes:成员方法参数列表的class文件对象
            注意:类中没有指定的成员方法,会抛出NoSuchMethodException

         */
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);
        System.out.println(setNameMethod);
        //private void method()
        Method privateMethod = clazz.getDeclaredMethod("method");
        System.out.println(privateMethod);
    }
}

invoke方法执行获取到的成员方法

java.lang.reflect.Method :描述成员方法的类

Method类中的invoke方法
Object invoke​(Object obj, Object... args) 执行成员方法

参数:
    Object obj:invoke方法需要对象支持,执行的是哪个类的方法,就传递哪个对象,执行的是Person类的方法,传递Person对象(clazz.newInstance())。
    Object... args:成员方法执行的时候,需要的实际参数。
返回值:
    Object:方法的返回值,如果方法有返回值则返回对应的值,如果没有返回值,返回null。

注意:私有方法没有权限执行,有权限检查,会抛出IllegalAccessException
使用暴力反射,取消权限检查。

代码示例:

public class Demo05 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //1.获取类的class文件对象
        Class clazz = Person.class;
        //2.使用class文件对象中的方法getMethod/getMethods获得成员方法
        /*
            方法[] getMethods() 获取类中所有公共的成员方法,包括父类和接口中的
            方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。

         */
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        /*
            方法 getMethod​(String name, 类<?>... parameterTypes)获取指定的公共成员方法
            方法 getDeclaredMethod​(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
            参数:String name:方法名称
                 类<?>... parameterTypes:成员方法参数列表的class文件对象
            注意:类中没有指定的成员方法,会抛出NoSuchMethodException

         */
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);
        System.out.println(setNameMethod);
        //private void method()
        Method privateMethod = clazz.getDeclaredMethod("method");
        System.out.println(privateMethod);
        System.out.println("----------------------------------");
        //3.使用Method类中的方法invoke执行获取到的成员方法
        /*
            java.lang.reflect.Method :描述成员方法的类
            Object invoke​(Object obj, Object... args) 执行成员方法
            参数:Object obj:invoke方法需要对象支持,执行的是哪个类的方法,就传递哪个对象
                    执行的是Person类的方法,传递Person对象(clazz.newInstance())
                 Object... args:成员方法执行的时候,需要的实际参数
            返回值:Object:方法的返回值
                    如果方法有返回值则返回对应的值,如果没有返回值,返回null

         */
        Object obj = clazz.newInstance();
        //public String getName()
        Object v1 = getNameMethod.invoke(obj);//此方法相当于执行了getName方法
        System.out.println("v1:"+v1);//v1:null
        ////public void setName(String name)
        Object v2 = setNameMethod.invoke(obj,"老王");//此方法相当于执行了setName方法,给成员遍历赋值
        System.out.println("v2:"+v2);//v2:null 方法没有返回值
        System.out.println(obj);
        /*
            私有方法没有权限执行,有权限检查,会抛出IllegalAccessException
            使用暴力反射,取消权限检查
         */
        privateMethod.setAccessible(true);
        //private void method()
        privateMethod.invoke(obj);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章