JAVA注解使用

news2024/9/27 17:36:15

JAVA注解使用

    • 简介
      • 概念说明
      • 自定义注解
        • 注解格式
        • 元注解
        • 注解本质
        • 属性
    • 注解生成文档案例
      • 生成的源代码
      • 生成Doc
    • 简单的自定义反射演示
      • 注解定义
      • 调用类定义
      • 测试
    • 通过注解实现配置类
      • 定义枚举类
      • 定义注解
      • 配置类使用注解
      • 测试
    • 自动生成数据库生成演示
      • 数据库注解定义
      • 使用注解创建数据表
      • 使用注解创建数据库
      • 测试
    • 使用方法注解实现开机自检演示
      • 创建方法注解
      • 注解调用
      • 测试
    • 注解自动生效
      • 添加pom依赖
      • 自定义约束注解
      • 使用注解
      • 验证注解
      • 测试

参考资料
详解Java如何实现自定义注解
https://www.jb51.net/article/252457.htm

简介

概念说明

概念

  • 概念:说明程序的。给计算机看的
  • 注释:用文字描述程序的。给程序员看的
  • 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

作用和作用分类

  • 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
  • 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
  • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

JDK中预定义的一些注解

  • @Override:检测被该注解标注的方法是否是继承自父类(接口)的
  • @Deprecated:该注解标注的内容,表示已过时
  • @SuppressWarnings:压制警告

一般传递参数all @SuppressWarnings(“all”)

自定义注解

注解格式

元注解[例如:@Target@Retention@Documented]
第三方注解[例如: @Constraint]
public @interface 注解名称{
    属性列表;
}

元注解

JDK默认提供了@Target、@Retention、@Documented、@Inherited、@Native、@Repeatable六大默认注解。

  1. @Target 解析注解在什么地方可以进行使用,可以同时支持多种,主要包含以下类型:
  • TYPE 可以在 类、接口、注解、枚举上使用
  • FIELD 可以在字段、枚举常量上使用
  • METHOD 可以在方法上使用
  • PARAMETER 可以在方法参数上使用
  • CONSTRUCTOR 可以在构建函数上使用
  • LOCAL_VARIABLE 可以在本地变量上使用
  • ANNOTATION_TYPE 可以在注解类型上使用,例如@Retention就用到了这个注解
  • PACKAGE 可以在上使用
  • TYPE_PARAMETER 可以在类型参数上使用
  • TYPE_USE 可以作为一个类型使用
  1. @Retention 标识注解被保留的阶段
  • SOURCE 注解在编译阶段会被忽略,不进行保留
  • CLASS 注解在编译阶段会被记录下来,但是不会保留在运行时。这是默认的行为
  • RUNTIME 注解在编译阶段会被记录下来,并保留在运行时内,所以可以通过反射来读取
  1. @Documented 注解是否会被输出到JavaDoc文档里
  2. @Inherited 注解是否可以被继承
  3. @Native 定义一个可以被Native 编码访问到的常量
  4. @Repeatable 标识使用@Repeatable申明的注解类型是可以重复的

注解本质

注解本质上就是一个接口,该接口默认继承Annotation接口

public interface MyAnno extends java.lang.annotation.Annotation {}

属性

接口中的抽象方法,比如定义了一个名为name()的抽象方法,在使用的时候按照属性的方式来进行赋值, 例如name="张超"

要求:

  • 1.属性的返回值类型有下列取值

    • 基本数据类型
    • String
    • 枚举
    • 注解
    • 以上类型的数组
  • 2.定义了属性,在使用时需要给属性赋值

    • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
    • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
    • 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

注解生成文档案例

生成的源代码

我们以DatabaseConfig .java为示例来生成一份JavaDoc文档

/**
 * 数据表类注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseConfig {
    /**
     * 数据库表Class集合,用于生成数据库表
     *  默认为空集合
     * */
    Class<?>[] entity() default {};

    /**
     * 数据库版本
     *  默认为1
     * */
    int version() default 1;

    /**
     * 数据库名称
     *   默认为test_db
     * */
    String databaseName() default "test_db";
}

