Junit 反射 注解

web

8.18

Junit

属于白盒测试

  1. 使用步骤:
  • 定义测试类:类名 ClassTest、包名 package.test
  • 定义测试方法:void testMethond()
  • @Test并导包

注:junit测试建议参考如上定义方式

  1. 判断结果:断言 Assert
  2. @Before、@After

反射

反射机制:将类的各个组成部分封装成对象

  1. 获取一个Class对象的方式:
    . 类名.class,通过类名的属性class获取,多用传参,即调用其他方法引出需要的对象!!比如非静态方法使用this关键字去获取流对象,类加载器对象,注解(实现类)对象。
    3个方法:加载类进内存

     1. Class.forName(" 类全名 "), 将字节码文件加载入内存,返回Class对象,多用于配置文件
     2. 类名.class,通过类名的属性class获取,多用传参,即调用其他方法引出需要的对象!!比如流对象,类加载器对象,注解(实现类)对象。
     3. 对象.getClass(),此方法在Object类中定义,针对对象的获取方式;可以new对象时直接使用此方法获取
    
  2. Class类的(功能)方法

    获取:

     获取成员变量们
     				Field[] getFields() :获取所有`public`修饰的成员变量
     				Field getField(String name)   获取`指定名称的public`修饰的成员变量
     				Field[] getDeclaredFields()  获取`所有`的成员变量,不考虑修饰符
     				Field getDeclaredField(String name)  
     获取构造方法们
     				Constructor<?>[] getConstructors()  
     			    Constructor<T> getConstructor(类<?>... parameterTypes) 
     				Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
     			    Constructor<?>[] getDeclaredConstructors()  
     获取成员方法们:
     				Method[] getMethods()  :包含Object继承的方法
     			    Method getMethod(String name, 类<?>... parameterTypes)  
     				Method[] getDeclaredMethods()  
     			    Method getDeclaredMethod(String name, 类<?>... parameterTypes)  
     获取全类名	
     				String getName()  
    
//指定构造方法获取格式: 
Constructor constructor = personcls.getConstructor(String.class, int.class);

Field:成员变量

		 操作:
			1. 设置值 void set(Object obj, Object value)  
			2. 获取值 get(Object obj)  私有的无法访问,需要使用 3
			3. 忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射,在方法也是可以使用
		//获取成员变量并操作
        Class personcls = Person.class;
        Field[] fields = personcls.getDeclaredFields();
		Person p = new Person();
        fields[0].setAccessible(true);
        fields[0].set(p,"lee");
        Object o = fields[0].get(p);
        System.out.println(o);

Constructor:构造方法

		 创建对象: T newInstance(Object... initargs)  
		 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
		//获取构造器并new
		Class personcls = Person.class;
        Constructor constructor = personcls.getConstructor(String.class, int.class);
        Object person = constructor.newInstance("lee", 13);	
        System.out.println(person);	
        //空参构造
        personcls.newInstance();

Method:方法对象

		执行方法: Object invoke(Object obj, Object... args)  
	   获取方法名称: String getName:获取方法名
		//获取方法并run
		Class personcls = Person.class;
        Method toString = personcls.getMethod("toString");
        String name = toString.getName();
        System.out.println(name);
        toString.invoke(new Person());

个人理解:反射也是基于一个类,万物皆对象。使用Class类来获取其他类的各个组成部分,构造器Constructor用来new,方法Method用来run,成员Field用来操作
(Object obj, Object… args) :(对象,参数)

案例

ReflectDemo.java

