Java基础25 反射 junit测试

一、Junit测试

1.Junit 主要用于单元测试 也就是局部测试 可以通过局部单元测试 查找代码出现bug
2.测试
黑盒测试 功能测试 接口测试 不需要编写代码 瞎点 根据测试用例进行测试
白盒测试 性能测试 编写脚本代码 shell脚本 语法与指令(测试)
3.Junit 单元测试
4.使用的步骤
需要进行导包 junit jar
定义一个类
定义方法 在方法上加上@Test 注解
访问修饰符public
没有返回值 只能使用void J进行修饰
其余跟普通方法一样
5.操作流程
step01 新建一个文件 lib 目录 用于存放jar
在这里插入图片描述

step2 将jar添加项目依赖
在这里插入图片描述

public class Test {
// @org.junit.Test
@org.junit.Test
public void show(){
// System.out.println("我是局部测试");
System.out.println(1/0);
}
}

二、反射的概念

1.反射是所有框架的灵魂 所有的框架都需要用到反射 所有的类在反射面前都是裸体(私有的 公有)
2.框架 :半成品 在半成品的基础进行开发 效率会更高 大部分公司提倡使用框架
市场分布式解决方案
1.ssm+doubb(阿里) spring +spring mvc +mybatis +doubb
2.spring cloud +spring boot (趋势)
3.反射机制:将类的各个组成部分 分装成其他的对象(Class对象)就是反射机制
在这里插入图片描述
注意:Class阶段会先执行静态代码

三、反射-获取Class对象

1.第一种方式:通过实例化一个对象 对象在调用 getClass() 方法来获取 (很少使用这种方式)
2.第二种方式:直接通过类名.class来获取
3.第三种方式:通过 Class.forName(“完整的包名+类名”); (最多使用)
4.代码
第三种方式 只要完整的包名加类名 无需项目名
如果你只是希望一个类的静态代码块执行,其它代码一律不执行,可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行

package day25.test;

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //实例化对象 调用getClass方法
        Student stu = new Student();
        Class cla1 = stu.getClass();
        System.out.println(cla1);
        //第二种方式
        Class cla2 = Student.class;
        System.out.println(cla2);
        //第三种方式 只要完整的包名加类名 无需项目名
        Class cla3 =Class.forName("day25.test.Student");
        System.out.println(cla3);

    }
}

四、反射-获取构造方法

在这里插入图片描述

package day25.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public class Test03 {
    /**
     * 获取所有公有的构造方法
     */
    public void show1() throws ClassNotFoundException {
        Class cla = Class.forName("day25.test.Student");
        //调用方法可以获取所有公有的构造
        Constructor[] cons = cla.getConstructors();
        for (Constructor c : cons) {
            System.out.println(c);
        }
    }

    //获取所有的构造方法
    public void show2() throws ClassNotFoundException {
        Class cla = Class.forName("day25.test.Student");
        Constructor[] cons = cla.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
    }

    //获取单个公有构造方法 并实例化对象
    public void show3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cal = Class.forName("day25.test.Student");
        //获取单个共有的构造 ,没有共有构造方法或者多个都报错
        Constructor con = cal.getConstructor();
        System.out.println(con);
        //通过反射来实例化这个对象
        Student stu = (Student) con.newInstance();
        System.out.println(stu.name);

    }

    //获取单个私有的构造  单个无s 返回一个数据 多个加s 英语的复数 返回数组
    public void show4() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cla = Class.forName("day25.test.Student");
        Constructor con =cla.getDeclaredConstructor(String.class,int.class);
        //通过暴力反射去除私有
        con.setAccessible(true);
        //通过构造方法反射来实例化这个对象
        Student stu =(Student)con.newInstance("小明",23);
        System.out.println(stu.name);
        //私有属性就无法直接通过对象名.属性调用
        //System.out.println(stu.age);
    }
}



五、反射-获取成员方法

在这里插入图片描述

