Java面向对象(高级)-- 注解(Annotation)

文章目录

  • 一、 注解概述
    • (1) 什么是注解
    • (2) 注解与注释
    • (3) 注解的重要性
  • 二、常见的Annotation作用
    • (1)示例1
    • (2)示例2
    • (3)示例3
  • 三、 三个最基本的注解
    • (1) @Override
    • (2) @Deprecated
    • (3) @SuppressWarnings
  • 四、元注解
    • (1)介绍
    • (2)讲解
      • 2.1 @Target
      • 2.2 @Retention
      • 2.3 @Documented
      • 2.4 @Inherited
    • (3)示例
  • 五、 自定义注解的使用
    • (1) 声明自定义注解
    • (2)举例
      • 2.1 举例1
      • 2.2 举例2
    • (3)读取和处理自定义注解
  • 六、JUnit单元测试
    • (1)测试分类
    • (2)JUnit单元测试介绍
    • (3) 引入本地JUnit.jar
    • (4)测试
      • 4.1 测试1
      • 4.2 测试2
    • (5)编写和运行@Test单元测试方法
    • (6) 设置执行JUnit用例时支持控制台输入
    • (7) 定义test测试方法模板

一、 注解概述

(1) 什么是注解

注解(Annotation)是从JDK5.0开始引入,以“@注解名”在代码中存在。例如:

①修饰的方法是父类中重写的方法

@Override

②相应结构过时

@Deprecated

③编译器一直警告

@SuppressWarnings(value=”unchecked”)

注解Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。

注解可以在类编译、运行时进行加载,体现不同的功能。

框架 = 注解 + 反射 + 设计模式

(2) 注解与注释

注解也可以看做是一种注释,通过使用 Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。但是,注解,不同于单行注释和多行注释。

  • 对于单行注释多行注释是给程序员看的。(编译形成的字节码文件中没有单行、多行注释)
  • 注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同,做出相应的处理。

(3) 注解的重要性

JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码XML配置等。

未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,Struts2有一部分也是基于注解的了。注解是一种趋势,一定程度上可以说:框架 = 注解 + 反射 + 设计模式

二、常见的Annotation作用

(1)示例1

示例1:生成文档相关的注解

@author 标明开发该类模块的作者,多个作者之间使用,分割
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception 对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写

比如:

package com.annotation.javadoc;
/**
 * @author 雨翼轻尘
 * @version 1.0
 * @see Math.java
 */
public class JavadocTest {
	/**
	 * 程序的主方法,程序的入口
	 * @param args String[] 命令行参数
	 */
	public static void main(String[] args) {
	}
	
	/**
	 * 求圆面积的方法
	 * @param radius double 半径值
	 * @return double 圆的面积
	 */
	public static double getArea(double radius){
		return Math.PI * radius * radius;
	}
}

(2)示例2

示例2:在编译时进行格式检查(JDK内置的三个基本注解)

@Override: 限定重写父类方法,该注解只能用于方法。

@Deprecated: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。

@SuppressWarnings: 抑制编译器警告。

package com.annotation.javadoc;
 
public class AnnotationTest{
 
	public static void main(String[] args) {
		@SuppressWarnings("unused")
		int a = 10;
	}
	@Deprecated
	public void print(){
		System.out.println("过时的方法");
	}
 
	@Override
	public String toString() {
		return "重写的toString方法()";
	}
}

(3)示例3

示例3:跟踪代码依赖性,实现替代配置文件功能

  • Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) { }
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        doGet(request, response);
	}  
}
 <servlet>
    <servlet-name>LoginServletservlet-name>
    <servlet-class>com.servlet.LoginServletservlet-class>
  servlet>
  <servlet-mapping>
    <servlet-name>LoginServletservlet-name>
    <url-pattern>/loginurl-pattern>
  servlet-mapping>
  • Spring框架中关于“事务”的管理
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,readOnly=false,timeout=3)
public void buyBook(String username, String isbn) {
	//1.查询书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);
    //2. 更新库存
    bookShopDao.updateBookStock(isbn);	
    //3. 更新用户的余额
    bookShopDao.updateUserAccount(username, price);
}

