zl程序教程

您现在的位置是:首页 >  其他

当前栏目

小治同学的JAVAWEB学习笔记-Junit&反射&注解

ampjunit笔记学习反射 注解 javaweb 同学
2023-09-11 14:18:49 时间

Junit 单元测试

单元测试 黑盒测试 不需要写代码,只放入输入然后查看输出是否是符合目标标准 白盒测试 需要写代码,关注代码的具体流程
Junit使用:白盒测试
	步骤
		1.定义一个测试类(测试用类)
			建议:
				测试类名:北侧是的类+Test
				包名:XXX.XXX.XX.Test
		2.定义测试方法:可以独立运行
			建议:
				方法名:test测试的方法名
				返回值 void
				参数列表 空参

		3.给方法加@Test
	判定结果
		1.红色代表失败
		2.绿色代表成功在这里
原代码
public class Caltest {
    public static void main(String[] args) {
        Cal c = new Cal();
        int ans = c.add(1,2);
        System.out.println(ans);
    }
}
注意:
需要引入Junit依赖项,
可以直接在测试代码写入@Test来声明,这是测试代码,并由系统添加依赖路径

测试代码
使用断言操作Assert.assertEquals(期望值,目标值);
来进行判断输出值是否与期望一致
package itcast.junit.Test;

import itcast.junit.Cal;
import org.junit.Assert;
import org.junit.Test;

public class CalTest {
    @Test
    public void testAdd(){
        System.out.println("我被执行了");
        Cal c =  new Cal();
        int result = c.add(1,2);
        Assert.assertEquals(3,result);
    }
}

失败状态
在这里插入图片描述
测试通过状态
在这里插入图片描述

补充
@Before
修饰的方法会在测试方法之前被自动执行
@After
修饰的方法会在测试方法之后被自动执行

反射:框架设计的灵魂

反射
框架:半成品软件。可以在狂剑的基础上进行软件开发,简化编码
反射:将类的各个组成部分封装为其他对象,这就是反射机制
	好处:
		1.可以在程序运行工程中操作这些对象
		2.可以解耦,提高程序的可扩展性

java代码在计算机中经历的三个阶段:


1.Source源代码阶段
	字节码文件
	Person.java->(javac编译)->Person.class
2.ClassLoader类加载器
	成员变量Field[] fields
	构造方法Constructor[] Con
	成员方法Method[] methods
3.RunTime

在这里插入图片描述
定义了一个字符串,把字符串文件加载进了内存,
在内存中有一个Class对象,把所有方法都放到Method[]数组中
使用的时候只需要把Method[]所有方法拿出来即可
这就是一个反射的机制

相当于IDEA持续运行自动编译

在这里插入图片描述

获取Class对象的方式
1.Class.forname(”全类名“)
将字节码文件加载进内存,返回Class对象
2.类名.Class通过类名的属性Class获取
3.对象.getClass():getClass()方法在Objdect类中定义着

对象:
	同一个字节码文件(*.class)
		*多用于配置文件,将类名定义在配置文件中
		读取文件,加载类
	在一次程序运行过程中,只会被加载一次
		*多用于参数的传递
	不论通过哪一种方式获取的Class对象都是同一个
		*多用于多项的获取字节码方式

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

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    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;
    }

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

测试获取对象

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception{
        Class cls1 =  Class.forName("testpackage.domian.Person");
        System.out.println(cls1);

        Class cls2 = Person.class;
        System.out.println(cls2);

        Person p  = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);

        System.out.println(cls1==cls2&&cls1==cls3);
    }
}

输出

class testpackage.domian.Person
class testpackage.domian.Person
class testpackage.domian.Person
true

Class对象功能 获取功能: 1.获取成员变量们 Filed[] getFields() 获取所有成员变量
		Filed getFields(String name)
		获取指定名字的成员变量
			
		Filed[] getDeclaredFields()
		获取所有成员本良不考虑修饰符
		Filed getDeclaredField(String name)
		
		2.获取构造方法们
		Constructor<?>[] getConstructors()
		Constructor<T> getConstructors(类<?>... parameterTypes)
		
		Constructor<T> getDeclaredConstructors(类<?>... parameterTypes)
		Constructor<?>[] getDeclaredConstructors()
		3.获取成员方法们
		Method[] getMethods()
		Method getMethods(String name,类<?>...parameterTypes)

		Method[] getDeclaeredMethods()
		Method getDeclaeredMethods(String name,类<?>...parameterTypes)

		4.获取类名
		String getName()
		
		Field:成员变量
		操作: 
			1.设置值 set(Object obj,Object value)
			2.获取值 get(Object obj)
			3.setAccessible(true)忽略安全检查,称为暴力反射