package day25.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Test04 {
    //获取所有公有的方法
    public void show1() throws ClassNotFoundException {
        Class<?> cla = Class.forName("day25.test.Student2");

        Method [] methods =cla.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

    }

    //获取所有的方法 不包括父类的方法
    public  void show2() throws ClassNotFoundException {
        Class<?> cla = Class.forName("day25.test.Student2");

        Method [] methods =cla.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

    }

    //获取反射公有的方法
    public void show3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cla = Class.forName("day25.test.Student2");

        //获取单个公有的方法
        //第一个参数 方法的名称 第二个参数 方法参数的类型的.class
        Method method = cla.getMethod("eat",String.class);

        //通过反射来实例化对象
        Constructor con =cla.getConstructor();
        //实例化对象
        Object obj =con.newInstance();
        //调用方法invoke (Object obj,Object ...args)
        //第一个时调用方法的对象 第二个参数时多需要的具体的值
        //返回值就是参数的返回值
        Object ob =method.invoke(obj,"梨子");
        System.out.println(ob);

    }

    public void show4() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cla = Class.forName("day25.test.Student2");
        //获取所有的方法
        Method method= cla.getDeclaredMethod("sleep");
        //使用暴力反射去除私有
        method.setAccessible(true);
        //通过反射来实例化对象
        Constructor con = cla.getConstructor();
        //实例化对象
        Object obj =con.newInstance();
        method.invoke(obj);

    }
}

七、通过反射来忽略集合泛型

泛型是在编译期间起作用的,在编译后的.class文件中是没有泛型的。所有泛型,本质都是通过Object处理的。反射是在编译后操作.class文件,所以可以越过泛型
理解:泛型通过object在编译期间转为对应类型,编译完后生成.class文件,这时反射在操作.class文件,所以可以忽略泛型
使用反射给集合泛型添加了其他类型数据后
增强for循环需要返回值类型需要为Object,否则遍历输出时会再一次进行强转,然后可能报错
Integer num = list.get(2); 会报错 类转换异常
Object num = list.get(2); 正常运行
list.add(“ss”); 编译就报错 不能添加String类型

 //通过反射来忽略集合泛型
    public void show5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<Integer> list =new ArrayList<>();
        list.add(123);
        list.add(344);
        //获取当前对象的Cla对象
        Class cla =list.getClass();
        //获取集合的add方法
        Method method =cla.getMethod("add",Object.class);
        //调用集合中的add方法
        method.invoke(list,"add");
        //增强for循环需要返回值类型需要为Object,否则遍历输出时会再一次进行强转,然后可能报错
        for (Object obj : list) {
            System.out.println(obj);
        }
        //Integer num = list.get(2);  会报错 类转换异常
        System.out.println(list.get(2));
        Object num = list.get(2);
        Object object = list.get(0);
        System.out.println(list);
        //list.add("ss");  编译就报错 不能添加String类型

    }

案例1 配置文件

step01 需求 在不改变任意的代码的前提下 调用任何类中任何方法
step02 分析
1.需要调用一个类中的方法
完整的包名 方法的名称
2.完整的包名 方法的名称 只能存放在配置文件中 只能存在Properties对象中
3.可以通过反射来动态的来进行调用
step03代码结构
在这里插入图片描述
配置文件创建方法,与工具类在同一个包下,右键包名,new->file->命名(名字后缀必须为.properties)->创建成功添加成功内容
其他文件类型也可以用file创建,后缀名写好就行,例如1.txt,就会创建文本文件
实体类

package day25.test;

public class Student1 {
    private void show(String n,int num,String w){
        System.out.println(n+"\t"+num+"\t"+w);
    }
    public void showInfo(){
        System.out.println("我是调用的showInfo方法");
    }

}

配置文件

classname=day25.test.Student1
methodname=show
parametertypes=String,int,String
result=false

工具类

package day25.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 工具类
 */