<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
       <tx:attributes>
       
       <tx:method name="buyBook" propagation="REQUIRES_NEW" 
	 isolation="READ_COMMITTED"  read-only="false"  timeout="3" />
       tx:attributes>
tx:advice>

三、 三个最基本的注解

Java基础涉及到的三个常用注解:

  • @Override: 限定重写父类方法,该注解只能用于方法。
  • @Deprecated: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。
  • @SuppressWarnings: 抑制编译器警告。

(1) @Override

  • 用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误!
  • 只能标记在方法上
  • 它会被编译器程序读取。

【案例】

class Person{
    String name;
    int age;

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

class Student extends Person{
    @Override
    public void eat(){
        System.out.println("学生吃饭");
    }
}

如果将@Override去掉,他其实也可以。

如果写了这个注解,它就会显示地去校验一下这个方法是不是重写。

比如现在将注解删除,然后将重写的方法写错了,此时不会报错,如下:

Java面向对象(高级)-- 注解(Annotation)_第1张图片

此时它和父类中的eat()就是两个不同的方法了,若此时试图加上注解,就会报错,如下:

Java面向对象(高级)-- 注解(Annotation)_第2张图片

所以这里就相当于一个保护机制,如果写错了就会报错。

(2) @Deprecated

  • 用于表示被标记的数据已经过时,不推荐使用,但仍可以使用。
  • 可以用于修饰 属性、方法、构造、类、包、局部变量、参数。
  • 它会被编译器程序读取。

【案例】

package yuyi07;

import java.util.Date;

public class AnnotationTest {
    public static void main(String[] args) {
        //日期Date的API
        Date date =new Date();  //util下的Date表示的是一个日期类
        System.out.println(date);   //获取当前时间
    }
}

输出结果:

image.png

按住Ctrl点击进入Date中,然后Ctrl+F12,如下:

Java面向对象(高级)-- 注解(Annotation)_第3张图片

这些加了横线的就是加了@Deprecated标识,表示已经过时了,就是不推荐使用它,但是还是可以用它的。如下:

Java面向对象(高级)-- 注解(Annotation)_第4张图片

比如此时想用一下上面这个构造器:

Java面向对象(高级)-- 注解(Annotation)_第5张图片

打印一下:

Java面向对象(高级)-- 注解(Annotation)_第6张图片

可以看到这里是3924年,这里有一个偏移量,在1900的基础上加上2023,如下:(为啥我的编译器是3924,搞不明白)

Java面向对象(高级)-- 注解(Annotation)_第7张图片

API中可以看到(将鼠标光标放在画横线的Date上即可),这个Date被它们替换了,如下:

Java面向对象(高级)-- 注解(Annotation)_第8张图片


其实我们自己也可以用。

比如在Person构造器上写一个@Deprecated,表示这个构造器不推荐使用。

class Person{
    String name;
    int age;

    public Person(){

    }

    @Deprecated
    public Person(String name){
        this.name=name;
    }

}

但是仍然可以使用,比如:

public class AnnotationTest {
    public static void main(String[] args) {
        Person p2=new Person();
        Person p3=new Person("Tom");    //此构造器已经过时,不建议使用,但仍然可以使用
        System.out.println(p3);

    }
}

class Person{
    String name;
    int age;

    public Person(){

    }

    @Deprecated
    public Person(String name){
        this.name=name;
    }


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

输出结果:

image.png

这个不推荐的意思就是,它在以后的某个版本中可能会被删除。

image.png

这个注解的意义就在于过渡jdk版本的更新,防止更新版本之后,之前的代码不能用了,所以以后写代码的时候尽量避开这些过时的。

(3) @SuppressWarnings