生成Doc

执行javadoc -encoding utf-8 DatabaseConfig.java,然后就可以生成一系列的文档文件了,打开生成后的文档文件,可以看到对类的详细说明
在这里插入图片描述

简单的自定义反射演示

注解定义

/**
 * 描述需要执行的类名,和方法名
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Reflect {
    String className();
    String methodName();
}

调用类定义

package com.example.call;

public class BlackPrinter {
    public static void print() {
        System.out.println("通过注解和反射来打印一条信息");
    }
}

测试

@Reflect(className = "com.example.call.BlackPrinter", methodName = "print")
public class ReflectTest {
    public static void main(String[] args) throws Exception {

        /**
         * 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
        //1.解析注解
        // 1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;

        //2.获取上边的注解对象, 其实就是在内存中生成了一个该注解接口的子类实现对象
        /*
        public class ReflectImpl implements Reflect{
            public String className(){
                return "com.example.call.BlackPrinter";
            }
            public String methodName(){
                return "print";
            }
        }
        */

        //3.调用注解对象中定义的抽象方法,获取返回值
        Reflect reflect = reflectTestClass.getAnnotation(Reflect.class);
        String className = reflect.className();
        String methodName = reflect.methodName();
        System.out.println(className);
        System.out.println(methodName);

        // 4.加载该类进内存
        Class<?> cls = Class.forName(className);
        // 5.创建对象
        Object obj = cls.newInstance();
        // 6.获取方法对象
        Method method = cls.getMethod(methodName);
        // 7.执行方法
        method.invoke(obj);
    }
}

输出如下:

com.example.call.BlackPrinter
print
通过注解和反射来打印一条信息

整个程序的大体逻辑如下:

    1. 在程序最外层通过@Reflect注解配置了需要进行反射的类名、需要调用的方法名,这样就可以动态的对需要调用的类、方法进行配置。
    1. 程序执行过程中通过解析Test类的字节码文件,动态解析出@Reflect对象,然后获取其中配置的类名、方法名。
    1. 通过Class对类进行加载、对方法进行调用。

通过注解实现配置类

定义枚举类

/**
 * 协议类型枚举
 */
public enum ProtocolType {
    WebSocket, MQTT;
}

定义注解

/**
 * 白名单配置
 */
@Target(ElementType.TYPE)            //描述注解能够作用的位置
@Retention(RetentionPolicy.RUNTIME)  //描述注解被保留的阶段
public @interface WhiteConfig {
    /**
     * 白名单地址列表
     * */
    String[] whiteIpAddress();
}
/**
 * 协议配置注解
 */
@Target(ElementType.TYPE)            //描述注解能够作用的位置
@Retention(RetentionPolicy.RUNTIME)  //描述注解被保留的阶段
public @interface ProtocolConfig {
    // 服务器端口号
    int port();

    // 协议类型[使用枚举来作为参数]
    ProtocolType protocolType();

    // 白名单配置[使用另一个注解来作为参数]
    WhiteConfig whiteConfig();

    // 服务器端口号[使用数组来作为参数]
    String[] urls();
}

配置类使用注解

/**
* 配置类
*/
@ProtocolConfig(port = 9100 ,protocolType = ProtocolType.MQTT ,urls="172.16.40.121",
        whiteConfig = @WhiteConfig(whiteIpAddress = {"119.16.254.37", "116.51.39.46"}))
public class ServerConfig {

}

测试

/**
 * 自定义注解测试
 *
 * @author panyongjie
 * @date 2022/12/29
 */
public class AnnotationTest {
    public static void main(String[] args) {
        ServerConfig serverConfig = new ServerConfig();
        ProtocolConfig protocolConfig = serverConfig.getClass().getAnnotation(ProtocolConfig.class);
        System.out.println("port: " + protocolConfig.port());
        System.out.println("protocolType: " + protocolConfig.protocolType());

        for (String url : protocolConfig.urls()) {
            System.out.println("url: " + url);
        }
        for (String whiteIpAddress : protocolConfig.whiteConfig().whiteIpAddress()) {
            System.out.println("whiteIpAddress: " + whiteIpAddress);
        }
    }
}

