java基础-枚举、注解、单元测试、日志

赖卓成2023年7月12日
大约 8 分钟

13-4-1枚举

17-枚举-什么是枚举

用来表示一些固定的值,用常量有以下问题:

  • 代码不够简洁。
  • 不同类型的数据是通过名字区分的。
  • 不安全,可能被使用的值被用来做了运算。

为了简洁表示一些固定的值,Java提供了枚举。

将变量的值一一列出,变量的值只限于列举出来的值的范围内

18-枚举-定义格式

格式:

public enum s{
    枚举项1,枚举项2,枚举项3;
}

19-枚举-枚举的特点

  • 所有枚举都是Enum的子类

    image-20230712202110270

    image-20230712202300911

  • 可以通过枚举名称.枚举项名称访问指定的枚举项

  • 每个枚举项是该枚举的一个对象

    public class EnumDemo {
        public static void main(String[] args) {
            Season spring = Season.SPRING;
            System.out.println(spring instanceof  Season);
            Class<? extends Season> springClass = spring.getClass();
            Constructor<?>[] constructors = springClass.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("constructor = " + constructor);
            }
        }
    }
    
  • 枚举是类,可以定义成员变量

  • 枚举类的第一行必须是枚举项,最后一个枚举项的分号可以省略,如果后面还有代码则不能省略

  • 枚举可以有构造器,必须是private、默认也是private。枚举项的用法比较特殊:枚举("")

    public enum Season {
    
        // 每一个枚举项,其实就是一个枚举的对象,枚举项后面什么都不写,默认调用空参构造,也可以指定调用有参构造
        SPRING(),SUMMER,AUTUMN,WINTER;
    
        private String name;
    
        /**
         * 空参构造
         */
        private Season(){
    
        }
    
        /**
         * 构造函数只能是私有的,默认也是私有的
         *
         * @param name 名字
         */
        private Season(String name) {
            this.name = name;
        }
    }
    
  • 枚举也可以有抽象方法,但枚举项必须重新该方法

    public enum Season {
    
        // 每一个枚举项,其实就是一个枚举的对象,枚举项后面什么都不写,默认调用空参构造,也可以指定调用有参构造
        SPRING(){
            // 如果枚举类中有抽象方法,那么枚举项中必须重写
            @Override
            public void show() {
                System.out.println(this.name);
            }
        },SUMMER{
            @Override
            public void show() {
                
            }
        },AUTUMN{
            @Override
            public void show() {
                
            }
        },WINTER{
            @Override
            public void show() {
                
            }
        };
    
        public String name;
    
        /**
         * 空参构造
         */
        private Season(){
    
        }
    
        /**
         * 构造函数只能是私有的,默认也是私有的
         *
         * @param name 名字
         */
        private Season(String name) {
            this.name = name;
        }
    
        public abstract void show();
    }
    

    20-枚举-枚举的方法

    image-20230712204254641

image-20230712204533304

13-4-2-注解

21-注解-注解的优势

在配置servlet时,需要配置很多:

image-20230712210500026

使用注解以后,就不需要写这么多了:

image-20230712210557751

22-注解-注解的概述

这就是一个注解:

image-20230712210630892

注解的主要作用:对我们的程序进行标注和解释。

Java自带的注解举例:

image-20230712211240857

23-注解-自定义注解

格式:

public @interface 注解名称{
    public 属性类型 属性名称() default 默认值;
}

注解类型可以是:

  • 基本数据类型
  • String
  • Class
  • 注解
  • 枚举
  • 以上类型的一位数组

例子:

public @interface Anno1 {

    // 定义一个基本数据类型
    public int a() default 1;

    // 定义一个String类型
    public String name();

    // 定义一个Class类型
    public Class clazz() default Anno2.class;

    // 定义一个注解类型
    public Anno2 anno() default @Anno2;

    // 定义一个枚举类型
    public Season season() default Season.SPRING;
    
    // 定义一个一位数组
    public int[] ints() default {1,2,3};
}

public @interface Anno2 {
}

/**
 * 在使用注解时,如果注解中的属性没有给定默认值,需要手动传参,给定值,不然会报错
 *
 * @author lzc
 * @date 2023/07/12
 */
@Anno1(name="lzc")
public class AnnoDemo {
    public static void main(String[] args) {

    }
}

21-注解-特殊属性value

在使用注解时,如果我们只需要给注解的value属性赋值,那么value可以省略。

如:其他属性都赋了默认值,那么在使用注解时,可以不指定注解属性名称,默认就是给value属性赋值

image-20230712212845627

image-20230712212942589

25-注解-自定义注解练习

需求:

自定义一个注解,用于指定类的方法上,如果某一个类的方法上使用了该注解,就执行该方法。

自定义注解:

/**
 * 自定义注解,加了该注解的方法会被执行
 * @Retention(RetentionPolicy.RUNTIME)表示该注解的生命周期
 * @author lzc
 * @date 2023/07/12
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface LzcTest {


}

类:

public class UserTest {

    public void show(){
        System.out.println("show  运行");
    }

    @LzcTest
    public void method01(){
        System.out.println("method01  运行");
    }

    @LzcTest
    public void method02(){
        System.out.println("method02  运行");
    }
}

启动类:

public class AnnoDemo02 {
    public static void main(String[] args) throws Exception {
        // 获取class
        Class<?> clazz = Class.forName("com.lzc.enums.annotation.annodemo02.UserTest");

        // 调用空参构造创建对象
        Object instance = clazz.newInstance();

        // 获取注解上的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();

        // 遍历方法
        for (Method declaredMethod : declaredMethods) {
            // 判断方法上是否有该注解
            if (declaredMethod.isAnnotationPresent(LzcTest.class)){
                declaredMethod.setAccessible(true);
                declaredMethod.invoke(instance);
            }
        }
    }
}

运行结果:

image-20230712214142402

26-注解-元注解

元注解:描述注解的注解。

image-20230712214300184

@Target可以写如下参数:

image-20230712214556767

@Retention如果不写,默认是源码阶段:

image-20230712214657206

@Inherited该注解可以被继承:子类会继承父类的该注解。

验证:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited()
public @interface Anno03 {
}

@Anno03
public class Person {
}

public class Student extends Person{
}

public class AnnoDemo03 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.lzc.enums.annotation.annodemo03.Student");
        System.out.println("clazz.isAnnotationPresent(Anno03.class) = " + clazz.isAnnotationPresent(Anno03.class));
    }
}

image-20230712215104254

13-5-1-单元测试

01-单元测试-概述

之前是把所有代码写完再进行测试,并不是很好,单元测试可以更早定位问题,也避免了由于代码多无法准确定位错误代码的问题。

Junit特点:

  • 开放源代码的测试工具
  • 提供注解来识别测试方法
  • 编写代码更快,还能提高质量
  • 代码简洁,花费时间少
  • 有显示进度条,绿色运行良好,红色运行失败

02-单元测试-基本使用

基本流程:

  • 导入jar包
  • 测试方法必须是无参无返回值的非静态方法
  • 测试方法加@Test注解标注该方法是测试方法
  • 右键通过Junit运行该方法

导包:

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
public class TestDemo01 {

    public static void main(String[] args) {

    }

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

image-20230712225135672

03-单元测试-常用注解

image-20230712225219235

public class TestDemo01 {

    public static void main(String[] args) {

    }

    @After
    public void testBefore(){
        System.out.println("testBefore");
    }

    @Before
    public void testAfter(){
        System.out.println("testAfter");
    }

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

image-20230712225337211

13-5-2-日志技术

04-日志技术与输出语句的区别

日志的作用:

记录程序运行的详细信息,并永久存储

类似输出语句,但是也有区别。

输出语句有如下弊端:

  • 想取消记录的信息需要修改代码才可以完成
  • 信息只能展示在控制台,不能记录到其他位置(文件,数据库)

对比:

image-20230712225728328

05-日志技术-体系结构和Log4j

image-20230712225857876

06-日志技术-Log4j入门案例

开发流程

  • 导如Log4j包
  • 编写Log4j配置文件
  • 代码中获取日志对象
  • 按照级别设置日志记录信息
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>

配置文件:文件名必须是log4j.properties

log4j.rootLogger=debug,my,fileAppender

### direct log messages to my ###
log4j.appender.my=org.apache.log4j.ConsoleAppender
log4j.appender.my.ImmediateFlush = true
log4j.appender.my.Target=System.out
log4j.appender.my.layout=org.apache.log4j.PatternLayout
log4j.appender.my.layout.ConversionPattern=%d %t %5p %c{1}:%L - %m%n

# fileAppenderʾ
log4j.appender.fileAppender=org.apache.log4j.FileAppender
log4j.appender.fileAppender.ImmediateFlush = true
log4j.appender.fileAppender.Append=true
log4j.appender.fileAppender.File=D:/log4j-log.log
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=%d %5p %c{1}:%L - %m%n

public class LogDemo {

    /**
     * 获取日志对象 可以根据log4j自己的api来获取日志对象
     * 弊端:以后更换日志框架,代码也要跟着修改
     * 不推荐使用,一般使用sl4f的api获取
     */

    private static final org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(LogDemo.class);


    /**
     * 推荐使用日志门面 这样更换日志实现类时不需要修改代码
     */
    private static final org.slf4j.Logger sl4jLogger = org.slf4j.LoggerFactory.getLogger(LogDemo.class);


    public static void main(String[] args) {
        log4jLogger.debug("你好 debug");
        log4jLogger.info("你好 info");
        log4jLogger.warn("你好 warn");
        log4jLogger.error("你好 error");

        sl4jLogger.debug("sl4jLogger你好 debug");
        sl4jLogger.info("sl4jLogger你好 info");
        sl4jLogger.warn("sl4jLogger你好 warn");
        sl4jLogger.error("sl4jLogger你好 error");

    }
}

运行结果:(这里由于没有找到对应的slf4j-lo4j.jar),所以使用日志门面进行记录日志时报错。后面使用spring框架后会集成,不纠结了。

image-20230713001904269

07-日志技术-Log4j三个核心

  • Loggers 记录器 --- 日志的级别
  • Appenders 输出源 --- 日志要输出的地方
  • Layouts 布局 --- 日志输出的格式

Loggers

Loggers组件在此系统中常见的五个级别:DEBUG、INFO、WARN、ERROR和FATAL

Log4j有个规则:只输出不低于设定级别的日志信息

日志级别:DEBUG<INFO<WARN<ERROR<FATAL

Appenders

把日志输出到不同的地方,如控制台、文件等

org.apache.log4j.ConsoleAppender(控制台)

org.apache.log4j.FileAppender(文件)

image-20230713002505655

Layout

根据自己的喜好指定日志输出的格式。

常见的布局管理器

org.apache.log4j.PatternLayout(可以灵活的指定布局模式)

org.apache.log4j.SimpleLayout(包含日志的级别和信息字符串)

org.apache.log4j.TTCCLayout (包括日志的产生时间、线程、类别等信息)

image-20230713002728685

08-日志技术-配置文件详解

配置Loggers

image-20230713002949342

配置Appender

image-20230713003037688

其中如果使用ConsoleAppender

image-20230713003045350

如果使用FileAppender:

image-20230713003240352

配置Layout

image-20230713003416022

如果是使用PatternLayout

image-20230713003704184

PatternLayout常用的选项

Loading...