  • 抑制编译警告。当我们不希望看到警告信息的时候,可以使用 SuppressWarnings 注解来抑制警告信息 。
  • 可以用于修饰类、属性、方法、构造、局部变量、参数
  • 它会被编译器程序读取。
  • 可以指定的警告类型有(了解)
    • all,抑制所有警告
    • unchecked,抑制与未检查的作业相关的警告
    • unused,抑制与未用的程式码及停用的程式码相关的警告
    • deprecation,抑制与淘汰的相关警告
    • nls,抑制与非 nls 字串文字相关的警告
    • null,抑制与空值分析相关的警告
    • rawtypes,抑制与使用 raw 类型相关的警告
    • static-access,抑制与静态存取不正确相关的警告
    • static-method,抑制与可能宣告为 static 的方法相关的警告
    • super,抑制与置换方法相关但不含 super 呼叫的警告

示例代码:

package com.atguigu.annotation;

import java.util.ArrayList;

public class TestAnnotation {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        int i;

        ArrayList list = new ArrayList();
        list.add("hello");
        list.add(123);
        list.add("world");

        Father f = new Son();
        f.show();
        f.methodOl();
    }
}

class Father{
    @Deprecated
    void show() {
        System.out.println("Father.show");
    }
    void methodOl() {
        System.out.println("Father Method");
    }
}

class Son extends Father{
/*	@Override
	void method01() {
		System.out.println("Son Method");
	}*/
}

比如编译器右边就会有橙色的警告信息,可以让它不显示。

比如现在写个语句,右边会有橙色警告:

image.png

点击“更多操作”:

image.png

放在语句上面:

Java面向对象(高级)-- 注解(Annotation)_第9张图片

系统就会自动加上:

@SuppressWarnings("unused") int num=10;

表示当前对象定义了没有使用:

Java面向对象(高级)-- 注解(Annotation)_第10张图片

四、元注解

(1)介绍

【元注解的理解】

JDK1.5在java.lang.annotation包定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。
元注解:对现有的注解进行解释说明的注解

比如这两个就是元注解:(修饰现有注解SuppressWarnings的注解)

Java面向对象(高级)-- 注解(Annotation)_第11张图片

(2)讲解

【讲4个元注解】

2.1 @Target

@Target:用于描述注解的使用范围

  • 可以通过枚举类型ElementType的10个常量对象来指定
  • TYPE,METHOD,CONSTRUCTOR,PACKAGE…

Java面向对象(高级)-- 注解(Annotation)_第12张图片

可以看到Target里面有数组,可以存多个值,比如:@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})

ElementType[] value();:Target里面有一个ElementType类型的数组,点进去看一下:

Java面向对象(高级)-- 注解(Annotation)_第13张图片

可以看到,ElementType是一个枚举类,里面有很多对象,比如TYPE就可以表示这么多意思:

image.png

还有这些:

Java面向对象(高级)-- 注解(Annotation)_第14张图片

所以在SupperWarnings里面,它的Target注解就表示SupperWarnings注解可以用来修饰哪些结构

Java面向对象(高级)-- 注解(Annotation)_第15张图片

Target注解用来表明当前注解可以修饰的结构。

2.2 @Retention

@Retention:用于描述注解的生命周期

  • 可以通过枚举类型RetentionPolicy的3个常量对象来指定
  • SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
  • 唯有RUNTIME阶段才能被反射读取到。

Java面向对象(高级)-- 注解(Annotation)_第16张图片

可以看到Retention里面有一个RetentionPolicy类型,按住Ctrl将它点开,如下:

Java面向对象(高级)-- 注解(Annotation)_第17张图片

可以看到,它也是一个枚举类。有3个对象。

SOURCE,在编译器编译的时候就被处理了。

如果RetentionPolicy修饰的注解是SOURCE的话,对应修饰的注解(比如SuppressWarnings)生成的字节码文件中是没有它的。

Java面向对象(高级)-- 注解(Annotation)_第18张图片

CLASS,就是字节码文件。这个被修饰的注解会被保留在字节码文件中,但是不会在虚拟机运行的时候保留(往内存中加载的时候就不考虑它了),这是一个默认的行为。

RUNTIME。在CLASS基础上还希望在运行的时候也保留,就得用它。