运行效果如下

port: 9100
protocolType: MQTT
url: 172.16.40.121
whiteIpAddress: 119.16.254.37
whiteIpAddress: 116.51.39.46

自动生成数据库生成演示

数据库注解定义

这里定义四个注解,分别对应字段主键数据表数据库 配置。

/**
 * 表格字段注解,用于实体类字段与数据表字段之间的映射
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {
    // 数据表字段名称
    String name();
}
/**
 * 主键注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrimaryKey {
    boolean autoIncrease() default false;
}
/**
 * 表注解,用于生成数据库结构
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String name();
}
/**
 * 数据表类注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseConfig {
    /**
     * 数据库表Class集合,用于生成数据库表
     *  默认为空集合
     * */
    Class<?>[] entity() default {};

    /**
     * 数据库版本
     *  默认为1
     * */
    int version() default 1;

    /**
     * 数据库名称
     *   默认为test_db
     * */
    String databaseName() default "test_db";
}

使用注解创建数据表

/**
 * 人员信息表
 */
@Table(name = "t_user")
public class User {
    @TableField(name = "user_id")
    @PrimaryKey(autoIncrease = true)
    private int userId;

    @TableField(name = "user_name")
    private String userName;

    private String genderName;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}
/**
 * 记录信息表
 */
@Table(name = "t_record")
public class Record {
    @TableField(name = "record_id")
    @PrimaryKey(autoIncrease = true)
    private int recordId;

    @TableField(name = "user_id")
    private int userId;

    @TableField(name = "user_name")
    private String userName;

    @TableField(name = "record_time")
    private String recordTime;

    private int syncState;

    public int getRecordId() {
        return recordId;
    }

    public void setRecordId(int recordId) {
        this.recordId = recordId;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getRecordTime() {
        return recordTime;
    }

    public void setRecordTime(String recordTime) {
        this.recordTime = recordTime;
    }

    public int getSyncState() {
        return syncState;
    }

    public void setSyncState(int syncState) {
        this.syncState = syncState;
    }
}

使用注解创建数据库

/**
 * 数据库测试
 *
 * @author panyongjie
 * @date 2022/12/29
 */
@DatabaseConfig(entity = { User.class, Record.class }, version = 2, databaseName = "staff")
public class DatabaseTest {

    /**
     * 读取配置,解析数据库配置
     * */
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 获取当前的Class类型
        Class<DatabaseTest> databaseTestClass = DatabaseTest.class;

        DatabaseConfig databaseConfig = databaseTestClass.getAnnotation(DatabaseConfig.class);
        Class<?>[] classes = databaseConfig.entity();
        System.out.println("读取数据库配置,数据库名称: " +  databaseConfig.databaseName() +
                 " ,数据库版本:" + databaseConfig.version() +
                 " ,当前有" + classes.length + "个表格注解类需要解析");

        for (Class<?> cls : classes) {
            System.out.println("className: " + cls.getName());
            Class<?> userClass = Class.forName(cls.getName());

            Table table = userClass.getAnnotation(Table.class);
            System.out.println("表格名称: " + table.name());

            Field[] fields = userClass.getDeclaredFields();
            for (Field field : fields) {
                if(field.isAnnotationPresent(TableField.class)) {
                    TableField tableField = field.getAnnotation(TableField.class);
                    System.out.println("\t字段: " + field.getName() + "需要添加到数据表中,数据字段名称: " + tableField.name());
                    if(field.isAnnotationPresent(PrimaryKey.class)) {
                        PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                        System.out.println("\t字段: " + field.getName() + "是当前数据表的主键,主键" + (primaryKey.autoIncrease() ? "自增" : "非自增"));
                    }
                } else {
                    System.out.println("\t字段: " + field.getName() + "不需要添加到数据表中");
                }
            }
        }
    }
}

测试

测试效果如下

