• java基础进阶——log日志、类加载器、XML、单元测试、注解、枚举类


    前言

    这篇内容主要掌握的就是logback使用、理解类加载器、XML文件的编写,XML文档约束schema,用Dom4j解析XML文档,Xpath检索XML文档,完整使用Junit单元测试框架常用部分,注解的定义和使用,枚举类的定义和开发应用。

    一、log日志

    log日志类似输出语句,可以用来输出程序的信息,但是log日志更加好用。

    1.输出语句的弊端

    2. log日志的优点

    可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中)。

    可以随时以开关的形式控制是否记录日志,无需修改源代码。

    日志技术的具体优势

    3.日志框架体系

    日志框架需要日志规范的要求实现,日志规范大多是一些接口,提供给实现框架去设计的。常见的规范有:Commons Logging、SimpleLoggingFacadeforJava。

    志的实现框架有,Log4J、Logback(我们重点学习的,其他的都大同小异)

    4.日志框架LogBack 

    (1)概述

    Logback是基于slf4j的日志规范实现的框架,性能比之前使用的log4j要好。

    官方网站:Logback Home

    Logback主要分为三个技术模块:

     logback-core:该模块为其他两个模块提供基础代码,必须有。

     logback-classic:完整实现了slf4j APl的模块。

     logback-access模块与Tomcat和Jetty等Servlet容器集成,以提供HTTP访问日志功能

     (2)配置

    1.创建一个maven项目,在pom文件将下面的依赖坐标导入项目

    1. <dependency>
    2. <groupId>ch.qos.logbackgroupId>
    3. <artifactId>logback-classicartifactId>
    4. <version>1.2.3version>
    5. dependency>

     刷新maven自动导入下面三个jar包

    2. 将Logback的核心配置文件logback.xml直接拷贝到maven项目的resources目录下

    logback.xml内容如下:

    1. "1.0" encoding="UTF-8"?>
    2. <configuration>
    3. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    4. <target>System.outtarget>
    5. <encoder>
    6. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%npattern>
    7. encoder>
    8. appender>
    9. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    10. <encoder>
    11. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%npattern>
    12. <charset>utf-8charset>
    13. encoder>
    14. <file>C:/code/itpzh-data.logfile>
    15. <rollingPolicy
    16. class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    17. <fileNamePattern>C:/code/itpzh-data2-%d{yyyy-MMdd}.log%i.gzfileNamePattern>
    18. <maxFileSize>1MBmaxFileSize>
    19. rollingPolicy>
    20. appender>
    21. <root level="info">
    22. <appender-ref ref="CONSOLE"/>
    23. <appender-ref ref="FILE" />
    24. root>
    25. configuration>

    (3)入门程序

    A.步骤

    1.获取日志的对象。

    private final static Logger LOGGER = LoggerFactory.getLogger("类对象");

    注意:这个对象要全局共享唯一(static final修饰)

               这个Logger一定要是org.slf4j.Logger这个第三方包下的

    2.用日志对象的方法记录系统的日志信息。

     B.入门实例代码
    1. import org.slf4j.Logger;
    2. import org.slf4j.LoggerFactory;
    3. import java.util.Scanner;
    4. public class LoggerDemo1 {
    5. //获取一个共享的唯一的log日志对象
    6. private final static Logger LOGGER = LoggerFactory.getLogger(LoggerDemo1.class);
    7. public static void main(String[] args) {
    8. //获取用户登录的日志
    9. Scanner sr = new Scanner(System.in);
    10. while (true) {
    11. System.out.println("请输入用户名");
    12. String username = sr.next();
    13. System.out.println("请输入密码");
    14. String password = sr.next();
    15. if ("张三".equals(username) && "12345".equals(password)) {
    16. LOGGER.info("用户{}登录成功,密码为{}", username, password);
    17. break;
    18. } else {
    19. LOGGER.info("用户{}登录失败", username);
    20. }
    21. }
    22. }
    23. }

    运行完,LOGGER.info的内容不仅显示在控制台,而且还被保存到文件里面

    因为配置文件appender标签内容有配置,后面会说 

    (4)配置文件logback.xml解说

    Logback日志系统的特性都是通过核心配置文件logback.xml控制的。

    Loaback日志输出位置、格式设置:

    1.通过logback.xml中的标签可以设置输出位置和日志信息的详细格式。

    2.通常可以设置2个日志输出位置:一个是控制台、一个是系统文件中

     下面的标签内的各种标签解说

    标签的属性name=CONSOLE时表示输出到控制台

    class表示输出到控制台的功能由ch.qos.logback.core.ConsoleAppender这个类来实现

    标签表示System.out以标准输出流的形式输出到控制台上面

    标签规定输出内容的格式,%d{yyyy-MM-dd HH:mm:ss.SSS}表示按格式输出年月日 时分秒        [%-5level]表留五个空格的长度输出日志的等级        %c表示当前操作的类

    %thread表示当前所在的线程,比如main线程     %msg表示输出的信息

     下面讲解标签内的属性和附属标签

    标签控制哪几个标签能否生效的,里面用标签标记要生效的标签。

    标签的属性level用来设置打印级别,一共7个,All表示标签所包含的标签全部生效,OFF表示全部不生效。

    (5)日志级别

    作用:

    如果系统上线后只想记录一些错误的日志信息或者不想记录日志了,怎么办?

    可以通过设置日志的输出级别来控制哪些日志信息输出或者不输出。

    解说:日志级别是可以通过日志对象的.level方法去设置的,不设置的话默认是继承日志的顶级父类 root的级别debug。

    标签的level属性设置为WARN时,日志对象如果日志级别为TRACE,DEBUG,INFO就不输出日志。

    二、类加载器

    1.作用

    负责将.class文件(存储的物理文件)加载在到内存中

    2.类加载的完整过程

     (1)类加载时机

    (简单理解:字节码文件什么时候会被加载到内存中?)

    有以下的几种情况:

    • 创建类的实例(对象)

    • 调用类的类方法

    • 访问类或者接口的类变量,或者为该类变量赋值

    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

    • 初始化某个类的子类

    • 直接使用java.exe命令来运行某个主类

    总结而言:用到了就加载,不用不加载

    (2)类加载过程  

    类加载分为5个阶段

    加载,验证,准备,解析,初始化。

    其中验证,准备,解析归为链接阶段

     加载

    就是,将字节码文件通过IO流读取到JVM的方法区,并同时在堆中生成Class对象。

    • 通过包名 + 类名,获取这个类,准备用流进行传输

    • 在这个类加载到内存中

    • 加载完毕创建一个class对象

    注意:创建的class对象的变量类型如果是引用类型,就用符号代替

    链接 
    A.验证

    确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全

    (文件中的信息是否符合虚拟机规范有没有安全隐患)

    B.准备

    负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值

    (初始化静态变量)

    C.解析

    将类的二进制数据流中的符号引用替换为直接引用

    (本类中如果用到了其他类,此时就需要找到对应的类)

    这里的符号引用,意思是,在类加载的时候将一个类的字节码文件以二进制数据流输入JVM,该类的成员变量的类型如果是像String类型这种引用类型,但是在加载的时候,String从哪里来那里去都不知道,于是先用符号代替String,这就是符号引用

    初始化

    根据程序员通过程序制定的主观计划去初始化类变量和其他资源

    (静态变量赋值以及初始化其他资源)

     3.类加载器分类

    (1)分类

    • Bootstrap class loader:虚拟机的内置类加载器,通常表示为null ,并且没有父级类加载器

    • Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块

    • System class loader:系统类加载器,负责加载用户类路径上所指定的类库

    • 自定义类加载器

    (2)类加载器的继承关系(逻辑上的继承,看后面的双亲委派模型)

    自定义加载器的父加载器为System

    System的父加载器为Platform

    Platform的父加载器为Bootstrap

    (3)代码演示证明

    1. public class ClassLoaderDemo1 {
    2. public static void main(String[] args) {
    3. //获取系统类加载器
    4. ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    5. //获取系统类加载器的父加载器 --- 平台类加载器
    6. ClassLoader classLoader1 = systemClassLoader.getParent();
    7. //获取平台类加载器的父加载器 --- 启动类加载器
    8. ClassLoader classLoader2 = classLoader1.getParent();
    9. System.out.println("系统类加载器" + systemClassLoader);
    10. System.out.println("平台类加载器" + classLoader1);
    11. System.out.println("启动类加载器" + classLoader2);
    12. }
    13. }

     运行结果

    4. 双亲委派模型

    如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

    5.ClassLoader 中的两个方法

    方法名说明
    public static ClassLoader getSystemClassLoader()获取系统类加载器
    public InputStream getResourceAsStream(String name)加载某一个资源文件

    实例代码

    1. public class ClassLoaderDemo2 {
    2. public static void main(String[] args) throws IOException {
    3. //获取系统类加载器
    4. ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    5. //利用加载器去加载一个指定的文件
    6. //参数:文件的路径(放在maven项目的resources目录下,默认去那里加载)
    7. //返回值:字节流。
    8. InputStream is = systemClassLoader.getResourceAsStream("prop.properties");
    9. Properties properties = new Properties();
    10. properties.load(is);
    11. System.out.println(properties);
    12. is.close();
    13. }
    14. }

    三、XML

    1.配置文件

    XML文件一般作为配置文件使用,那么什么是配置文件?

    配置文件就是,用来保存程序在运行时需要的一些参数。比如说idea的配置文件,当我们第一次使用idea的时候,我们配置idea的主题,字体大小等等,这些参数会被保存在idea的配置文件里面,当我们再次打开idea的时候,idea会从配置文件中加载这些参数,就不用我们重新配置idea主题什么的了。

    常见的配置文件有什么?

    有.txt文件,有.properties文件,有.xml文件

    三种配置文件的优缺点?

    txt文件只记录参数的值,太初略

    properties文件以键值对的形式记录参数,较为详细

    但是对于复杂的多用户的配置文件,就不适应,选择使用xml文件

    TXT文件:没有优点,缺点是不利于阅读

    properties文件:优点就是键值对形式易于阅读,缺点就是无法配置一组一组的数据。

    XML文件:优点是易于阅读,可以配置成组出现的数据

    以后配置文件怎么选择?

    数据量较少,一个键只对应一个值,使用properties

    数据量较多,使用xml

    2.XML概述

    XML的全称为(EXtensible Markup Language),是一种可扩展的标记语言标记语言: 通过标签来描述数据的一门语言(标签有时我们也将其称之为元素)可扩展:标签的名字是可以自定义的,XML文件是由很多标签组成的,而标签名是可以自定义的

    • 作用

      • 用于进行存储数据和传输数据

      • 作为软件的配置文件

    • 作为配置文件的优势

      • 可读性好

      • 可维护性高

    3.XML的基本语法

    (1)XML文件的命名和位置

    XML文件要放在src目录下,或者放在maven项目下面的resources目录下面

    (2)标签(元素)规则 

    (3)XML其他组成 

    (4)示例

    4.XML文档约束

    (1)什么是文档约束?

     于是需要文档约束来用来限定xml文件中的标签以及属性应该怎么写。以此强制约束程序员必须按照文档约束的规定来编写xml文件。

    (2)约束分类

    DTD

    schema

    (3)DTD约束

    DTD约束编写

    A.步骤

    1. 创建一个文件,这个文件的后缀名为.dtd

    2. 看xml文件中使用了哪些元素

      ELEMENT> 可以定义元素

    3. 判断元素是简单元素还是复杂元素

      简单元素:没有子元素。复杂元素:有子元素的元素;

    4. 简单元素修饰词

      ​ EMPTY: 表示标签体为空

      ​ ANY: 表示标签体可以为空也可以不为空

      ​ PCDATA: 表示该元素的内容部分为字符串

    5. 复杂元素修饰词​                                                                                                                   直接写子元素名称. 多个子元素可以使用","或者"|"隔开;​ ","表示定义子元素的顺序 ; "|": 表示子元素只能出现任意一个​ "?"零次或一次, "+"一次或多次, "*"零次或多次;如果不写则表示出现一次

    6. 元素的属性的定义

      格式

      定义一个属性的格式为:属性的类型:​ CDATA类型:普通的字符串

      属性的约束:

      ​ // #REQUIRED: 必须的​ // #IMPLIED: 属性不是必需的​ // #FIXED value:属性值是固定的

    B.代码

    简单入门

    1. persons (person)>
    2. person (name,age)>
    3. name (#PCDATA)>
    4. age (#PCDATA)>

     复杂

    1. persons (person+)>
    2. person (name,age)>
    3. name (#PCDATA)>
    4. age (#PCDATA)>
    5. person id CDATA #REQUIRED>

    DTD约束引入

    三种引入方式

    简单入门代码
    1. // 这是persondtd.dtd文件中的内容,已经提前写好
    2. persons (person)>
    3. person (name,age)>
    4. name (#PCDATA)>
    5. age (#PCDATA)>
    6. // 在person1.xml文件中引入persondtd.dtd约束
    7. "1.0" encoding="UTF-8" ?>
    8. persons SYSTEM 'persondtd.dtd'>
    9. <persons>
    10. <person>
    11. <name>张三name>
    12. <age>23age>
    13. person>
    14. persons>
    复杂代码 
    1. //dtd代码
    2. persons (person+)>
    3. person (name,age)>
    4. name (#PCDATA)>
    5. age (#PCDATA)>
    6. person id CDATA #REQUIRED>
    7. //xml代码
    8. "1.0" encoding="UTF-8" ?>
    9. persons SYSTEM 'persondtd.dtd'>
    10. <persons>
    11. <person id="001">
    12. <name>张三name>
    13. <age>23age>
    14. person>
    15. <person id = "002">
    16. <name>张三name>
    17. <age>23age>
    18. person>
    19. persons>

     (4)schema约束

    schema和dtd的区别
    1. schema约束文件也是一个xml文件,符合xml的语法,这个文件的后缀名.xsd

    2. 一个xml中可以引用多个schema约束文件,多个schema使用名称空间区分(名称空间类似于java包名)

    3. dtd里面元素类型的取值比较单一常见的是PCDATA类型,但是在schema里面可以支持很多个数据类型

    4. schema 语法更加的复杂

    schema约束编写

    A.步骤 

    1,创建一个文件,这个文件的后缀名为.xsd。

    2,定义文档声明(跟xml文件第一行一样)

    3,schema文件的根标签为:

    4,在中定义属性:​ xmlns=http://www.w3.org/2001/XMLSchema

    5,在中定义属性 :​ targetNamespace =唯一的url地址,指定当前这个schema文件的名称空间。

    6,在中定义属性 :​ elementFormDefault="qualified“,表示当前schema文件是一个质量良好的文件。直接复制用就行

    7,通过element定义元素

    8,判断当前元素是简单元素还是复杂元素

    代码

    1. "1.0" encoding="UTF-8" ?>
    2. <schema xmlns="http://www.w3.org/2001/XMLSchema"
    3. targetNamespace="http://www.example.org/b"
    4. elementFormDefault="qualified">
    5. <element name="persons">
    6. <complexType>
    7. <sequence maxOccurs="unbounded">
    8. <element name="person">
    9. <complexType>
    10. <sequence>
    11. <element name="name" type="string"/>
    12. <element name="age" type="int"/>
    13. sequence>
    14. complexType>
    15. element>
    16. sequence>
    17. complexType>
    18. element>
    19. schema>
    schema约束引入

    直接在xml文件的第二行,先输入<,idea会弹出提示框,选择目标的xsd

    选择后idea直接导入约束

     入门代码
    1. //xsd代码
    2. "1.0" encoding="UTF-8" ?>
    3. <schema xmlns="http://www.w3.org/2001/XMLSchema"
    4. targetNamespace="http://www.example.org/b"
    5. elementFormDefault="qualified">
    6. <element name="persons">
    7. <complexType>
    8. <sequence maxOccurs="unbounded">
    9. <element name="person">
    10. <complexType>
    11. <sequence>
    12. <element name="name" type="string"/>
    13. <element name="age" type="int"/>
    14. sequence>
    15. complexType>
    16. element>
    17. sequence>
    18. complexType>
    19. element>
    20. schema>
    21. //xml代码
    22. "1.0" encoding="UTF-8" ?>
    23. <persons xmlns="http://www.example.org/b">
    24. <person>
    25. <name>张三name>
    26. <age>18age>
    27. person>
    28. <person>
    29. <name>张三name>
    30. <age>18age>
    31. person>
    32. <person>
    33. <name>张三name>
    34. <age>18age>
    35. person>
    36. persons>

     5.XML文件解析

    XML的数据的作用是什么,最终需要怎么处理?

    存储数据、做配置信息、进行数据传输, 

    最终需要被程序进行读取,解析里面的信息。

    有几种解析方式?

    SAX(一般不用)和DOM

    SAX和DOM的优缺点

    SAX:不会把整体的xml文件都加载到内存,而是从上往下逐行进行扫描。
    缺点:只能读取,不能添加,不能删除。
    优点:因为他是逐行扫描不需要把整体的xml文件都加载到内存,所以他可以解析比较大的xml文件。

    DOM:会把整体的xml文件都加载到内存。
    会把这个整天在内存中形成一个树形结构,我们可以通过这个树形结构去解析xml文件。
    优点:可以读取,可以添加,可以删除,可以做任何事情。
    缺点:需要xml文件全部加载到内存,所以不能解析非常大的xml文件。

    (1)Dom解析的文档对象模型(一层一层解析的过程)

     (2)Dom常见的解析工具

     6.Dom4j解析XML

    (1)快速入门

    1. public class Dom4jDemo1 {
    2. public static void main(String[] args) throws DocumentException {
    3. //1.创建解析器对象
    4. SAXReader saxReader = new SAXReader();
    5. //2.利用解析器去读取XML文件,并返回文件对象
    6. File file = new File("src\\main\\resources\\a.xml");
    7. Document document = saxReader.read(file);
    8. //拿到了document表示我已经拿到了xml文件的整体
    9. //3.打印
    10. System.out.println(document);
    11. //下面一层一层的扒开获取里面的内容即可
    12. }
    13. }

     (2)成员方法

     Element类

    (3)最终示例

    需求:

            将下面的XML文件的几个人的信息保存到内存中

    建议:创建一个Person类来存储人物信息

    1. "1.0" encoding="UTF-8" ?>
    2. <persons>
    3. <person id="1">
    4. <name>张三name>
    5. <age>18age>
    6. person>
    7. <person id="2">
    8. <name>李四name>
    9. <age>20age>
    10. person>
    11. <person id="3">
    12. <name>张三name>
    13. <age>21age>
    14. person>
    15. persons>

    创建一个Person类

    1. public class Person {
    2. private int id;
    3. private String name;
    4. private int age;
    5. public Person(int id, String name, int age) {
    6. this.id = id;
    7. this.name = name;
    8. this.age = age;
    9. }
    10. @Override
    11. public String toString() {
    12. return "Person{" +
    13. "id=" + id +
    14. ", name='" + name + '\'' +
    15. ", age=" + age +
    16. '}';
    17. }
    18. }

    正文代码

    1. public class Dom4jDemo2 {
    2. public static void main(String[] args) throws DocumentException {
    3. //1.创建解析器对象
    4. SAXReader saxReader = new SAXReader();
    5. //2.利用解析器去读取XML文件,并返回文件对象
    6. File file = new File("src\\main\\resources\\a.xml");
    7. Document document = saxReader.read(file);
    8. //拿到了document表示我已经拿到了xml文件的整体
    9. //创建一个集合来存储人物的信息
    10. ArrayList list = new ArrayList<>();
    11. //下面一层一层的扒开获取里面的内容即可
    12. //获取根元素
    13. Element rootElement = document.getRootElement();
    14. //获取根元素下的所有person元素
    15. List elements = rootElement.elements("person");
    16. for (Element element : elements) {
    17. //获取子元素的id属性
    18. Attribute id = element.attribute("id");
    19. String idValue = id.getValue();
    20. //获取子元素的name和age
    21. String nameValue = element.element("name").getText();
    22. String ageValue = element.element("age").getText();
    23. Person person = new Person(Integer.parseInt(idValue), nameValue, Integer.parseInt(ageValue));
    24. list.add(person);
    25. }
    26. list.stream().forEach(System.out::println);
    27. }
    28. }

    7.XML检索技术Xpath

    如果需要从XML文件中检索需要的某个信息(如name)怎么解决?

    Dom4j需要进行文件的全部解析,然后再寻找数据。

    Xpath技术更加适合做信息检索。

     XPath在解析XML文档方面提供了一独树一帜的路径思想,更加优雅,高效。

    XPath使用路径表达式来定位XML文档中的元素节点或属性节点

    /元素/子元素/孙元素

    //子元素//孙元素

     (1)使用步骤

    1.导入jar包(dom4j和jaxen-1.1.2.jar),Xpath技术依赖Dom4j技术

    2.通过dom4j的SAXReader解析器获取Document对象

    3.利用XPath提供的API,结合XPath的语法完成选取XML文档元素节点进行解析操作。

    4.Document中与Xpath相关的API如下:

    (2)路径表达式

    绝对路径

    相对路径
     全文搜索

    属性查找

    (3)快速入门

    需求:

            利用Xpath检索技术解析XML中的name元素,XML文件内容如下

    1. "1.0" encoding="UTF-8" ?>
    2. <persons>
    3. <person id="1">
    4. <name>张三name>
    5. <age>18age>
    6. person>
    7. <person id="2">
    8. <name>李四name>
    9. <age>20age>
    10. person>
    11. <person id="3">
    12. <name>王五name>
    13. <age>21age>
    14. person>
    15. <name>基尼太妹name>
    16. persons>

    代码

    1. public class XpathDemo {
    2. public static void main(String[] args) throws DocumentException {
    3. //1.获取解析器
    4. SAXReader saxReader = new SAXReader();
    5. //2.用解析器解析XML文件获得对于的Document对象
    6. File file = new File("src/main/resources/a.xml");
    7. Document document = saxReader.read(file);
    8. //3.通过Xpath的API获取name元素
    9. //(1)绝对路径
    10. List list1 = document.selectNodes("/persons/person/name");
    11. System.out.println("绝对路径获取name内容:一共"+list1.size()+"个");
    12. for (Element element : list1) {
    13. System.out.println(element.getText());
    14. }
    15. //(2)相对路径
    16. Element rootElement = document.getRootElement();
    17. List list2 = rootElement.selectNodes("./person/name");
    18. System.out.println("相对路径获取name内容:一共"+list2.size()+"个");
    19. for (Element element : list2) {
    20. System.out.println(element.getText());
    21. }
    22. //(3)全文搜索
    23. List list3 = document.selectNodes("//name");
    24. System.out.println("全文搜索获取name内容:一共"+list3.size()+"个");
    25. for (Element element : list3) {
    26. System.out.println(element.getText());
    27. }
    28. //(4)属性搜索
    29. Element node = (Element) document.selectSingleNode("//person[@id='1']/name");
    30. System.out.println("属性搜索获取name内容:"+node.getText());
    31. }
    32. }

    四、单元测试

    1.什么是单元测试?为什么用它?

    单元测试就是,针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。

    以前测试方法只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响。还需要程序员自己去观察测试是否成功。

    2.Junit单元测试框架

    (1)简述

    JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。

    此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。

    (2)优点

    1.JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。

    2.单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。

    3.运行成功是绿色,运行失败是红色

    (3)步骤

    (4)快速入门

    测试代码
    1. public class JunitDemo1 {
    2. @Test
    3. //输入注解@Test
    4. //如果爆红就按alt+回车
    5. //选择Junit4导入
    6. public void method1() {
    7. System.out.println(2/0);
    8. int a = 100;
    9. int b = 100;
    10. System.out.println(a+b);
    11. }
    12. @Test
    13. public void method2() {
    14. int a = 100;
    15. int b = 100;
    16. System.out.println(a+b);
    17. }
    18. @Test
    19. public void method3() {
    20. int a = 100;
    21. int b = 100;
    22. System.out.println(a+b);
    23. }
    24. }
    测试结果

     (5)Junit常用注解

    一般实际开发常用的就前面的3个注解,@Before,@Test,@After这三个注释配套使用

    实际开发的测试原则就是,保证测试前后的数据,原原本本不变

    于是完整的单元测试步骤

    1.先执行的@Before的方法,对数据进行一个初始化的动作和数据的备份

    2.再执行@Test的方法,去真正的去测试方法,要配合断言,与期望数据做对比

    3.最后执行@After的方法,去还原数据

     Junit中一般的断言使用方法

    参数1:如果断言为假,就将message的信息输出到控制台

                            为真,就不输出

    参数2:期望得到的结果

    参数3:运行完测试方法得到的实际结果

    Assert.assertEquals(message,expected, actual);

    (6)完整的单元测试

    以后在实际开发中,如果想要测试一个方法是否正确,并不是直接在当前方法的上面写@Test的

    而是,自己独立编写一个测试类。(不要写main方法)

    在这个类中,编写一些方法。

    在方法里面调用要被测试的方法即可

    需求:

            测试File类中的delete方法是否书写正确???

    代码

    独立写一个测试类,创建要测试的方法所在类的对象File,运行FIle对象的delete方法,

    测试方法必须数据备份和恢复

    1. public class JunitDemo2 {
    2. @Before
    3. public void method1() throws IOException {
    4. //数据备份
    5. FileInputStream fis = new FileInputStream("logs\\a.txt");
    6. FileOutputStream fos = new FileOutputStream("logs\\copy.txt");
    7. int b;
    8. while ((b=fis.read())!=-1){
    9. fos.write(b);
    10. }
    11. fos.close();
    12. fis.close();
    13. }
    14. @Test
    15. public void method2(){
    16. //测试File类的delete方法
    17. File file = new File("logs\\a.txt");
    18. //判断是否删除成功
    19. boolean delete = file.delete();
    20. //检查a.txt是否存在
    21. boolean exists = file.exists();
    22. Assert.assertEquals("delete方法出错了",true,delete);
    23. Assert.assertEquals("delete方法出错了",false,exists);
    24. }
    25. @After
    26. public void method3() throws IOException {
    27. //数据恢复
    28. FileInputStream fis = new FileInputStream("logs\\copy.txt");
    29. FileOutputStream fos = new FileOutputStream("logs\\a.txt");
    30. int b;
    31. while ((b=fis.read())!=-1){
    32. fos.write(b);
    33. }
    34. fos.close();
    35. fis.close();
    36. new File("logs\\copy.txt").delete();
    37. }
    38. }

     五、注解

    1.简述

    Annotation表示注解。是JDK1.5的新特性。

    注解的主要作用:对我们的程序进行标注。通过注解可以给类增加额外的信息。

    注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。

    (注释是给人看的,编译阶段会注释擦除)

    2.常见的注解(掌握) 

    @Override:表示方法的重写
    @Deprecated:表示修饰的方法已过时
    @SuppressWarnings("all"): 压制警告

    除此之外,还需要掌握第三方框架中提供的注解:

    比如:Junit

    @Test表示运行测试方法

    @Before表示在Test之前运行,进行数据的初始化

    @After表示在Test之后运行,进行数据的还原

     3.自定义注解

    (1)定义格式

    类似类的定义

    (2)使用格式

    @注解名(属性名1=值1,属性名2=值2)

    注意:

            注解使用可以放在类,方法和属性的上面

            使用自定义注解时要保证注解每个属性都有值

            注解可以使用默认值 ,没有默认值的一定要赋值

     创建一个注解

    1. public @interface MyAnno1 {
    2. String name();
    3. int age() default 18;
    4. }

    使用 

    1. @MyAnno1(name = "张三")
    2. public class Test {
    3. @MyAnno1(name = "王五",age = 20)
    4. int age;
    5. @MyAnno1(name = "李四")
    6. public void method1(){
    7. System.out.println("method1");
    8. }
    9. }

     (3)特殊属性value

    value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!

    但是如果有多个属性,且多个属性没有默认值,那么vaue名称是不能省略的。

    常见的@SuppressWarnings("all"):就是只有一个value属性的注解

    4.元注解

    元注解就是注解的注解,就是写在注解上面的注解

    比如下面

     

    (1)两个元注解

     @Target:约束自定义注解只能在哪些地方使用

     @Retention:申明注解的生命周期

    @Target

     @Target中可使用的值定义在ElementType枚举类(底层写好的)中,常用值如下

    TYPE,类,接口

    FIELD,成员变量

    METHOD,成员方法

    PARAMETER,方法参数

    CONSTRUCTOR,构造器

    LOCAL_VARIABLE,局部变量

     @Retention

    @Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下

    SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在

    CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值

    RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

    5.注解解析

    (1)概念和作用

     注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。

    举一个例子Junit框架中的@Test注解是怎么将测试方法运行起来的,这其中就是用到了注解解析。

     当运行测试方法的时候,@Test注解就会通过反射来获取对应方法的Method类,然后执行方法。

     (2)与注解解析相关的接口和方法

    Annotation:注解的顶级接口

    可以利用反射解析注解

    有的类成分Class,Method,Field,Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力

    (3) 解析注解的技巧

    注解在哪个成分上,我们就先拿哪个成分对象。

    比如注解作用成员方法,则要获得该成员方法对应的Methed对象,再来拿上面的注解

    比如注解作用在类上,则要该类的class对象,再来拿上面的注解

    比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解

    配合后面的 模拟Junit框架理解

     6.模拟Junit框架

    需求

            定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行

    分析

            定义一个自定义注解MvTest,只能注解方法,存活范围是一直都在。

            定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行。

    代码 
    1. @Target(ElementType.METHOD)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface MyTest {
    4. }
    5. public class MyTestMethod {
    6. @MyTest
    7. public void method1(){
    8. System.out.println("method1");
    9. }
    10. public void method2(){
    11. System.out.println("method2");
    12. }
    13. @MyTest
    14. public void method3(){
    15. System.out.println("method3");
    16. }
    17. }
    18. public class MyTestDemo {
    19. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
    20. //1,获取class对象
    21. Class clazz = Class.forName("com.itheima.test2.MyTestMethod");
    22. //获取对象
    23. Object o = clazz.newInstance();
    24. //2.获取所有方法
    25. Method[] methods = clazz.getDeclaredMethods();
    26. for (Method method : methods) {
    27. //method依次表示类里面的每一个方法
    28. method.setAccessible(true);
    29. //判断当前方法有没有MyTest注解
    30. if(method.isAnnotationPresent(MyTest.class)){
    31. method.invoke(o);
    32. }
    33. }
    34. }
    35. }
    运行结果 

    method1

    method3

    六、枚举类 

    1.概念,使用和优缺点

    可以查看【Java】枚举类型_java 枚举类-CSDN博客

    2.主要的应用开发场景

    状态管理

    使用枚举类表示对象的不同状态,例如订单状态(待支付、已支付、已发货、已完成等)。

    错误代码

    定义各种错误类型或错误代码,以提高代码的可读性和可维护性。

    配置选项

    使用枚举类来封装常见的配置选项,例如日志级别(DEBUG、INFO、WARN、ERROR)或环境(开发、测试、生产)。

    类型安全的选择

    替代字符串常量,提供类型安全的选项,避免因拼写错误导致的问题,例如选择用户角色(ADMIN、USER、GUEST)。

    策略模式

    在实现策略模式时,可以使用枚举类来封装策略的实现,从而使代码更清晰。

    分类系统

    在需要分类的场景中,例如商品类别(电子产品、服饰、食品等),枚举类可以提供一种清晰的结构。

    API 设计

    在 API 中使用枚举类可以清晰地表示可选参数,使接口更加易于理解和使用。

    事件类型

    用于定义不同的事件类型,例如用户操作事件(点击、提交、查看等)。

    数据库映射

    将数据库中的状态值映射到应用程序中的枚举类,简化数据操作。

    网络协议

    定义网络协议中的操作类型或消息类型,以提高代码的可读性。

    枚举类通过提供一组固定的常量值,能够使代码更清晰,更易于维护和扩展。

  • 相关阅读:
    C++用锁实现线程安全的stack容器
    发现一款非常好用的学术GPT,可形成知识库,并分析论文,根据观点生成文字
    【洛谷题解】P2670 [NOIP2015 普及组] 扫雷游戏
    性能测试:工具篇:Jmeter实时可视化平台搭建
    AIGC专栏7——EasyPhoto 人像训练与生成原理详解
    【Liunx】配置IP地址与MAC地址绑定
    漫画 | 单元测试实在是太可怕了!
    Spring实战之JavaConfig方式多环境与profile配置
    Tomcat7+ 弱口令 && 后台getshell漏洞
    面试官:你确定Redis是单线程的进程吗?
  • 原文地址:https://blog.csdn.net/2202_75483664/article/details/141033517