其实就是指明注解的生命周期。

2.3 @Documented

@Documented:表明这个注解应该被javadoc工具记录。

按住Ctrl键点击@Deprecated

Java面向对象(高级)-- 注解(Annotation)_第19张图片

可以看到它上面有@Documented。如下:

Java面向对象(高级)-- 注解(Annotation)_第20张图片

这就意味着,@Deprecated这个注解修饰的结构Person类,用javadoc解析之后的说明文档html文件,在文档里面能看到@Deprecated这个注解。因为它用@Documented修饰了。

再比如Date这个文档里面有显示:

Java面向对象(高级)-- 注解(Annotation)_第21张图片

再比如SuppressWarnings没有用@Documented修饰,所以它就不会保留在javadoc里面,如下:

Java面向对象(高级)-- 注解(Annotation)_第22张图片

2.4 @Inherited

@Inherited:允许子类继承父类中的注解

若是用它去修饰,比如修饰我们自己写的MyAnnotation注解,那么在Person里面使用了@MyAnnotation,它就具备了继承性,言外之意就是Person它的子类Student上面相当于也有一个MyAnnotation注解。

直接被子类所继承了。

Java面向对象(高级)-- 注解(Annotation)_第23张图片

(3)示例

示例代码:

package java.lang;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

拓展:元数据

String name = “Tom”; 最核心的是值“Tom”,前面两个是用来修饰值的结构,一个指定类型,一个指定名称。这时候String和name就可以看作Tom数据的元数据。

五、 自定义注解的使用

一个完整的注解应该包含三个部分:
(1)声明
(2)使用
(3)读取

(1) 声明自定义注解

【元注解】
【修饰符】 @interface 注解名{
    【成员列表】
}
  • 自定义注解可以通过四个元注解@Retention,@Target,@Inherited,@Documented,分别说明它的声明周期,使用位置,是否被继承,是否被生成到API文档中。
  • Annotation 的成员在 Annotation 定义中以无参数有返回值的抽象方法的形式来声明,我们又称为配置参数。返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组
  • 可以使用 default 关键字为抽象方法指定默认返回值
  • 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值。格式是“方法名 = 返回值”,如果只有一个抽象方法需要赋值,且方法名为value,可以省略“value=”,所以如果注解只有一个抽象方法成员,建议使用方法名value。

比如:

package com.atguigu.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}
package com.atguigu.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String columnName();
    String columnType();
}

(2)举例

2.1 举例1

再看一下这个例子:

package yuyi07;

import java.util.Date;

/**
 * ClassName: AnnotationTest
 * Package: yuyi07
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/12/5 0005 16:00
 */
public class AnnotationTest {
    public static void main(String[] args) {
        //日期Date的API
        Date date =new Date();  //util下的Date表示的是一个日期类
        System.out.println(date);   //获取当前时间

        Date date1=new Date(2023,12,5);
        System.out.println(date1);

        Person p2=new Person();
        Person p3=new Person("Tom");    //此构造器已经过时,不建议使用,但仍然可以使用
        System.out.println(p3);

        @SuppressWarnings({"unused"}) int num=10;

    }
}

class Person{
    String name;
    int age;

    public Person(){

    }