Class对象,获取成员变量的方法
    public static void main(String[] args) throws Exception{
        Class personClass = Person.class;

        //Filed[] getFields()
        //获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("------------");
        //Filed[] getDeclaredFields(String name)
        //获取成员变量a的值
        Field a = personClass.getField("a");
        Person p = new Person();//生成一个对象
        Object value = a.get(p);
        System.out.println(value);
        a.set(p,"小春");
        System.out.println(p);

        System.out.println("----------------------------");
        //Filed[] getDeclaredFields() 或许所有的成员变量,不用考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("------------------------");
        //Filed[] getDeclaredFields(String name)
        //访问私有时候,忽略访问权限修饰符的安全检查
        Field b = personClass.getDeclaredField("b");
        b.setAccessible(true);//暴力反射
        Object value2 = b.get(p);
        System.out.println(value2);
    }

输出

public java.lang.String testpackage.domian.Person.a
------------
null
Person{name='null', age=0, a='小春', b='null'}
----------------------------
private java.lang.String testpackage.domian.Person.name
private int testpackage.domian.Person.age
public java.lang.String testpackage.domian.Person.a
private java.lang.String testpackage.domian.Person.b
------------------------
null

Constructor:构造方法
创建对象
T newInstance(Object… initargs)

	如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
public class ReflectDemo3 {
    /*
    	2.获取构造方法们
		Constructor<?>[] getConstructors()
		Constructor<T> getConstructors(类<?>... parameterTypes)

		Constructor<T> getDeclaredConstructors(类<?>... parameterTypes)
		Constructor<?>[] getDeclaredConstructors()
    * */

    public static void main(String[] args) throws Exception{
        Class personClass = Person.class;
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        
        //创建对象
        Object person = constructor.newInstance("小春", 21);
        System.out.println(person);
        System.out.println("-------------------");

        Constructor constructor1 = personClass.getConstructor();
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        System.out.println("-------------------");

        Object o = personClass.newInstance();
        System.out.println(o);
    }
}

输出

public testpackage.domian.Person(java.lang.String,int)
Person{name='小春', age=21, a='null', b='null'}
-------------------
Person{name='null', age=0, a='null', b='null'}
-------------------
Person{name='null', age=0, a='null', b='null'}

获取成员方法们的范例

Method:方法对象
执行方法:
Object invoke(Object obj,Object… args)
获取方法名称:
String getName:()

package testpackage.domian;

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

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println("eat...");
    }

    public void eat(String food){
        System.out.println("eat..."+food);
    }

    public Person() {
    }

    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;
    }

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

主要

public class ReflectDemo3 {
    /*
    	3.获取成员方法们
		Method[] getMethods()
		Method getMethods(String name,类<?>...parameterTypes)

		Method[] getDeclaeredMethods()
		Method getDeclaeredMethods(String name,类<?>...parameterTypes)
    * */

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class personClass = Person.class;
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);

        Method eat_method2 = personClass.getMethod("eat",String.class);
        eat_method2.invoke(p,"饭");

        System.out.println("------------");
        //获取所有Public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            //获取类名
            System.out.println(method.getName());
        }
    }
}

输出

eat...
eat...------------
public java.lang.String testpackage.domian.Person.toString()
toString
public java.lang.String testpackage.domian.Person.getName()
getName
public void testpackage.domian.Person.setName(java.lang.String)
setName
public void testpackage.domian.Person.setAge(int)
setAge
public int testpackage.domian.Person.getAge()
getAge
public void testpackage.domian.Person.eat()
eat
public void testpackage.domian.Person.eat(java.lang.String)
eat
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
public boolean java.lang.Object.equals(java.lang.Object)
equals
public native int java.lang.Object.hashCode()
hashCode
public final native java.lang.Class java.lang.Object.getClass()
getClass
public final native void java.lang.Object.notify()
notify
public final native void java.lang.Object.notifyAll()
notifyAll