public class PropertiesUtils {
    //私有的属性
    private  static PropertiesUtils propertiesUtils;
    private Properties pro;
    //私有的构造
    private PropertiesUtils(){
        try {
            pro = new Properties();
            //通过反射来获取
            //文件必须与当前类在同一包下
            InputStream is =PropertiesUtils.class.getResourceAsStream("className.properties");
            pro.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //提供一个公有的方法
    public static synchronized PropertiesUtils getInstance(){
        if(propertiesUtils == null){
            propertiesUtils =new PropertiesUtils();
        }
        return propertiesUtils;
    }

    //提供一个方法 通过键来获取值
    public String getValue(String key){
        return pro.getProperty(key);
    }
}

测试类

package day25.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Factory {
    public static void main(String args[]){
          showInfo();
    }
    public void showInfo(){
        try {
            //通过PropertiesUtils 工具获取配置文件信息
            String className =PropertiesUtils.getInstance().getValue("classname");
            String methodName =PropertiesUtils.getInstance().getValue("methodname");

            //获取class对象
            Class cla =Class.forName(className);
            //获取构造方法
            Constructor con =cla.getConstructor();
            //实例化对象
            Object obj =con.newInstance();
            //获取参数列表
            Class [] classes =getforClass();
            //非空验证
            if(classes==null || classes.length<=0){
                //获取方法的对象
                Method m = cla.getDeclaredMethod(methodName);
                m.setAccessible(true);
                //调用其方法
                m.invoke(obj);
            }else {
                Method m =cla.getDeclaredMethod(methodName,classes);
                m.setAccessible(true);
                m.invoke(obj,"张三",18,"爱好男");

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
    //获取配置文件中的方法参数类型数组
    public Class[] getforClass() {
        //获取配置文件参数的信息 判断是否有参数
        //配置文件即 同一个包 右键 new ->file->命名->添加内容
        String str = PropertiesUtils.getInstance().getValue("parametertypes");
        //进行非空验证
        if(str == null || str ==""){
            return null;
        }
        //根据逗号进行分割
        //定义一个集合来进行存储
        List<Class> list= new ArrayList<>();
        String [] arrays =str.split(",");
        for (String s : arrays) {
            if(s.equals("String")){
                list.add(String.class);
            }else if(s.equals("int")){
                list.add(int.class);
            }else {
                list.add(Object.class);
            }
        }
        return list.toArray(new Class[list.size()]);
    }
}

需要输出不同内容 即更改foctory类反射调用方法传入的参数即可,需要调用Student1的showInfo方法,即在配置文件中 将methodname=show 改为 methodname = showInfo parametertypes=String,int,String 改为parametertypes= 或者直接删除这行

八、反射-获取成员变量

在这里插入图片描述

package day25.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Test06 {
    /**
      * 获取所有公有的属性
      */
    public void show1() throws ClassNotFoundException {
        Class<?> cla = Class.forName("day25.test.Student");
        //获取所有的属性 调用其方法
        Field [] fields =cla.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }
    }
    /**
     * 获取所有的属性 包括私有的
     * @throws ClassNotFoundException
     */
    public void show2() throws ClassNotFoundException {
        //获取Class对象
        Class<?> cla = Class.forName("day25.test.Student");
        //获取所有的属性 调用其方法
        Field [] fields =cla.getDeclaredFields();
        for (Field f : fields) {
            System.out.println(f);
        }

    }
    /**
     * 获取单个公有的属性
     */
    public void show3() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象
        Class<?> cla = Class.forName("day25.test.Student");
        //获取所有的属性 调用其方法
        Field f =cla.getField("name");
        //获取构造方法
        Constructor con =cla.getConstructor();
        //实例化对象
        Object obj =con.newInstance();

        //给属性进行设置值 第一个参数 赋值的对象 赋的具体的数据
        //public void set(Object obj,Object Value)
        f.set(obj,"张三");

        //通过对象来获取值
        Student stu =(Student) obj;
        System.out.println(stu.name);



    }

    public void show4() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cla = Class.forName("day25.test.Student");
        //获取所有的属性 调用其方法
        Field f =cla.getDeclaredField("sid");

        f.setAccessible(true);

        Constructor con =cla.getConstructor();

        Object obj =con.newInstance();

        f.set(obj,123);

        Object ob =f.get(obj);
        System.out.println(ob);
    }
}

反射小总结

1.用反射class创建对象,实质是用无参构造,所以该类需要有无参构造才能用此方式 Object p = cla.newInstance();
2.得到类对象就能得到 构造方法getConstructor 成员方法getMethod 成员变量getFiled
3.加s 即得到多个即所有 加Declared 即能得到私有 加方法参数即得到指定方法
4.单数返回一个对象类型,复数返回一个对象类型数组
5.Modifier.toString(类或方法或属性.getModifiers()) 得到相应的修饰符 public private
6.构造方法或成员方法.getParameterTypes(); 得到方法的参数类型数组
7.m.getReturnType().getSimpleName() 方法返回值类型
8.f.getType().getSimpleName()属性类型
9. m.getName() 方法名 f.getName() 属性名 cla.getSimpleName() 类名
10.setAccessible(true); 暴力解除私有 之后不安全 其他类也能访问私有属性
11.属性.set(实例化的对象名,赋值的属性值) get(实例化的对象名) 赋值取值
12.方法.invoke(实例化的对象名,该方法需传入的实参1,实参2) 得到方法的返回值,void类型返回null
13.构造方法.newInstance();无参构造不传参数,有参构造传需要的参数

14.method.getParameters() parameters[i].getName() 得到默认的形参命名 arg0

package day25.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class<?> cla = Class.forName("day25.demo.Person");
        //获取包名加类名
        System.out.println(cla.getName());
        //获取类名
        System.out.println(cla.getSimpleName());
        //用反射class创建对象,实质是用无参构造,所以该类需要有无参构造才能用此方式
        Object p = cla.newInstance();

        //获取所有的方法
        Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            //declaredConstructor.setAccessible(true);
            System.out.println(declaredConstructor);
        }
        //获取单独指定的构造方法 无参构造并实例化
        Constructor<?> con = cla.getConstructor();
        Object o = con.newInstance();
        Person p1 = (Person)o;
        //获取有参构造并实例化
        Constructor<?> con1 = cla.getConstructor(String.class, int.class);
        Person p2 = (Person) con1.newInstance("张三", 18);
        //获取所有的方法
        Method[] declaredMethods = cla.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName());
        }
        //获取指定的方法 第一个参数 方法的名称 第二个参数 方法参数的类型的.class
        // 调用  调用方法invoke (Object obj,Object ...args)
        // 第一个时调用方法的对象 第二个参数时多需要的具体的值 返回值就是参数的返回值
        Method setName = cla.getMethod("setName", String.class);
        setName.invoke(p2,"换成李四啦");
        System.out.println(p2);
        //其值是该方法的返回值,为void 返回null 有具体的返回值就是该返回值
        System.out.println(setName.invoke(p2,"换成李四啦"));

        //获取所有的属性
        Field[] declaredFields = cla.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
        }
        //获取指定的属性
        Field age = cla.getDeclaredField("age");
        //因为是私有的 需要先破解 打破封装
        age.setAccessible(true);
        //获取属性
        System.out.println(age.get(p2));
        //设置属性
        age.set(p2,20);
        System.out.println(age.get(p2));

    }
}