    @Deprecated
    public Person(String name){
        this.name=name;
    }

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

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

class Student extends Person{
    @Override
    public void eat(){
        System.out.println("学生吃饭");
    }
}

@SuppressWarnings为参照,如下:

Java面向对象(高级)-- 注解(Annotation)_第24张图片

注解使用@interface这种结构来定义的。(注意与接口没有关系)

下面我们自己写一个

public @interface MyAnnotation {

}

参照@SuppressWarnings,它里面写的String[] value();其实是一个属性,这个属性是一个String型的数组,可以有多个值的意思。

就比如刚才只赋了一个值,如果还有可以继续赋值。如下:

image.png

现在我们自己写,也可以提供属性

比如String value();,这里咱们就不写数组了,直接就是String类型的,注意value后面有个小括号,按照格式来就行。

属性名就叫value

定义结束。

在哪里用呢?注解使用的位置很灵活。

比如现在想在Person类上使用。

直接输入@MyAnnotation,如下:

Java面向对象(高级)-- 注解(Annotation)_第25张图片

然后传入一个属性值,比如value="class",表示这是一个类。如下:

Java面向对象(高级)-- 注解(Annotation)_第26张图片

其实我们也可以给注解的属性提供一个默认值,比如“hello”:

public @interface MyAnnotation {
    String value() default "hello";
}

所以就有了这两种情况:

Java面向对象(高级)-- 注解(Annotation)_第27张图片

定义注解是后期你想让它干什么,然后配合“反射”的方式去用它,光写在那里就只是一个标识而已。

现在感受一下就行,后期讲反射的时候再用。


<1> Target

现在将这个Target注解粘贴过来:

Java面向对象(高级)-- 注解(Annotation)_第28张图片

如下:

package yuyi07;

import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

/**
 * ClassName: MyAnnotation
 * Package: yuyi07
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/12/6 0006 10:21
 */

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
public @interface MyAnnotation {
    String value() default "hello";
}

这里表示MyAnnotation注解可以修饰上面Target注解里面的结构

此时咱们将Target注解改造一下,删除一些可以修饰的结构:@Target({FIELD, METHOD,CONSTRUCTOR})

然后就有一些不能修饰的结构,就会报错,如下:

Java面向对象(高级)-- 注解(Annotation)_第29张图片

Target注解用来表明当前注解可以修饰的结构。


<2> Retention

然后我们再将Retention粘过来:

Java面向对象(高级)-- 注解(Annotation)_第30张图片

如下:

Java面向对象(高级)-- 注解(Annotation)_第31张图片

若是CLASS,那么在字节码文件中有MyAnnotation注解,而在运行时没有它(不会把它加载到内存中)。

Java面向对象(高级)-- 注解(Annotation)_第32张图片

后边会说到“反射”,在程序运行的过程中,使用反射的API调用注解,所以必须要保证运行的时候有。就是要将注解改成RUNTIME,让它运行的时候也要在,才能通过反射拿到它。

Retention注解就是表示当前注解的生命周期。

2.2 举例2

注解:

package com.atguigu.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}
package com.atguigu.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String columnName();
    String columnType();
}

【Student.java】

package com.atguigu.annotation;

@Table("t_stu")
public class Student {
    @Column(columnName = "sid",columnType = "int")
    private int id;
    @Column(columnName = "sname",columnType = "varchar(20)")
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

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

(3)读取和处理自定义注解

自定义注解必须配上注解的信息处理流程才有意义。

我们自己定义的注解,只能使用反射的代码读取。所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。

具体的使用后边讲反射再说。

六、JUnit单元测试

(1)测试分类

黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。(一般用来测试功能)

白盒测试:需要写代码的。关注程序具体的执行流程。
Java面向对象(高级)-- 注解(Annotation)_第33张图片

Java面向对象(高级)-- 注解(Annotation)_第34张图片

(2)JUnit单元测试介绍

JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个测试框架(regression testing framework),供Java开发人员编写单元测试之用。

JUnit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。

以前我们写代码都是在main方法中测试的,但是在main方法中测试有一些不方便,比如想在后面测试其他的,与前面的代码无关,就要注释前面的代码,很麻烦。

这个时候我们就可以引用单元测试来替换原有的main方法

要使用JUnit,必须在项目的编译路径中引入JUnit的库,即相关的.class文件组成的jar包。

jar就是一个压缩包,压缩包都是开发好的第三方(Oracle公司第一方,我们自己第二方,其他都是第三方)工具类,都是以class文件形式存在的。

(3) 引入本地JUnit.jar

需要导入的jar包