读取数据库配置,数据库名称: staff ,数据库版本:2 ,当前有2个表格注解类需要解析
className: com.example.table.User
表格名称: t_user
	字段: userId需要添加到数据表中,数据字段名称: user_id
	字段: userId是当前数据表的主键,主键自增
	字段: userName需要添加到数据表中,数据字段名称: user_name
	字段: genderName不需要添加到数据表中
className: com.example.table.Record
表格名称: t_record
	字段: recordId需要添加到数据表中,数据字段名称: record_id
	字段: recordId是当前数据表的主键,主键自增
	字段: userId需要添加到数据表中,数据字段名称: user_id
	字段: userName需要添加到数据表中,数据字段名称: user_name
	字段: recordTime需要添加到数据表中,数据字段名称: record_time
	字段: syncState不需要添加到数据表中

可以看到我们通过配置的注解和注解值都能正常进行解析,这样我们就可以根据这些解析出来的值组装成SQL脚本然后去执行生成数据库的操作。

使用方法注解实现开机自检演示

创建方法注解

/**
 * 检查注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
    // 检查项名称
    String checkItemName();

    // 异常类型
    String exceptionType();
}

注解调用

import com.example.annotation.Check;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;

/**
 * 计算机类
 *
 * @author panyongjie
 * @date 2022/12/29
 */
public class Computer {

    /**
     * 最大支持内存,单位G
     * */
    private final int MAX_SUPPORT_MEMORY = 32;

    /**
     * 最大支持硬盘,单位G
     * */
    private final int MAX_SUPPORT_DISK = 2048;

    /**
     * 支持的操作系统
     * */
    private final List<String> SUPPORT_OS_LIST = Arrays.asList(
    	    "windows 2003", "windows vista",
            "windows 7", "windows 10", "windows 11", "windows 12",
            "macos", "linux", "ubuntu","centos");

    /**
     * 内存大小,单位G
     * */
    private int memory;

    /**
     * 硬盘大小,单位G
     * */
    private int disk;

    /**
     * 系统版本
     * */
    private String systemVersion;

    public Computer(int memory, int disk, String systemVersion) {
        this.memory = memory;
        this.disk = disk;
        this.systemVersion = systemVersion;
    }

    /**
     * 内存检查
     * */
    @Check(checkItemName = "内存", exceptionType = "超出内存支持上限/下限")
    public void memoryCheck() throws Exception {
        if(memory > 0 && memory <= MAX_SUPPORT_MEMORY) {
            System.out.println("内存自检正常");
        } else {
            throw new Exception("内存大小超出可用范围!");
        }
    }

    /**
     * 硬盘检查
     * */
    @Check(checkItemName = "硬盘", exceptionType = "超出硬盘支持上限/下限")
    public void diskCheck(){
        if(disk > 0 && disk <= MAX_SUPPORT_DISK) {
            System.out.println("硬盘自检正常");
        } else {
            throw new RuntimeException("硬盘大小超出可用范围!");
        }
    }
    /**
     * 系统检查
     * */
    @Check(checkItemName = "系统版本", exceptionType = "超出系统版本支持范围")
    public void systemCheck(){
        if(!StringUtils.isEmpty(systemVersion) && SUPPORT_OS_LIST.contains(systemVersion)) {
            System.out.println("系统自检正常");
        } else {
            throw new RuntimeException("不支持的系统版本!");
        }
    }

    /**
    * 屏幕信号检查
    * */
    @Check(checkItemName = "屏幕信号", exceptionType = "超出显卡支持上限/下限")
    public void signalCheck(){
        System.out.println("屏幕信号输出正常");
    }

    public void start(){
        System.out.println("开始开机自检...");
    }

    public void finish(){
        System.out.println("开始开机自检完毕!");
    }
}

测试

/**
 * 开机自检测试类
 * 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,并对异常进行打印输出
 */
public class CheckTest {