案例 反编译类

package day25.demo;

import java.lang.reflect.*;

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        StringBuilder sb = new StringBuilder();
        //获取类对象
        Class cla = Class.forName("day25.demo.Person");
        //反编译类名
        sb.append(Modifier.toString(cla.getModifiers())+" class "+cla.getSimpleName()+"{\n");
        //获取所有的属型 反编译属性
        Field [] fields =cla.getDeclaredFields();
        for (Field field : fields) {
            sb.append("\t");
            //获取属性的修饰符列表,返回的修饰符是一个数字,每个数字对应修饰符表的一个修饰符
            //用Modifier.toString() 转为字符串
            sb.append(Modifier.toString(field.getModifiers()));
            if(field.getModifiers() !=0){
                sb.append(" ");
            }
            //获取属性的类型
            sb.append(field.getType().getSimpleName());
            sb.append(" ");
            //获取属性的名字
            sb.append(field.getName());
            sb.append(";\n");
        }
        //获取构造方法 反编译构造方法
        Constructor[] cons = cla.getDeclaredConstructors();
        for (Constructor con : cons) {
            sb.append("\t");
            sb.append(Modifier.toString(con.getModifiers()));
            sb.append(" "+cla.getSimpleName()+" ");
            sb.append("(");
            //获取形参的类型
            Class[] parameterTypes = con.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getSimpleName());
                sb.append(" "+fields[i].getName());
                if(i != parameterTypes.length-1){
                    sb.append(",");
                }

            }

            if(parameterTypes.length==0){
                sb.append("){\n\t}\n");
            }else {
                sb.append("){\n\t");
                for (int i = 0; i < fields.length; i++) {
                    sb.append("\t"+"this."+fields[i].getName()+" = "+fields[i].getName()+";");
                    sb.append("\n\t");
                }
                sb.append("}\n");

            }

        }

        //获取方法 反编译方法
        Method[] methods = cla.getDeclaredMethods();
        for (Method method : methods) {
            sb.append("\t");
            //获取修饰符列表
            sb.append(Modifier.toString(method.getModifiers())+" ");
            //获取方法的返回值类型
            sb.append(method.getReturnType().getSimpleName()+" ");
            //获取方法名
            sb.append(method.getName()+"(");
            //方法形参的类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            //方法形参默认命名  parameters[i].getName() args0 args1
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getSimpleName());
                sb.append(" "+parameters[i].getName());
                if(i != parameterTypes.length-1){
                    sb.append(",");
                }

            }
            sb.append("){}\n");

        }
        sb.append("}");
        System.out.println(sb);


    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值