  • junit-4.12.jar
  • hamcrest-core-1.3.jar

①第1步

在项目结构File-Project Structure中操作:添加Libraries库

Java面向对象(高级)-- 注解(Annotation)_第35张图片

Java面向对象(高级)-- 注解(Annotation)_第36张图片

其中,junit-libs包内容如下:

Java面向对象(高级)-- 注解(Annotation)_第37张图片

junit使用需要依赖上面的

②第2步

选择要在哪些module中应用JUnit库

Java面向对象(高级)-- 注解(Annotation)_第38张图片

③第3步

检查是否应用成功

Java面向对象(高级)-- 注解(Annotation)_第39张图片

注意Scope:选择Compile(默认就是),否则编译时,无法使用JUnit。

④第4步

下次如果有新的模块要使用该libs库,这样操作即可

Java面向对象(高级)-- 注解(Annotation)_第40张图片

Java面向对象(高级)-- 注解(Annotation)_第41张图片

Java面向对象(高级)-- 注解(Annotation)_第42张图片

Java面向对象(高级)-- 注解(Annotation)_第43张图片

在编译器里面直接写@Test,编译器报错,选择直接下载即可。

(4)测试

4.1 测试1

可以看到,导入之后这里有刚才导入的jar包:

Java面向对象(高级)-- 注解(Annotation)_第44张图片

然后我们需要的是junit里面的Test结构。(具体细节不用关注)

Java面向对象(高级)-- 注解(Annotation)_第45张图片

然后我们在刚才关联的包下面创建java类,比如:

public class JUnitTest {
    @Test
    public void test1(){
        System.out.println("hello");
    }
}

可以看到左边有一个“运行”符号,点击它:

Java面向对象(高级)-- 注解(Annotation)_第46张图片

然后就可以看到输出:

Java面向对象(高级)-- 注解(Annotation)_第47张图片

这有什么特别的呢?

我们可以写多个,比如:

public class JUnitTest {
    @Test
    public void test1(){
        System.out.println("hello");
    }

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

在当前类里面还可以定义变量,这个变量还可以在方法里面调用,比如:

public class JUnitTest {
    int number=10;

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

    @Test
    public void test2(){
        System.out.println("world");
        System.out.println("number= "+number);
    }
}

这时候再点击左边的运行:

Java面向对象(高级)-- 注解(Annotation)_第48张图片

输出结果:

Java面向对象(高级)-- 注解(Annotation)_第49张图片

我们在执行这个单元测试方法的时候,跟上面那个测试没有关系,上面写的那个就不执行。

只会测试test2。

所以我们可以写很多单元测试方法。

还可以写一个普通的方法,这个方法就可以在单元测试里面使用,比如:

public class JUnitTest {
    int number=10;

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