    public static void main(String[] args) {
        // 1.创建计算器对象
        Computer computer = new Computer(64, 3072, "windows 10");
        // 2.获取所有方法
        Method[] methods = computer.getClass().getMethods();

        computer.start();
        System.out.println();

        //出现异常的次数
        int count = 0;
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                Check check = method.getAnnotation(Check.class);
                System.out.println("--------------------------");
                System.out.println("开始" + check.checkItemName() + "自检...");
                try {
                    method.invoke(computer);
                } catch (Exception e) {
                    count++;
                    System.out.println(check.checkItemName() + "自检异常!");
                    System.out.println("自检异常类型: " + check.exceptionType());
                    System.out.println("自检异常原因: " + e.getCause().getMessage());
                }
            }
        }

        System.out.println("--------------------------");
        System.out.println();
        System.out.println("本次开机自检一共出现 " + count + " 次异常");
        computer.finish();
    }
}

运行输出如下

开始开机自检...

--------------------------
开始内存自检...
内存自检异常!
自检异常类型: 超出内存支持上限/下限
自检异常原因: 内存大小超出可用范围!
--------------------------
开始硬盘自检...
硬盘自检异常!
自检异常类型: 超出硬盘支持上限/下限
自检异常原因: 硬盘大小超出可用范围!
--------------------------
开始屏幕信号自检...
屏幕信号输出正常
--------------------------
开始系统版本自检...
系统自检正常
--------------------------

本次开机自检一共出现 2 次异常
开始开机自检完毕!

注解自动生效

在Springboot web环境中进行测试

添加pom依赖

<!-- Web启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
        
<!-- 验证API -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.4.5</version>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

自定义约束注解

/**
 * 自定义不为0注解
 */
@Documented   //是否会生成文档
@Constraint(validatedBy = {LongConstraintValidator.class})
@Retention(RetentionPolicy.RUNTIME)  //描述注解被保留的阶段
@Target({ElementType.FIELD, ElementType.METHOD})  //生效的目标,对字段和方法生效
public @interface LongDataValidate {
    String message() default "不能为null或者0";

    Class<?>[] groups()  default {};

    Class<? extends Payload>[] payload() default  {};
}
/**
 * Long类型验证器
 */
public class LongConstraintValidator implements ConstraintValidator<LongDataValidate, Long> {
    @Override
    public void initialize(LongDataValidate constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        System.out.println("调用了 initialize 方法");
    }

    @Override
    public boolean isValid(Long value, ConstraintValidatorContext context) {
        System.out.println("调用了 isValid 方法");
        return null != value && value != 0;
    }
}

使用注解

/**
 * 通行记录请求
 */
public class AccessRecordReq {
    @LongDataValidate(message = "序列号不能为null或者0")
    private Long serialNo;

    public AccessRecordReq(Long serialNo) {
        this.serialNo = serialNo;
    }

    public Long getSerialNo() {
        return serialNo;
    }

    public void setSerialNo(Long serialNo) {
        this.serialNo = serialNo;
    }
}

验证注解

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private int code = 200;
    private String msg = "success";
    private T data;
}
@RestControllerAdvice
public class ApiController {
    public <T> Result<T> success(T t) {
        return new Result<>(200, "success", t);
    }

    @ExceptionHandler({BindException.class, Exception.class})
    public Result<Void> handleException(Throwable e) {
        Result<Void> result = new Result<>();
        if (e instanceof  BindException) {
            result.setCode(1000);
            result.setMsg(((BindException) e).getFieldError().getDefaultMessage());
        }
        else {
            result.setCode(500);
            result.setMsg(e.getMessage());
        }
        return  result;
    }
}
import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.Serializable;
import java.util.List;

/**
 * (Customer)表控制层
 */
@RestController
@RequestMapping("customer")
public class CustomerController extends ApiController {
    @GetMapping("validate")
    @ResponseBody
    public Result<Object> validateData(@Valid AccessRecordReq accessRecordReq) {
        return success("验证通过" + accessRecordReq.getSerialNo());
    }
}


测试

在浏览器中输入http://localhost:8080/customer/validate?serialNo=或者
http://localhost:8080/customer/validate?serialNo=0, 服务器控制台输出

调用了 initialize 方法
调用了 isValid 方法

浏览器上输出

{"code":1000,"msg":"序列号不能为null或者0","data":null}

重新输入http://localhost:8080/customer/validate?serialNo=1000, 浏览器上输出

