【Java进阶】Day11

Junit 单元测试

通过导入Junit模块可以使我们可以独立运行方法。
导入方法:在方法上方添加注解@Test然后Alt+回车跳出提示,选择将’JUnit4’添加到类路径中。
image.png
导入之后就可以直接执行该方法了,需要注意的是这个方法不能为static
image.png
如果正常运行下方会显示绿色,失败会显示红色。正常运行不代表结果会是我们想要的结果。在Junit测试中,使用断言(即断定结果是多少)的方法去判断运算是否正常。
下面我使用了Assert.assertEquals(期望值,运算结果,偏差)来判断程序输出是否是我想要的,这里我期望程序结果是0.3,但实际上是不精确的小数,就提示我运算不正确。
image.png
除了浮点数的比较,Junit还提供了其他类型的比较。
image.png

建议:

在使用Junit之前,我们需要创建一个测试类和测试方法。在命名方面有些建议,用于大型项目中告知是测试用例。

定义测试类(测试用例)

测试类名:被测试的类名 功能名+Test,CalculatorTest
包名:xxx.xxx.xx.test,cn.itcast.test

定义测试方法

方法名:test方法名,testAdd
返回值:void
参数列表:空参

生命周期

我们可以使用注解@Before@After来控制初始化方法和释放资源的方法。
image.png

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

反射

框架设计的灵魂,如果需要写框架就需要深入学习反射。
反射:将类的各个组成部分封装为其他对象。让我们在程序运行过程中可以操作这些对象,并且经过封装后可以降低耦合,提高程序的扩展性。
字节码文件(.class),通过类加载器加载Class类对象,这个Class对象用于描述字节码文件。Class 是一个特殊的Java类,它可以保存类运行时的标识(类的一系列信息)。
image.png
image.png
image.png

操作成员变量

在测试之前创建了一个命名为Person的实体类,并定义了两个私有成员变量和一个公开成员变量。
image.png
image.png
获取类里面所有 public 修饰的变量

// getFields 方法
Class<Person> pc = Person.class;
Field[] pcField = pc.getFields();
for (Field f : pcField){
    System.out.println(f);
}
// getField 方法
Field sex = pc.getField("sex");
System.out.println(sex);
/*
* 输出:
* public int com.learn.obj.Person.sex
* public int com.learn.obj.Person.sex
* */

获取类所有变量,无视权限修饰符。

// getDeclaredFields 方法
Class<Person> pc = Person.class;
Field[] pcField = pc.getDeclaredFields();
for (Field f : pcField){
    System.out.println(f);
}
// getDeclaredField 方法
Field sex = pc.getDeclaredField("sex");
System.out.println(sex);
/*
* 输出:
* private int com.learn.obj.Person.age
* private java.lang.String com.learn.obj.Person.name
* public int com.learn.obj.Person.sex
* public int com.learn.obj.Person.sex
* */

image.png
操作 public 修饰的成员变量。

Class<Person> pc = Person.class;
Field publicValueSex = pc.getField("sex");
Person p = new Person();
publicValueSex.set(p, 1);
Object po = publicValueSex.get(p);
System.out.println(po);
/*
* 输出:1
* */

无视权限修饰符操作成员变量。

Class<Person> pc = Person.class;
Field privateValueAge = pc.getDeclaredField("age");
// 设置访问权限为 True
privateValueAge.setAccessible(true);
Person p = new Person();
privateValueAge.set(p, 23);
Object po = privateValueAge.get(p);
System.out.println(po);
/*
* 输出:23
* */

操作构造方法

为了方便测试还是使用Person实体类,在其中定义了三个构造方法,帮助我们初始化那三个变量。
image.png
除此之外还重写了toString方法。
image.png
image.png
获取所有 public 修饰的构造方法。

// getConstructors 方法
Class<Person> pc = Person.class;
Constructor[] pcConstructors = pc.getConstructors();
for (Constructor c : pcConstructors){
    System.out.println(c);
}
// getConstructor 方法
Constructor<Person> pc2 = pc.getConstructor(int.class, String.class);
System.out.println(pc2);
/*
* 输出:
* public com.learn.obj.Person(int,java.lang.String,int)
* public com.learn.obj.Person(int,java.lang.String)
* public com.learn.obj.Person()
* public com.learn.obj.Person(int,java.lang.String)
* */

image.png

Class<Person> pc = Person.class;
Constructor pConstructor = pc.getConstructor();
Object po = pConstructor.newInstance();
System.out.println(po);