    @Test
    public void test2(){
        System.out.println("world");
        System.out.println("number= "+number);

        method();
    }

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

test2()是非静态的,method()也是非静态的,非静态里面可以调非静态的,如下:

Java面向对象(高级)-- 注解(Annotation)_第50张图片

输出:

Java面向对象(高级)-- 注解(Annotation)_第51张图片


若是以前,number是一个非静态变量,method()是一个非静态的方法,如果想要测试,需要写在main方法里面。

又因为main方法是静态的,静态里面要是想调非静态方法,就需要造对象。

如下:

public static void main(String[] args) {
    JUnitTest test=new JUnitTest();
    System.out.println(test.number);
    test.method();
}

而现在test2是非静态的方法,就可以直接调用非静态方法method()了。

如下:

@Test
public void test2(){
    System.out.println("world");
    System.out.println("number= "+number);

    method();
}

所以很方便,而且想测试什么代码,就定义一个单独的单元测试即可。

这时候我们使用的注解@Test就是在刚才导入的jar包里面。

为啥用Main方法的时候需要造对象,而在单元测试类test2()里面就不需要了呢?

因为单元测试,在调用它的时候,它就会自动帮我们造,所以只需要写核心逻辑即可。

既然可以帮我们造对象,那么前提它要能造对象,所以所在类需要是非抽象的。


如果想在其他Module下面使用,比如在模块face06下面新建一个java类,可以看到报错了:

Java面向对象(高级)-- 注解(Annotation)_第52张图片

这是因为这个face06模块根本没有使用jar包。

怎么解决呢?

①如果现在你联网了,可以这样操作:

将鼠标放在报错的地方,然后按Alt+Enter,点击添加JUnit:

Java面向对象(高级)-- 注解(Annotation)_第53张图片

然后就点击“Ok”,就可以联网下载了。

②如果现在你没有联网,可以这样操作:

还是回到“项目结构”:

Java面向对象(高级)-- 注解(Annotation)_第54张图片

因为当前工程已经下载过jar包一次了,所以当前Module用一下即可。

具体操作如下:

Java面向对象(高级)-- 注解(Annotation)_第55张图片

然后添加:

Java面向对象(高级)-- 注解(Annotation)_第56张图片

然后就可以在这里看见Junit:

Java面向对象(高级)-- 注解(Annotation)_第57张图片

只是后面的是Test–“测试”,必须要改为Compile–“编译”。如下:

Java面向对象(高级)-- 注解(Annotation)_第58张图片

然后OK即可。


再次回到刚才的代码,可以看到可以使用了,如下:

Java面向对象(高级)-- 注解(Annotation)_第59张图片

单元测试方法,就是写代码不需要再用main方法了

4.2 测试2

package com.atguigu.junit;

import org.junit.Test;

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

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

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

Java面向对象(高级)-- 注解(Annotation)_第60张图片

(5)编写和运行@Test单元测试方法

(重点关注)

JUnit4版本,要想能正确的编写单元测试方法,要求@Test标记的方法必须满足如下要求:

  • 所在的(单元测试类)必须是public的,非抽象的(这个类可以造对象),包含唯一的无参构造器
  • @Test标记的方法(单元测试方法)本身必须是public非抽象的(肯定要是非抽象的,若是抽象的那就没有方法体,要它没用),非静态的(不能加static,直接拿对象去调用),void无返回值(和main方法一样,是程序的入口,不需要返回什么了),()无参数的。

【举例】

public class JUnitTest {    //单元测试类(包含单元测试方法)
    public static void main(String[] args) {
        JUnitTest test=new JUnitTest();
        System.out.println(test.number);
        test.method();
    }

    int number=10;

    @Test
    public void test1(){    //单元测试方法
        System.out.println("hello");
    }

    @Test
    public void test2(){
        System.out.println("world");
        System.out.println("number= "+number);

        method();
    }

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

为啥用Main方法的时候需要造对象,而在单元测试类test2()里面就不需要了呢?

因为单元测试,在调用它的时候,它就会自动帮我们造,所以只需要写核心逻辑即可。

既然可以帮我们造对象,那么前提它要能造对象,所以所在类需要是非抽象的。

同时只能有一个无参构造器

若是有多余的构造器,比如:

package yuyi08;

import org.junit.Test;

public class JUnitTest {    //单元测试类(包含单元测试方法)

    int number=10;

    public JUnitTest(){

    }

    public JUnitTest(int number){
        this.number=number;
    }

    @Test
    public void test1(){    //单元测试方法
        System.out.println("hello");
    }

}

运行会报错:

Java面向对象(高级)-- 注解(Annotation)_第61张图片

而无参构造器可以不写,所以干脆都别写了。

Java面向对象(高级)-- 注解(Annotation)_第62张图片


对于单元测试方法不要有参数,返回值,若是有参数也没有办法传。

但是普通方法是可以有的。

比如:

package yuyi08;

import org.junit.Test;

public class JUnitTest {    //单元测试类(包含单元测试方法)

    int number=10;

    public JUnitTest(){

    }

    @Test
    public void test2(){
        System.out.println("world");
        System.out.println("number= "+number);

        method();

        int num=showInfo("China");
        System.out.println(num);
    }

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