{"code":200,"msg":"success","data":"验证通过1000"}

可以发现我们的自定义注解已自动生效并进行按照我们定义的规则进行了数据验证。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Java安全]—fastjson漏洞利用

前言 文章会参考很多其他文章的内容&#xff0c;记个笔记。 FASTJSON fastjson 组件是 阿里巴巴开发的序列化与 反序列化组件。 fastjson 组件 在反序列化不可信数据时会导致远程代码执行。 POJO POJO是 Plain OrdinaryJava Object 的缩写 &#xff0c;但是它通指没有使用 …

ZGC 垃圾收集器详解(过程演示)

理论部分就不细讲了&#xff0c;具体可以看《 jvm 虚拟机原理》&#xff0c;下面直接画图来演示 ZGC 垃圾回收过程。 第一步 初始状态&#xff0c;视图由 mark0 切换为 Remapped &#xff0c;其中&#xff0c;大方块是 region&#xff0c;小方块是对象&#xff0c;小方块上面数…

Win11的几个实用技巧系列之不能玩植物大战僵尸、如何彻底删除360所有文件

目录 Win11不能玩植物大战僵尸怎么办?Win11玩不了植物大战僵尸的解决方法 Win11玩不了植物大战僵尸的解决方法 win11如何彻底删除360所有文件?win11彻底删除360所有文件方法分享 win11如何卸载360&#xff1a; Win11不能玩植物大战僵尸怎么办?Win11玩不了植物大战僵尸的解…

记一次H3CIE实验考试

一、前言 直接上图 IE机试在12月19号考的&#xff0c;为避免成为小羊人&#xff0c;没去北京/杭州这2个固定地点&#xff0c;就在本省的协办单位考的。但是&#xff0c;还是中招了&#xff0c;5个同学一起去考的&#xff0c;全阳了。 华三机试一共有三套图&#xff0c;ACD&am…

1343:【例4-2】牛的旅行

1343&#xff1a;【例4-2】牛的旅行 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 农民John的农场里有很多牧区。有的路径连接一些特定的牧区。一片所有连通的牧区称为一个牧场。但是就目前而言&#xff0c;你能看到至少有两个牧区不连通。现在&#xff0c;John…

【特殊的一年,过去了】再见2022,你好2023

现在是2022年12月30日&#xff0c;提前的新年快乐&#xff01; 各位阳过了吗&#xff1f;&#xff08; tips:最近新学会的打招呼方式:) &#xff09; 我已经阳康啦&#xff0c;所以本文是带有奥密克戎抗体的&#xff0c;各位不用担心~ – 2022可算是快接近尾声啦&#xff01;…

虹科案例|Vuzix辅助和增强现实技术的全球领导者

当今企业的新现实 Vuzix 大多数科技业内人士都认为&#xff0c;未来将是免提时代。总有一天&#xff0c;智能手机、平板电脑、台式电脑和笔记本电脑将被更直观的设备所取代。 然而&#xff0c;对于未来免提的条件&#xff0c;各方意见并不一致。未来的免提设备是将数字信息覆盖…

IntelliJ IDEA 详细使用教程 – 主题,字体,类和方法注释设置

IDEA是Java开发者最喜爱的开发工具之一&#xff0c;高端大气&#xff0c;智能化&#xff0c;个性化&#xff0c;每个开发者都喜欢设置自己喜欢的主题&#xff0c;字体&#xff0c;打造一个属于自己的IDE&#xff0c;本次介绍在IDEA中&#xff0c;如何设置主题&#xff0c;字体等…

聊聊AQS

Java中 AQS 是 AbstractQueuedSynchronizer 类&#xff0c;AQS 依赖 FIFO 队列来提供一个框架&#xff0c;这个框架用于实现锁以及锁相关的同步器&#xff0c;比如信号量、事件等。 在 AQS 中&#xff0c;主要有两部分功能&#xff0c;一部分是操作 state 变量&#xff0c;第二…

调用html5播放器时,出现播放器按钮太小的问题