package day01;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflectDemo {
    /*
     * 利用反射实现: 1.前提不改变代码 2.加载配置文件 3.利用反射技术将类文件加载入内存 4.实现创建对象,并调用方法
     */
    public static void main(String[] args) {
        Properties prop = new Properties();
        String file = "otherclasses.properties";
        String className = null;
        String methodName = null;
        InputStream is = null;
        try {
            // 使用反射机制获取流
            is = ReflectDemo.class.getResourceAsStream(file);
            prop.load(is);
            className = prop.getProperty("className");
            methodName = prop.getProperty("methodName");

            // 加载类进入内存,只要获取也可以间接加载
            Class cls = Class.forName(className);
            // 解决创建对象类型不一致问题
            Object obj = cls.newInstance();
            Method method = cls.getMethod(methodName);
            method.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

.properties

className=Person
methodName=eat

Person.java

public class Person {

    private String name;
    private int age;

	public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
   
    /* eat方法用于测试反射 */
    public void eat(){
        System.out.println("我吃过了!");
    } 
}

保险起见使用自己写的类获取ClassLoader: this.getClass().getClassLoader(),解决返回null问题
对于获取输入流对象空指针异常:获取资源文件路径的一些方法

		//表示在src下找配置文件
		fis = this.getClass().getClassLoader().getResourceAsStream("config.properties");
		
		//表示在类包下找配置文件
		fis = this.getClass().getResourceAsStream("config.properties");

		//静态无法使用this关键字,解决
		Reflect.class.getResourceAsStream(file)
		/*两者也是便于理解的,对于报错出现空指针异常`java.lang.NullPointerException`应该关注一下这里的区别*/
		//对于URI资源转换的文件资源路径:因为使用了URI编码,导致特殊符号和中文的异常,比如空格。如果文件路径有空格等特殊字符,应该使用toURI()方法解决
		 String path = JsoupDemo1.class.getClassLoader().getResource("student.xml").toURI().getPath();

使用注解简化的反射案例:
AnnoAboutReflect.java

package day01;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/* 这是用于简化反射demo:ReflectDemo 的注解 */
//使用元注解描述注解
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoAboutReflect {
    String className();
    String methodName();
}

SimplifyReflectDemo.java

package day01;

import java.lang.reflect.Method;

@AnnoAboutReflect(className = "Person", methodName = "eat")
public class SimplifyReflectDemo {
    /* 使用注解简化的反射demo */

    /*
     * 利用反射实现: 1.前提不改变代码 2.加载配置文件 3.利用反射技术将类文件加载入内存 4.实现创建对象,并调用方法
     */
    public static void main(String[] args) {
        String className = null;
        String methodName = null;
        // 解析注解
        // 1.获取该类字节码对象
        Class<SimplifyReflectDemo> reflectcls = SimplifyReflectDemo.class;
        // 2.获取该类的注解对象,此getAnnotation方法是在内存中生成了一个注解*实现类*对象并返回,如下:
        /*
         * public class AarImpl implements AnnoAboutReflect {
         * //事实上不是这个样子的过程,这只是一个逻辑,里面有些东也不理解,就这样理解把。
         * 
         * @Override public String className() { return "Person"; }
         * 
         * @Override public String methodName() { return "eat"; } }
         */
        AnnoAboutReflect anno = reflectcls.getAnnotation(AnnoAboutReflect.class);
        // 3.调用注解对象(此对象是实现类)的(重写)方法
        className = anno.className();
        methodName = anno.methodName();
        System.out.println(className + "---" + methodName);// Person---eat

        try { // 使用反射创建对象并操作方法 
           
            // 加载类进入内存,只要获取也可以间接加载
            Class<?> cls = Class.forName(className);
            // 解决创建对象类型不一致问题
            Object obj = cls.newInstance();
            Method method = cls.getMethod(methodName);
            method.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

注解

注解概念:说明程序给计算机看
注释:说明程序给程序员看

Java注解:元数据

作用分类:

①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
②代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

JDK预定义注解

@Override  @Deprecated	@SuppressWarnings

自定义注解

1.格式:

元注解
public @interface MyAnno {
		属性列表;
}

2.本质:反编译后可以看出注解本质是一个接口默认继承Annotation

javap MyAnno.class
Compiled from "MyAnno.java"
public interface MyAnno extends java.lang.annotation.Annotation {
}

3.属性:接口(注解)中的抽象方法
要求

属性返回值类型
	基本数据类型
	String
	枚举
	注解
	以上返回类型的数组

使用

package day01;

public enum User {
    u1,u2;
}
package day01;

public @interface MyAnno2 {
    
}
package day01;

public @interface MyAnno {
    int showint();
    String showString() default "hahaha";
    User showenum();// MyAnno的属性(方法)返回User枚举类型
    MyAnno2 showAnno();//传入注解
    String[] showStringArr();//传入数组,只有一个元素可以省略{}
}
package day01;
/* test the MyAnno */
/* showString()是默认传入字符串,不需要初始化 */
@MyAnno(showint = 1,showenum = User.u1,showAnno = @MyAnno2,showStringArr = {"ab","c"})
public class MyAnnoDemo{
}

*4.元注解:用于描述注解的注解

package day01;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/* 
@Target:描述注解作用的位置
        使用ElementType(是一个数组)取值决定作用域:
        TYPE:作用于类
        METHOD:作用于方法
        FIELD:作用于字段
@Retention:描述注解保留的阶段
@Documented:描述注解是否被抽取到API文档中
@Inherited:描述注解是否被子类继承
 */

//关于MyAnno3注解
//作用域类、方法
@Target(value = { ElementType.TYPE, ElementType.METHOD })
// 保留时段一般取值RUNTIME
@Retention(RetentionPolicy.RUNTIME)
// 添加此注解可以使用javadoc抽取注解
@Documented
// 添加此注解可以被子类继承
@Inherited
public @interface MyAnno3 {

}

在程序中使用(解析)注解

  • 获取注解定义位置的对象
  • 获取实现注解的对象
  • 调用方法获取配置的属性值
    示例在反射案例的简化部分

案例

//关于Check注解检查程序异常,输出日志
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值