    public int showInfo(String info){   //可以有返回值类型,有形参,也可以有多个
        System.out.println(info);
        return 10;
    }
}

要测试的方法什么样的都有可能,看一下输出:

Java面向对象(高级)-- 注解(Annotation)_第63张图片

对于单元测试方法来说,不要有参数,若是有也没有办法给它传实参。

这里只是调用执行一下而已。

这些要求不必要都记住,只需要造完一个类,然后在里面写单元测试方法的时候,直接用public void 名字(){ }即可。关于类也不用提供构造器,直接使用无参的即可。

(6) 设置执行JUnit用例时支持控制台输入

默认情况下,单元测试方法中使用Scanner失效

比如,现在有一个单元测试方法test3(),如下:

package yuyi08;

import org.junit.Test;

import java.util.Scanner;

public class JUnitTest {    //单元测试类(包含单元测试方法)

    @Test
    public void test3(){
        Scanner scan=new Scanner(System.in);
        System.out.println("请输入一个值");
        int num=scan.nextInt();
        System.out.println(num);
    }

}

运行test3()的时候,可以发现没有办法从键盘输入数据进去

Java面向对象(高级)-- 注解(Annotation)_第64张图片

可以看到一直在转圈,没有办法输入数据。


☕如何解决?

1. 设置数据

默认情况下,在单元测试方法中使用Scanner时,并不能实现控制台数据的输入。需要做如下设置:

idea64.exe.vmoptions配置文件中加入下面一行设置,重启idea后生效。

-Deditable.java.test.console=true

2. 配置文件位置

Java面向对象(高级)-- 注解(Annotation)_第65张图片

Java面向对象(高级)-- 注解(Annotation)_第66张图片

添加完成之后,重启IDEA即可。

Java面向对象(高级)-- 注解(Annotation)_第67张图片

重启:

Java面向对象(高级)-- 注解(Annotation)_第68张图片

3. 如果上述位置设置不成功,需要继续修改如下位置

修改位置1:IDEA安装目录的bin目录(例如:D:\develop_tools\IDEA\IntelliJ IDEA 2022.1.2\bin)下的idea64.exe.vmoptions文件。

修改位置2:C盘的用户目录C:\Users\用户名\AppData\Roaming\JetBrains\IntelliJIdea2022.1 下的idea64.exe.vmoptions`件。


再回到最初的代码,就可以输入啦。如下:

Java面向对象(高级)-- 注解(Annotation)_第69张图片

按下回车之后:

Java面向对象(高级)-- 注解(Annotation)_第70张图片

(7) 定义test测试方法模板

以后写代码会经常写“单元测试方法”,大家可以将单元测试方法设置为一个模板

步骤:

①“文件”–> “设置”

Java面向对象(高级)-- 注解(Annotation)_第71张图片

②“实时模板”–> “模板组”

建议先创建一个模板组,如下:

Java面向对象(高级)-- 注解(Annotation)_第72张图片

③给组取个名字,比如“用户自定义组”–CustomDefine,如下:

Java面向对象(高级)-- 注解(Annotation)_第73张图片

④在组里面定义一个“模板”,如下:

Java面向对象(高级)-- 注解(Annotation)_第74张图片

⑤写模板

Java面向对象(高级)-- 注解(Annotation)_第75张图片

比如模板是这样:

Java面向对象(高级)-- 注解(Annotation)_第76张图片

写完一回车,希望光标在哪里,就写两个$符号,在中间写var2即可,若是最后,可以写end,比如:

@Test
public void test$var1$(){
    $end$
}

注意写完之后,一定要选择“在Java相关逻辑中使用”,如下:

Java面向对象(高级)-- 注解(Annotation)_第77张图片


OK,那么以后想写单元测试方法,可以直接用刚才我们写的快捷方式,输入“test”,然后就会自动显示我们写的模板,如下:

Java面向对象(高级)-- 注解(Annotation)_第78张图片

然后点击它即可,自动写出模板:

Java面向对象(高级)-- 注解(Annotation)_第79张图片

【总结】

选中自定义的模板组,点击”+”(1.Live Template)来定义模板。

Java面向对象(高级)-- 注解(Annotation)_第80张图片

你可能感兴趣的:(Java基础,java,面向对象(高级),注解,Annotation,JUnit单元测试)