用手机浏览器打开视频&#xff0c;有时会出现播放器按钮太小的情况&#xff0c;此时只需在<head>中加入下面这段viewport代码即可解决&#xff1a; <meta name"viewport" content"widthdevice-width, initial-scale1, maximum-scale1,minimum-scale1…

Docker下Mysql应用部署

目录 环境搭建 进入mysql 外部连接mysql 外部插入数据 查询容器数据 环境搭建 docker pull mysqlmkdir /root/mysql cd /root/mysqldocker run -id \ -p 3307:3306 \ --name my_sql \ -v $PWD/logs:/logs \ -v $PWD/data:/var/lib/mysql \ -v $PWD/conf:/etc/mysql/conf…

【开源项目】任务调度框架PowerJob介绍及源码解析

项目介绍 PowerJob&#xff08;原OhMyScheduler&#xff09;是全新一代分布式调度与计算框架&#xff0c;能让您轻松完成作业的调度与繁杂任务的分布式计算。 项目地址 源码&#xff1a;https://gitee.com/KFCFans/PowerJob官网&#xff1a;http://www.powerjob.tech/index…

前端期末考试试题及参考答案(01)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 一、 填空题 ______表示页面中一个内容区块或整个页面的标题。______表示页面中一块与上下文不相关的独立内容&#xff0c;比如一篇文章。CSS的引入方式有3种&#xff0c;分…

Python数据分析案例15——超市零售购物篮关联分析(apriori)

啤酒和纸尿裤的故事大多数人都听说过&#xff0c;纸尿裤的售卖提升了啤酒的销售额。 关联分析就是这样的作用&#xff0c;可以研究某种商品的售卖对另外的商品的销售起促进还是抑制的作用。 案例背景 本次案例背景是超市的零售数据&#xff0c;研究商品之间的关联规则。使用的…

移植SFUD,驱动SPI FLASH ZD25WQ80

1、关于SFUD SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库&#xff0c;支持众多spi flash&#xff0c;关于SFUD的详细资料可参考&#xff1a;https://github.com/armink/SFUD。 2、为什么会有通用驱动 JEDEC &#xff08;固态技术协会&#xff09;针对串行 …

Python的22个万用公式,你确定不看看吗

前言 在大家的日常python程序的编写过程中&#xff0c;都会有自己解决某个问题的解决办法&#xff0c;或者是在程序的调试过程中&#xff0c;用来帮助调试的程序公式。 小编通过几十万行代码的总结处理&#xff0c;总结出了22个python万用公式&#xff0c;可以帮助大家解决在…

TypeScript中type和interface区别

typescript中interface介绍&#xff1a;TypeScript 中的接口 interface_疆~的博客-CSDN博客通常使用接口&#xff08;Interface&#xff09;来定义对象的类型。https://blog.csdn.net/qq_40323256/article/details/128478749 type type关键字是声明类型别名的关键字。用来给一…

windows 编译C++ boost库(超详细)

系列文章目录 文章目录系列文章目录前言一、windows二、b2.exe 参数前言 boost库其实不进行编译&#xff0c;大部分库也是可以正常使用的 而且也有一个开源工具vcpkg可以帮组我们下载编译&#xff0c;只是在国内用起来比较麻烦&#xff0c;而且还时常出bug 所以这里详细记录…

mac下,使用 docker 搭建,单机机器集群

背景&#xff1a; 在 Mac本下&#xff0c;通过 docker 完成一个 es 集群&#xff08;3台-或许可多台&#xff09;搭建。&#xff08;后续如果有真实的机器&#xff0c;只需要又该对应的 ip 地址即可&#xff0c;需要关注的是&#xff0c;机器间是可以互相 ping通的&#xff0c;…

4.3.5、IPv4 地址的应用规划

给定一个 IPv4 地址块&#xff0c;如何将其划分成几个更小的地址块&#xff0c;并将这些地址快分配给互联网中的不同网络&#xff0c; 进而可以给各网络中的主机和路由器接口分配 IPv4 地址。 一般有两种方法&#xff1a; 定长的子网掩码 FLSM &#xff08;Fixed Length Sub…