案例:
需求:写一个框架,可以帮我们创建任意类的对象,并且执行其中的方法
实现:
1.配置文件
2.反射

步骤:
	1.将需要创建的对象的全类名和需要执行的方法定义在配置文件
	2.在程序加载读取配置文件
	3.使用反射技术加载类文件进内存
	4.创建对象
	5.执行方法

我创建了testpackage.domian.Person这个类
定义了eat这个方法
配置文件

className = testpackage.domian.Person
methodName = eat

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意对象,执行任意方法
        //加载配置文件
        //1.1创建对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取Class目录下的配置文件
        //pro.load();
        ClassLoader classLoader = ReflectTest.class.getClassLoader();//类加载器
        InputStream is = classLoader.getResourceAsStream("pro.properties");//获取文件字节流
        pro.load(is);

        //2.获取配置文件中的定义的数据
        String classNmae = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

       // 3.加载该类进内存
       Class cls = Class.forName(classNmae);

       //4.创建对象
        Object obj = cls.newInstance();

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

        //6.执行方法
        method.invoke(obj);
    }
}

输出

eat...

注解

注解
注释:用文字描述给程序员看的

注解:注解(Annotation),也叫元数据。
一种代码级别的说明。
它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
它可以声明在包、类、字段、方法、局部变量、
方法参数等的前面,用来对这些元素进行说明,注释。

作用分类:

①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】

package itcast.junit.Test;

/*
@since 1.5
@author bi
@version 1.5
* */
public class AnnoDemo1 {
 /*
    @param a 整数
    @param b 整数
    @return 两数之和
 * */
  public int add(int a,int b){
      return a+b;
  }
}

使用javadoc进行编译
使用ANSI编码
在这里插入图片描述

在这里插入图片描述
② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

JDK中预定义的一些注解

@Override	:检测被该注解标注的方法是否是继承自父类(接口)的
@Depprecated:该注解标注的内容,表示已过时
@SuperessWarnings:压制警告
@SuppressWarnings("all")//压制类中所有警告
public class AnnoDemo2 {
    @Override
    public String toString() {
        return super.toString();
    }
    
    @Deprecated//过时注释
    public void show(){
        //有缺陷
    }
    
    public  void showMore(){
            //替代SHOW1方法
    }
    
    
}

自定义注解

自定义注解

格式:
	元注解
	public @interface 注解名称{}
本质:注解本质上就是一个接口,该接口继承Annotation接口

```java
public interface itcast.junit.Test.AnnoDemo3 extends java.lang.annotation.Annotation {
}
```

属性:接口中的抽象方法
	要求:
		1.属性的返回值类型
			基本数据类型
			String
			枚举
			注解
	2.定义了之后需要赋值
		1.如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
		2.如果只有一个属性需要赋值,并且属性名字为value,则value可以省略,直接定义值即可
		3.数组赋值时,值用{}包括。如果数组中只有一个值,则{}省略

元注解:

注解

public @interface AnnoDemo3 {
    int show();
    boolean flag();
    /*
    String show2();
    String[] per();
    */
}

主要

@AnnoDemo3(show = 1,flag = true)//赋值
public class AnnoDemo4 {
}

属性:接口中的抽象方法

	1.属性的返回值类型
		基本数据类型
		String
		枚举
		注解
		以上类型的数组
	
	2.定义了属性,在使用时需要给属性赋值
		1.如果定义属性时,使用Default关键字给属性默认初始化,则使用注解时,可以不进行属性的赋值
		2.如果只有一个属性需要复制,且属性的名称是Value,则value可以省略,直接定义值即可
		3.数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}省略

元注解:描述注解的注解
	@Target:描述注解能够作用的位置
		TYPE:作用于类上
		METHOD:可以作用于方法上
		FIELD:可以作用于成员变量上

注解

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

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface AnnoDemo5 {

}

测试

@AnnoDemo5
public class Worker {
    @AnnoDemo5
    public  String name ="testname";
    
