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>LoginServlet</servlet-name>
    <servlet-class>com.servlet.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-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去掉,他其实也可以。

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

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

image.png

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

image.png

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

(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,如下:

image.png

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

image.png

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

image.png

打印一下:

image.png

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

image.png

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

image.png


其实我们自己也可以用。

比如在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

放在语句上面:

image.png

系统就会自动加上:

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

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

image.png

四、元注解

(1)介绍

【元注解的理解】

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

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

image.png

(2)讲解

【讲4个元注解】

2.1 @Target

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

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

image.png

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

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

image.png

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

image.png

还有这些:

image.png

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

image.png

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

2.2 @Retention

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

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

image.png

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

image.png

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

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

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

image.png

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

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

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

2.3 @Documented

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

按住Ctrl键点击@Deprecated

image.png

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

image.png

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

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

image.png

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

image.png

2.4 @Inherited

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

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

直接被子类所继承了。

image.png

(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为参照,如下:

image.png

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

下面我们自己写一个

public @interface MyAnnotation {

}

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

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

image.png

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

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

属性名就叫value

定义结束。

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

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

直接输入@MyAnnotation,如下:

image.png

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

image.png

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

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

所以就有了这两种情况:

image.png

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

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


<1> Target

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

image.png

如下:

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

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

image.png

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


<2> Retention

然后我们再将Retention粘过来:

image.png

如下:

image.png

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

image.png

后边会说到“反射”,在程序运行的过程中,使用反射的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)测试分类

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

白盒测试:需要写代码的。关注程序具体的执行流程。
image.png

image.png

(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库

image.png

image.png

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

image.png

junit使用需要依赖上面的

②第2步

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

image.png

③第3步

检查是否应用成功

image.png

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

④第4步

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

image.png

image.png

image.png

image.png

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

(4)测试

4.1 测试1

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

image.png

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

image.png

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

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

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

image.png

然后就可以看到输出:

image.png

这有什么特别的呢?

我们可以写多个,比如:

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

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

image.png

输出结果:

image.png

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

只会测试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()也是非静态的,非静态里面可以调非静态的,如下:

image.png

输出:

image.png


若是以前,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类,可以看到报错了:

image.png

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

怎么解决呢?

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

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

image.png

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

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

还是回到“项目结构”:

image.png

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

具体操作如下:

image.png

然后添加:

image.png

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

image.png

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

image.png

然后OK即可。


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

image.png

单元测试方法,就是写代码不需要再用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");
    }
}

在这里插入图片描述

(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");
    }

}

运行会报错:

image.png

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

image.png


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

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

比如:

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

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

image.png

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

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

这些要求不必要都记住,只需要造完一个类,然后在里面写单元测试方法的时候,直接用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()的时候,可以发现没有办法从键盘输入数据进去

image.png

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


☕如何解决?

1. 设置数据

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

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

-Deditable.java.test.console=true

2. 配置文件位置

image.png

image.png

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

image.png

重启:

image.png

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`件。


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

image.png

按下回车之后:

image.png

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

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

步骤:

①“文件”–> “设置”

image.png

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

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

image.png

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

image.png

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

image.png

⑤写模板

image.png

比如模板是这样:

image.png

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

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

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

image.png


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

image.png

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

image.png

【总结】

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

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/225848.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自动化测试:PO模式详解!

PO&#xff08;Page Object&#xff09;模式是一种在自动化测试中常用的设计模式&#xff0c;将页面的每个元素封装成一个对象&#xff0c;通过操作对象来进行页面的交互。 概括来说就是&#xff0c;每个页面都有对应的PO类&#xff0c;PO类中包含了页面的元素定位和操作方法。…

OMSA无法打开无法显示等服务异常时如何处理

文章目录 为何需要重启OMSAWindows 重启OMSA服务Linux 重启OMSA服务VMware 环境重启OMSA服务重启无效的解决办法推荐阅读 为何需要重启OMSA 在安装 OMSA 的服务器中&#xff0c;OMSA 管理软件运行可能会不稳定。例如&#xff1a; 某些信息&#xff08;如存储信息&#xff09;…

使用DockerUI结合内网穿透工具轻松实现公网访问和管理docker容器

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

C - 语言->内存函数

目录 系列文章目录 前言 1. memcpy使⽤和模拟实现 1.2 memcpy函数的模拟实现: 2. memmove 使⽤和模拟实现 2.1memmove的模拟实现&#xff1a; 3. memset 函数的使⽤ 4. memcmp 函数的使⽤ 系列文章目录 ✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff…

我有才打造知识付费小程序

一站式线上线下活动管理 为用户提供“精彩城市生活和人脉资源”。 在线活动提供创业、互联网、科技、投资、金融、教育、亲子、生活、聚会交友、医疗、设计、分享会、脱口秀、音乐演出等多种活动类型, 为职场白领提升技能、拓展人脉、聚会交友的首选平台。 为主办方提供“一…

迅镭激光受邀参加中国船舶与海洋工程产业知识产联盟年会

近日&#xff0c;由中国船舶工业行业协会知识产权分会、中国船舶与海洋工程产业知识产权联盟主办的“知识产权高质量发展高端论坛暨中国船舶工业行业协会知识产权分会及中国船舶与海洋工程产业知识产权联盟年会”在南通举行。 本次会议荟聚中国船舶行业专家、高校、科研院所及船…

基于ssm学生请假系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生请假系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&am…

Java网络编程——创建非阻塞的HTTP服务器

HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超级文本传输协议&#xff09;是网络应用层的协议&#xff0c;建立在TCP/IP基础上。HTTP使用可靠的TCP连接&#xff0c;默认端口是80端口。HTTP的第1个版本是HTTP/0.9&#xff0c;后来发展到了HTTP/1&#xff0c;现在最…

拼多多选品大作战:利用类目榜单找到潜力爆品

想要在激烈的电商竞争中脱颖而出&#xff0c;选品是至关重要的一环。 而拼多多提供的类目榜单数据&#xff0c;为商家们提供了一个寻找热门产品和趋势的利器。本文将详细介绍如何利用拼多多类目榜单进行选品&#xff0c;并帮助您找到畅销产品。 拼多多新手选品核心两要素&…

ARCGIS 中使用 ChatGPT 的 5 种方式

ChatGPT 一度成为最热门的话题。什么是 ChatGPT&#xff1f;谁能比 ChatGPT 本身更好地回答这个问题呢&#xff1f;我们要求它写一个关于 ChatGPT 是什么的简短描述&#xff0c;这是它的回应&#xff1a; ChatGPT 是一个聊天机器人&#xff0c;使用 OpenAI 开发的 GPT-3 语言模…

【AI】VIT Transformer论文学习笔记

论文&#xff1a;Dosovitskiy A, Beyer L, Kolesnikov A, et al. An image is worth 16x16 words: Transformers for image recognition at scale[J]. arXiv preprint arXiv:2010.11929, 2020 1.文章背景 计算机视觉当前最热门的两大基础模型就是Transformer和CNN了。 Transf…

1146-table performance-schema.session_variables don‘t exits打卡navicat连接MySQL报错

navicat连接MySQL时报错&#xff1a; 管理员权限打开cmd 输入下面代码&#xff1a; mysql_upgrade -u root -p --force输入密码 然后就可以正常连接了。 mysql_upgrade检查所有数据库中与mysql服务器当前版本不兼容的所有表。 mysql_upgrade也会升级系统表&#xff0c;以便你…

mysql的组合查询

mysql的组合查询 1、mysql的内连接查询 在 MySQL 中&#xff0c;内连接&#xff08;INNER JOIN&#xff09;是一种根据两个或多个表之间的匹配条件&#xff0c;将多个表中的数据进行联接的操作。内连接只返回符合联接条件的行&#xff0c;而不会返回未匹配的行。 内连接的语…

故宫博物院与周大福珠宝集团 战略合作签约仪式在京举行

12月5日上午&#xff0c;故宫博物院与周大福珠宝集团战略合作签约仪式在故宫博物院故宫文化资产数字化应用研究所举行。文化和旅游部党组成员、故宫博物院院长王旭东&#xff0c;国际儒学联合会常务副会长、原文化部副部长丁伟&#xff0c;国际儒学联合会特别顾问、中国国际友好…

【项目】学生信息管理系统

概述 本系统总耗时 6 6 6 天&#xff0c;系统包括 学生发展与数据驱动平台6.2.cpp、学生信息.txt、用户账号.txt、注意事项.txt。由于代码对文件的调用使用的是相对路径&#xff0c;所以要求这 4 4 4 个文件都需要在同一目录。使用代码前先仔细看 注意事项。 如图&#xff1…

数据分析基础之《matplotlib(4)—柱状图》

一、柱状图绘制 1、柱状图要素 有类别 2、需求&#xff1a;对比每部电影的票房收入 电影数据如下图所示&#xff1a; 3、matplotlib.pyplot.bar(x, height, width0.8, bottomNone, *, aligncenter, dataNone, **kwargs) 说明&#xff1a; x&#xff1a;有几个类别 height&am…

ROS小练习——参数设置

目录 一、参数名获取 二、参数修改 1、代码修改 C python 2、命令行修改 3、启动时修改 4、launch文件传参修改 一、参数名获取 rosparam list 二、参数修改 1、代码修改 C #include "ros/ros.h"int main(int argc, char *argv[]) {ros::init(argc,argv,…

接口自动化测试之Yaml数据驱动封装!

一、数据驱动&#xff1a;pytest.mark.parametrize(&#xff09; 首先看个样本&#xff1a; import pytestclass TestData:# parametrize有两个值&#xff0c;一个是args_name:参数名&#xff0c;一个是args_value:参数值,可以有多个&#xff0c;进行数据解包# args_value可以…

智慧农业技术解决方案:PPT全文32页,附下载

关键词&#xff1a;智慧农业解决方案&#xff0c;数字农村&#xff0c;数字农业&#xff0c;智慧农业大数据平台&#xff0c;智慧农业技术方案 一、智慧农业技术平台建设方案 1、全球化和信息化趋势 随着全球化和信息化的加速发展&#xff0c;农业领域也面临着前所未有的挑战…

5分钟搞懂ECN

ECN是通过在IP和TCP头中携带拥塞信息&#xff0c;通知发送方网络拥塞状态&#xff0c;从而采取相应拥塞控制措施。原文: What is ECN(Explicit Congestion Notification)?[1] ECN是Explicit Congestion Notification的缩写&#xff0c;意思是显式拥塞通知算法&#xff0c;和慢…