// 简化操作已弃用
Person person = pc.newInstance();
System.out.println(person);
/*
* 输出:
* Person{age=0, name='null'}
* Person{age=0, name='null'}
* */

操作成员方法

实体类Person中针对agename都设置了setget方法。除此之外我再新增一个方法用来同时输出这两个变量。
image.png
image.png
获取类中所有权限为 public 的方法。

// getMethods 方法
Class<Person> pc = Person.class;
Method[] pcMethods = pc.getMethods();
for (Method m : pcMethods){
    // 通过 getName 获取方法名称
    System.out.println(m.getName());
}
// getMethod 方法
Method sayHello = pc.getMethod("sayHello");
System.out.println(sayHello);
/*
输出:
getName
toString
setName
sayHello
setAge
getAge
equals
hashCode
getClass
notify
notifyAll
wait
wait
wait
public void com.learn.obj.Person.sayHello()
*/

image.png
使用 invoke 执行方法。

Class<Person> pc = Person.class;
Method sayHello = pc.getMethod("sayHello");
Person p = new Person(24, "大牛");
sayHello.invoke(p);
// 获取重载方法
Method sayHello2 = pc.getMethod("sayHello", String.class);
sayHello2.invoke(p, "数学");
/*
输出:
你好,我是大牛,我今年24岁了。
你好,我是大牛,我今年24岁了,还在学习数学
*/

获取类名

可以使用getName方法获取类的全称(包名+类名)。

Class<Person> pc = Person.class;
String name = pc.getName();
System.out.println(name);
/*
输出:
com.learn.obj.Person
*/

注解

大多数时候,我们使用注解,而不是自定义注解。
注解配合注解解析器来帮助我们完成一些事情,例如替代配置文件IO读写、在编译器处检查代码是否有异常、生成API文档等。注解并不是程序的一部分,可以理解为注解就是一个标签。

内置注解

  1. @SuppressWarnings压制警告

一般是给class上使用,且传递all参数。@SuppressWarnings("all")即是压制所有警告。

  1. @Override检测被该注解标注的方法是否是继承自父类重写的。
  2. @Deprecated被该注解标注的内容,表示已过时。

自定义注解

image.png
存在一个命名为EnumPerson的枚举。

public enum EnumPerson {
    Poor,
    Rich
}

存在一个没有任何意义的注解public @interface MyAnno2 { }
演示注解可以拥有的属性值数据类型。

public @interface MyAnno {
    int[] count() default {1,3};
    String[] name() default {"我","你"};
    EnumPerson value() default EnumPerson.Poor;
    Class<MyAnno2> mya() default MyAnno2.class;
}

image.png
元注解我们一般只使用两个,分别是@Target@Retention

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
    int[] count() default {1,3};
    String[] name() default {"我","你"};
    EnumPerson value() default EnumPerson.Poor;
    Class<MyAnno2> mya() default MyAnno2.class;
}

解析注解

在程序中使用(解析)注解,主要目的是获取注解中定义的属性值。
下面演示一个代替配置文件的作用案例。我们将编写一个登录功能,我希望有正确用户名和密码的默认值。下方定义了一个MyAnno注解,其中有两个属性,且只能标注于类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
    String username() default "admin";
    String password() default "123456";
}

编写登录功能,其中输入的用户名和密码从Scanner进来,而默认正确的用户名和密码从形参中获得。

@MyAnno
public class Login {
    public static void login(String defaultUser,String defaultPass) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你的用户名:");
        String user = sc.nextLine();
        System.out.println("请输入你的密码:");
        String pass = sc.nextLine();
        if (user.equals(defaultUser) && pass.equals(defaultPass)){
            System.out.println("pass");
        } else {
            System.out.println("error");
        }
    }
}
  1. 创建了一个测试类,通过类名.class的方式获取了Login的Class对象。
  2. 通过isAnnotationPresent方法判断是否被标注了@MyAnno
  3. 通过getAnnotation获得接口MyAnno的实现类,类似于下面的代码
public class MyAnnoImpl implements MyAnno{
    public String username(){return "admin"};
		public String password(){return "123456"};
}
  1. 同调用usernamepasswordlogin方法传递形参。
public class LoginTest {
    public static void main(String[] args) {
        Class<Login> loginClass = Login.class;
        if (loginClass.isAnnotationPresent(MyAnno.class)){
            MyAnno myAnno = loginClass.getAnnotation(MyAnno.class);
            Login.login(myAnno.username(), myAnno.password());
        }

    }
}

image.png
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值