    @AnnoDemo5
    public void show(){
        
    }
}
@Retention:描述注解被保留的阶段
	@Retention(RetentionPolicy.RUNTIME):
		当前被描述的注解,会保留到Class字节码文件中,并被JVM读取到
@Documented:描述直接是否抽取到API文档中

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface AnnoDemo5 {

}

@AnnoDemo5
public class Worker {
    @AnnoDemo5
    public  String name ="testname";

    @AnnoDemo5
    public void show(){

    }
}

javadoc

在这里插入图片描述
在这里插入图片描述

@Inherited:描述注解是否被子类继承
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
@Documented
@Inherited
public @interface AnnoDemo5 {

}

子类

public class Teacher extends  Worker{

}

在程序使用(解析)注解:获取注解中定义的属性值

接口可以存放的方法:

    public abstract String show();

1.获取注解定义的位置的对象:(Class,Method,Field)
2.获取指定的注解
getAnnotation(Class)

        //其实就是在内存中生成了一个该注解接口的子类实现对象
            /*
        public class ProImp1 implements Pro{
            public String className(){
                return "itcast.junit.Test.Demo1"';
            }
            public String methodName(){
                return "show"';
            }
        }
    * */

3.调用注解中的抽象方法获取配置的属性值

注解

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    /*
    描述需要执行的类名和方法名
    * */

    String className();
    String methodName();


    /*
        public class ProImp1 implements Pro{
            public String className(){
                return "itcast.junit.Test.Demo1"';
            }
            public String methodName(){
                return "show"';
            }
        }
    * */
}

正常使用

@Pro(className = "itcast.junit.Test.Demo1",methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意对象,执行任意方法


        //1.解析注解
        //1.1就获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;

        //2.获取上边的注解对象
        //其实就是在内存中生成了一个该注解接口的子类实现对象
            /*
        public class ProImp1 implements Pro{
            public String className(){
                return "itcast.junit.Test.Demo1"';
            }
            public String methodName(){
                return "show"';
            }
        }
    * */
        Pro an = reflectTestClass.getAnnotation(Pro.class);

        //3.调用注解对象中定义的抽象方法,获取返回值
        String ClassName = an.className();
        String methodName = an.methodName();
        System.out.println(ClassName+"      "+methodName);

        // 3.加载该类进内存
        Class cls = Class.forName(ClassName);

        //4.创建对象
        Object obj = cls.newInstance();

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

        //6.执行方法
        method.invoke(obj);
    }
}

输出

itcast.junit.Test.Demo1      show
demo1...show..

测试框架

小结:
1.以后大多时候,我们会使用注解,而不是自定义注解
2.注解给谁用?
1.编辑器
2.解析程序
3.注解不是程序的一部分,可以理解为注解就是一个标签

定义类

public class Calculator {
    @Check
    public void add(){
        System.out.println("1 + 0"+(1+0));
    }
    @Check
    public void sub(){
        System.out.println("1 - 0"+(1-0));
    }
    @Check
    public void mul(){
        System.out.println("1 * 0"+(1*0));
    }
    @Check
    public void div(){
        System.out.println("1 / 0"+(1/0));
    }
    public void show(){
        System.out.println("没有BUG");
    }
}

注解

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Check {
}

主要

/*
    简单的测试框架
    当主方法执行后,会自动自行被检测的所有方法(加了注解),判断是否有异常,然后记录到文件中
* */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //创建对象
        Calculator c = new Calculator();

        //获取字节码文件对象
        Class cls = c.getClass();

        //出现异常的次数
        int number = 0;
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        //获取所有方法
        java.lang.reflect.Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if(method.isAnnotationPresent(Check.class)){
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    number++;
                    bw.write(method.getName()+" 方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称"+e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因"+e.getCause().getMessage());
                    bw.newLine();
                    bw.write("-------------------------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现"+number+"次异常");
        bw.flush();
        bw.close();
        //判断方法上石佛有相应注解

        //有,执行

        //捕获异常

    }
}

输出

1 + 01
1 * 00
1 - 01

bug.txt

div 方法出异常了
异常的名称ArithmeticException
异常的原因/ by zero
-------------------------------------
本次测试一共出现1次异常