16550 字
47 分钟
Java/SpringBoot 学习笔记
2024-11-10

java命名规范#

  1. entity 包

    1. config: 作用: 存放与实体类相关的配置类,例如 JPA 实体监听器、自定义类型转换器等。 内容: 通常情况下,实体类相关的配置比较少,可以不创建 config 包,而是将配置类放在 entity 包下。

    2. constants: 作用: 存放与实体类相关的常量,例如表名、字段名、错误信息等。 内容: 可以将所有实体类的常量都放在一个 Constants 类中,也可以根据实体类进行分组,每个实体类对应一个常量类。

    3. dto (Data Transfer Object): 作用: 存放数据传输对象,用于在不同层之间传递数据。 内容: DTO 通常只包含数据,不包含业务逻辑。 可以根据不同的业务场景创建不同的 DTO 类,例如 UserCreateDTO、UserUpdateDTO、UserResponseDTO 等。

    4. enums: 作用: 存放枚举类,用于定义实体类中字段的取值范围。 内容: 将枚举类放在 enums 包下可以使代码结构更加清晰。

    5. po (Persistent Object): 作用: 存放持久化对象,与数据库表结构一一对应。 内容: PO 通常只包含属性和 getter/setter 方法,不包含业务逻辑。 在使用 JPA 的情况下,PO 类上需要使用 @Entity 注解。 有些开发者会将 entity 包直接作为 po 包使用,不再区分 entity 和 po。

    6. query: 作用: 存放查询对象,用于封装复杂的查询条件。 内容: 当查询条件比较简单时,可以直接在 Repository 接口中定义查询方法。 当查询条件比较复杂时,可以将查询条件封装成一个 query 对象,方便传递参数和复用查询逻辑。

    7. vo (Value Object): 作用: 存放值对象,用于封装返回给前端的数据。 内容: VO 通常只包含数据,不包含业务逻辑。 可以根据不同的接口返回不同的 VO 类。

  2. utils 包

    作用: 存放工具类,提供通用的功能方法。 内容: 工具类通常是无状态的,包含静态方法,方便其他类调用。 可以包含字符串处理、日期时间格式化、文件操作、网络请求等各种具类。

  3. component 包: 作用: 存放 Spring 组件类,例如 Service 类、Repository 类、Configuration 类等。 内容: @Service: 业务逻辑层,处理业务逻辑,调用 Dao 层完成数据操作。 @Repository: 数据访问层,负责与数据库交互,执行数据操作。 @Component: 泛指组件,当一个类不适合归类到 @Service, @Repository 或 @Controller 时,可以使用 @Component 注解。 @Configuration: 配置类,用于配置 Spring Bean。

IDEA常用快捷键#

ctrl+shift+u 大小写转换

ctrl+alt+点击 转到实现处

Ctrl + W 选中代码,连续按还会选中更多

Ctrl + Alt + L 格式化代码

Ctrl + H 显示类结构图

Ctrl + I 实现方法

springboot常用注解#

1.注册bean对象注解#

@Component: 通用的组件注解,可以用于标记任何需要被 Spring 管理的类。 @Service: 用于标记业务逻辑层组件,通常包含业务逻辑处理代码。 @Repository: 用于标记数据访问层组件,通常用于与数据库交互。 @Controller: 用于标记 Web 层控制器组件,通常用于处理 HTTP 请求。

2.扫描bean对象注解#

@MapperScan MyBatis 框架提供的一个注解,==用于扫描指定包下的所有 Mapper 接口==,并将这些接口注册为 Spring 管理的 Bean。

@ComponentScan 是 Spring 框架提供的一个注解,用于==指定 Spring 应该扫描哪些包以查找并注册带有特定注解的类为 Spring 管理的 Bean==。这些特定注解包括但不限于 @Component、@Service、@Repository 和 @Controller。

@SpringBootApplication 默认扫描当前包及其子包 ,是 Spring Boot 提供的一个复合注解,它实际上是以下三个注解的组合:

  • @Configuration:表示该类是一个配置类,可以用来定义 Bean。 @EnableAutoConfiguration:启用 Spring Boot 的自动配置功能,根据添加的 jar 依赖自动配置 Spring 应用。 @ComponentScan:启用组件扫描,自动发现和注册 Spring 管理的 Bean。

综合使用示例

假设你有一个 Spring Boot 应用,需要从 com.ruoyi.math.mappers 包中扫描并注册所有 MyBatis 的 Mapper 接口,从 com.ruoyi 包中扫描所有 Spring 组件,并且还需要额外从 com.example.servicecom.example.repository 包中扫描更多的 Spring 组件。可以这样做:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication(scanBasePackages = {"com.ruoyi"})
@MapperScan(basePackages = {"com.ruoyi.math.mappers"})
@ComponentScan({"com.example.service", "com.example.repository"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
  1. @SpringBootApplication(scanBasePackages = {"com.ruoyi"})

    • 启用了 Spring Boot 的自动配置功能。
    • 从 com.ruoyi 包及其子包中扫描并注册所有 Spring 组件。
  2. @MapperScan(basePackages = {"com.ruoyi.math.mappers"})

    • 从 com.ruoyi.math.mappers 包中扫描并注册所有 MyBatis 的 Mapper 接口。
  3. @ComponentScan({"com.example.service", "com.example.repository"})

    • 从 com.example.service 和 com.example.repository 包中扫描并注册所有 Spring 组件。
  4. 重复扫描:如果 @SpringBootApplication 和 @ComponentScan 指定的包路径有重叠,可能会导致某些组件被多次扫描。通常情况下,@SpringBootApplication 已经足够,不需要额外使==用 ====@ComponentScan====,除非你需要扫描额外的包==。

  5. 默认行为:如果没有显式指定 @ComponentScan@SpringBootApplication 默认会从当前类所在的包及其子包开始扫描。

3.@Value注解#

@Value 注解用于将属性值注入到 bean 的属性、构造函数参数或方法参数中。

它可以读取和注入来自各种来源的配置属性值

例如: properties 文件 @Value(”${spring.mail.username:}”) YAML 文件 环境变量 系统属性

2024/11/15#

1.maven项目pom.xml文件#

用于管理项目的依赖、构建过程和其他配置,实质上是对jar包的管理

  • (1)项目信息

  • ==<groupId>==com.MiKouPan_workspace</groupId>: 项目的组 ID,通常是公司或组织的域名反写。

  • ==<artifactId>==MiKouPan_workspace</artifactId>: 项目的 Artifact ID,项目的唯一标识符。

  • ==<version>==0.0.1-SNAPSHOT</version>: 项目的版本号。 SNAPSHOT 表示这是一个开发版本。

  • ==上述三者共同构成maven的坐标,一起唯一确定一个 Maven 构件。==

  • <name>MiKouPan_workspace</name>: 项目的名称。

  • <description>MiKouPan_workspace</description>: 项目的描述

  • (2)父项目

  • <parent> 部分定义了项目的父项目,继承自 spring-boot-starter-parent。 这样可以继承 Spring Boot 的默认配置,简化项目的配置管理。

  • ==父项目(parent project)本身也是一个 Maven 项目==,它通常位于 Maven 仓库中。 你在子项目的 pom.xml 文件中通过 <parent> 元素声明父项目的坐标,Maven 就会从仓库中下载父项目的 pom.xml 文件,并继承其配置。

  • (3)属性

  • <properties> 部分定义了一些项目属性,例如 Java 版本、编码、Spring Boot 版本、MyBatis ==版本==等。 这些属性可以在 pom.xml 文件的其他地方使用,例如在依赖的版本号中。 使用属性可以使版本管理更方便,避免重复。

  • **(4)依赖 **

    <dependencies> 部分定义了项目的依赖项

  • 常用的有:

  • spring-boot-starter-web: Spring Boot Web 启动器,包含了 Spring MVC、嵌入式 Tomcat 等 Web 开发所需的依赖。

  • spring-boot-starter-test: Spring Boot 测试启动器,包含了测试所需的依赖。

  • spring-boot-starter-mail: Spring Boot 邮件启动器,用于发送邮件。

  • spring-boot-starter-data-redis: Spring Boot Redis 启动器,用于集成 Redis。

  • mybatis-spring-boot-starter: MyBatis Spring Boot 启动器,用于集成 MyBatis。

  • mysql-connector-java: MySQL 数据库驱动。

  • logback-classic 和 logback-core: Logback 日志框架。

  • aspectjweaver: AspectJ 切面编程框架。

  • okhttp: OkHttp HTTP 客户端。

  • fastjson: Fastjson JSON 处理库。

  • commons-lang3, commons-codec, commons-io: Apache Commons 工具库。

  • <exclusion>排除依赖避免冲突

  • (5)构建

  • <build> 部分定义了项目的构建配置

  • <plugins>: 包含所有使用的插件的列表。

  • <executions>: 定义插件的执行配置。

    • <execution>: 定义一个具体的执行配置。

      • <goals>: 指定要执行的插件目标。

        • <goal>repackage</goal>: 这是最重要的目标。它会在 package 阶段之后执行,将项目打包成一个可执行的 JAR 或 WAR 文件。 它会创建一个新的可执行 JAR/WAR,并将原始的 JAR/WAR 包含在其中。 这样,你就可以直接运行 java -jar your-app.jar 来启动 Spring Boot 应用程序。
    • ==repackage goal 的主要作用是创建一个可执行的 JAR/WAR 文件。 Spring Boot 应用程序依赖于嵌入式 Web 服务器(例如 Tomcat、Jetty 或 Undertow),repackage goal 会将这些依赖和应用程序代码打包在一起,并配置好类加载器,使应用程序可以直接运行==。

2.属性注入到bean对象的方式 #

  • 1.基于构造函数的注入这是最推荐的注入方式,因为它保证了 Bean 在创建时就拥有了所有必需的依赖项,并且可以强制执行不变性。

    @Component
    public class MyBean {
    private final String name;
    private final int value;
    @Autowired // Spring 4.3+ 可以省略 @Autowired 注解
    public MyBean(String name, @Value("${my.value}") int value) {
    this.name = name;
    this.value = value;
    }
    }

    2. 基于 Setter 方法的注入:

    这种方式比较灵活,允许在 Bean 创建后修改依赖项。但是,它不能保证 Bean 在使用前已经初始化了所有必需的依赖项。

    @Component
    public class MyBean {
    private String name;
    private int value;
    @Autowired
    public void setName(String name) {
    this.name = name;
    }
    @Autowired
    @Value("${my.value}")
    public void setValue(int value) {
    this.value = value;
    }
    }

    ==3. 使用 @Value 注解注入属性值:==

    @Value 注解可以将属性值、表达式结果、系统属性、环境变量等注入到 Bean 的字段或方法参数中。

    • 注入 application.properties 中的属性:

      @Value("${my.property}") private String myProperty;
    • 注入系统属性:

      @Value("#{systemProperties['user.home']}") private String userHome;
    • 注入表达式结果:

      @Value("#{2 * T(java.lang.Math).PI}") private double circumference;
    • 设置默认值:

      @Value("${my.property:defaultValue}") private String myProperty;

    4. 使用 @ConfigurationProperties 注解注入属性值:

    @ConfigurationProperties 注解可以将一组相关的属性绑定到一个 Java Bean 中。这对于管理复杂的配置非常有用。

    @Component
    @ConfigurationProperties(prefix = "my.config")
    public class MyConfig {
    private String property1;
    private int property2;
    // ... getters and setters
    }

    在 application.properties 中:

    my.config.property1=value1
    my.config.property2=123

    ==通常像数据库这些配置信息一般在spring boot框架启动后自动加载配置文件注入到应用程序中==

  • 5. 注入其他 Bean:

    ==使用 @Autowired 或 @Resource 注解将其他 Bean 注入到当前 Bean 中==。

    @Component
    public class MyBean {
    @Autowired
    private OtherBean otherBean;
    }

3.正则表达式#

/**
* /d 匹配数字0-9 java中对于字符需要一次转义
* &^ 以所匹配的结尾或开头
* \w匹配数字、26个英文字母或者下划线
* *前面出现0/多次 + 前面1/多次
* (?=.*\d) 先行断言,先行检查是否至少存在一个数字,.* 表示匹配任意字符(除了换行符)零次或多次
* {8,18}前面集合出现8-18次
* [] 其中至少出现一次
* ?: 表示前面的组是可选的 可出现或不出现
*/
常用正则表达式
NO("","不校验"),
IP("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}", "IP地址"),
POSITIVE_INTEGER("^[0-9]*[1-9][0-9]*$", "正整数"),
NUMBER_LETTER_UNDER_LINE("^\\w+$", "由数字、26个英文字母或者下划线组成的字符串"),
PHONE("(1[0-9])\\d{9}$", "手机号码"),
COMMON("^[a-zA-Z0-9_\\u4e00-\\u9fa5]+$", "数字,字母,中文,下划线"),
PASSWORD("^(?=.*\\d)(?=.*[a-zA-Z])[\\da-zA-Z~!@#$%^&*_]{8,18}$", "只能是数字,字母,特殊字符 8-18位"),
ACCOUNT("^[a-zA-Z][0-9a-zA-Z_]*$", "字母开头,由数字、英文字母或者下划线组成"),
MONEY("^[0-9]+(.[0-9]{1,2})?$", "金额");

4.AOP切面(Aspect)#

一般我们通过==注解(Annotation)==可以用来定义==切面的切入点(Pointcut)切入点可以在使用时赋值==例如校验登录,校验参数,而==切面去实现切入点的具体逻辑==。

@Target({ElementType.METHOD,ElementType.TYPE}) //该注解可以用于方法或者类
@Retention(RetentionPolicy.RUNTIME) //该注解在运行时保留,可以在运行时通过反射访问
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckLogin { }
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckParams { }
// 定义切面
@Aspect
@Component
public class SecurityAspect {
// 登录校验切入点
@Around("@annotation(CheckLogin)")
public Object checkLogin(ProceedingJoinPoint joinPoint) throws Throwable {
// ... 登录校验逻辑 ...
return joinPoint.proceed();
}
// 参数校验切入点
@Before("@annotation(CheckParams)") //@Before是在切入点之前执行
public void checkParams(JoinPoint joinPoint) {
// ... 参数校验逻辑 ...
}
}
// 使用注解
@Service
public class MyService {
@CheckLogin
public void loginRequiredMethod() {
// ... 需要登录的方法逻辑 ...
}
@CheckParams
public void validateParamsMethod(String param1, int param2) {
// ... 需要参数校验的方法逻辑 ...
}
}

2024/11/16#

1.Java Servlet#

  • 1. Servlet 接口:

    这是所有 Servlet 的基础接口。它定义了 Servlet 的生命周期方法,例如 init()、service() 和 destroy()。Spring Boot 中的控制器通常间接实现了这个接口,由 Spring MVC 框架进行适配。

    • init(ServletConfig config): 在 Servlet 初始化时调用,用于设置 Servlet 的配置信息。
    • service(ServletRequest req, ServletResponse res): 处理客户端请求的核心方法,根据请求类型(GET、POST 等)调用相应的方法。
    • destroy(): 在 Servlet 销毁时调用,用于释放资源。

    2. ==ServletRequest== 接口:

    表示客户端的请求。它提供了访问请求参数、请求头、请求 URL 等信息的方法。Spring MVC 将其封装为 HttpServletRequest 对象。

    • getParameter(String name): 获取指定名称的请求参数。
    • getParameterValues(String name): 获取指定名称的多个请求参数值。
    • getHeader(String name): 获取指定名称的请求头。
    • getRequestURL(): 获取请求的 URL。
    • getSession(): 获取与请求关联的 HttpSession 对象。

    3. ==ServletResponse== 接口:

    表示服务器对客户端的响应。它提供了设置响应头、响应状态码以及向客户端输出数据的方法。Spring MVC 将其封装为 HttpServletResponse 对象。

    • setContentType(String type): 设置响应的内容类型。
    • setStatus(int sc): 设置响应的状态码。
    • getWriter(): 获取 PrintWriter 对象,用于向客户端输出文本数据。
    • getOutputStream(): 获取 ServletOutputStream 对象,用于向客户端输出二进制数据。

    4. ==HttpServletRequest 接口== (继承自 ServletRequest):

    专用于 HTTP 协议的请求,提供了访问 HTTP 特定信息的方法,例如请求方法、请求 URI 等。

    • getMethod(): 获取 HTTP 请求方法(GET、POST 等)。
    • getRequestURI(): 获取请求的 URI。
    • getContextPath(): 获取 Web 应用程序的上下文路径。
    • getCookies(): 获取请求中的 Cookie。
      设置 HTTP 请求的 Content-Type: 当客户端发送 JSON 数据到服务器时,也需要设置 Content-Type 头部,告诉服务器发送的数据类型。
      request.setHeader(CONTENT_TYPE, CONTENT_TYPE_VALUE);

    5. ==HttpServletResponse== 接口 (继承自 ServletResponse):

    专用于 HTTP 协议的响应,提供了设置 HTTP 特定响应头的方法,例如重定向、设置 Cookie 等。

    • sendRedirect(String location): 重定向到指定的 URL。
    • addCookie(Cookie cookie): 添加 Cookie 到响应。
    • setHeader(String name, String value): 设置响应头。
      **设置 HTTP 响应的 Content-Type:** 当服务器返回 JSON 数据给客户端时,需要设置 Content-Type 头部为 "application/json;charset=UTF-8",以便客户端能够正确解析数据。
      response.setHeader(CONTENT_TYPE, CONTENT_TYPE_VALUE);

    6. ==HttpSession== 接口:

    表示客户端的会话。它用于在多个请求之间存储用户特定的数据。

    • setAttribute(String name, Object value): 将数据存储到会话中。
    • getAttribute(String name): 从会话中获取数据。
    • invalidate(): 使会话失效。

    7. ==ServletContext== 接口:

    表示 Web 应用程序的上下文。它提供了访问 Web 应用程序的配置信息和资源的方法。

    • getInitParameter(String name): 获取 Web 应用程序的初始化参数。
    • getRealPath(String path): 获取指定路径的实际文件系统路径。
    • getAttribute(String name)
// RequestContextHolder: RequestContextHolder是 Spring 提供的一个工具类,它可以用来获取当前线程的请求信息。
// getRequestAttributes: getRequestAttributes() 方法返回一个 RequestAttributes 对象,它包含了当前请求的相关信息,包括 HttpServletRequest 对象。
// ServletRequestAttributes: RequestContextHolder.getRequestAttributes() 返回的 RequestAttributes 对象是一个接口,它无法直接访问 HttpServletRequest 对象。
// 我们需要将其强制转换为 ServletRequestAttributes 类型,才能获取 HttpServletRequest 对象。
//
// ServletRequestAttributes 是 Spring 提供的一个实现类,它扩展了 RequestAttributes 接口,并提供了获取 HttpServletRequest 对象的方法。
// ServletRequestAttributes 类中的 getRequest() 方法用于获取与当前请求相关的 HttpServletRequest 对象。
// HttpServletRequest 对象中有一个 getSession() 方法,可以获取与该请求相关的 HttpSession 对象。
// session.getAttribute(Constants.SESSION_KEY): 从 HttpSession 对象中获取名为 Constants.SESSION_KEY 的属性值。
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 获取 HttpServletRequest 对象
HttpSession session = request.getSession();
SessionWebUserDto sessionUser = (SessionWebUserDto) session.getAttribute(Constants.SESSION_KEY);

现如今springboot已经极大的简化了Java servlet的开发 例如

  • 1. 内嵌 Servlet 容器:

    Spring Boot 内嵌了 Tomcat、Jetty 或 Undertow 等 Servlet 容器,无需手动部署 WAR 文件。 你只需运行 Spring Boot 应用程序,它就会自动启动内嵌的 Servlet 容器,并将你的应用程序部署到其中。 这大大简化了部署过程,也方便了开发和测试。

    2. 自动配置:

    Spring Boot 的自动配置机制会根据你项目中的依赖自动配置 Servlet 容器以及其他相关的组件,例如 Spring MVC。 你无需编写繁琐的 XML 配置文件或 Java 配置类来配置 Servlet 容器、监听器、过滤器等。 Spring Boot 会根据你的需要自动完成这些配置。

    3. Starter 依赖:

    Spring Boot Starter 是一组方便的依赖描述符,可以帮助你快速添加项目所需的依赖。 例如,spring-boot-starter-web starter 依赖包含了 Spring MVC 和内嵌 Servlet 容器所需的全部依赖。 你只需添加一个 starter 依赖,即可开始 Web 开发。

    4. 简化的 Servlet API 使用:

    Spring Boot 提供了更高级的抽象,例如 Spring MVC,来简化 Servlet API 的使用。 你可以使用注解(例如 @Controller、@RequestMapping 等)来定义控制器和处理请求,而无需直接实现 Servlet 接口或处理 HttpServletRequest 和 HttpServletResponse 对象。

    5. 简化的部署:

    Spring Boot 应用程序可以被打包成可执行的 JAR 文件,可以直接运行。 你无需将应用程序部署到外部 Servlet 容器中。 这简化了部署过程,也使得应用程序更易于移植。

==但完全隐藏 Servlet API 并不现实,某些情况下,开发者需要直接访问 Servlet API 来实现特定的功能或进行底层控制。例如,操作 Cookie、设置缓存控制、处理文件下载等。==

2.java.lang.reflect(反射)#

==反射 (Reflection) 是指在程序运行时,能够获取自身类型信息的能力。==

包提供了用于获取和操作类、接口、字段、方法和构造函数等运行时类型信息的类和接口。它允许在运行时检查和修改程序的结构和行为,是实现动态代理、注解处理、依赖注入等高级特性的基础。

  • 核心类和接口:
    • Class<T>: 表示类的类型,可以通过它获取类的名称、修饰符、父类、接口、字段、方法、构造函数等信息。

      • ==getName(): 获取类的完全限定名。==
      • getSimpleName(): 获取类的简单名称。
      • getSuperclass(): 获取父类。
      • getInterfaces(): 获取实现的接口。
      • getModifiers(): 获取修饰符(public, private, static 等)。
      • ==getDeclaredFields(): 获取声明的字段。== (包括私有与保护)
      • getFields(): 获取所有公共字段(包括继承的)。
      • getDeclaredMethods(): 获取声明的方法。
      • ==getMethods(): 获取所有公共方法(包括继承的)。==
      • getDeclaredConstructors(): 获取声明的构造函数。
      • getConstructors(): 获取所有公共构造函数。
      • newInstance(): 创建新的实例(需要无参构造函数)。
      • ==.class 编译时知道类的存在==
      • ==forName(String className): 通过类名获取 Class 对象。运行时知道类的存在==
    • Field: 表示类的字段。 (即类的属性)

      • getName(): 获取字段名。
      • getType(): 获取字段类型。
      • getModifiers(): 获取修饰符。
      • ==setAccessible(true): 允许访问私有字段。==
      • get(Object obj): 获取字段的值。
      • set(Object obj, Object value): 设置字段的值。
    • Method: 表示类的方法。

      • getName(): 获取方法名。
      • getReturnType(): 获取返回类型。
      • ==getParameterTypes(): 获取参数类型数组。==
      • getModifiers(): 获取修饰符。
      • setAccessible(true): 允许访问私有方法。
      • invoke(Object obj, Object… args): 调用方法。
    • Constructor: 表示类的构造函数。

      • getParameterTypes(): 获取参数类型数组。
      • getModifiers(): 获取修饰符。
      • setAccessible(true): 允许访问私有构造函数。
      • newInstance(Object… args): 创建新的实例。
    • ==Parameter 类的常用方法:==

      • ==getName==(): 返回参数的名称。 需要注意的是,参数名称在编译后的字节码中默认是不保留的。要获取参数名称,需要在编译时使用 -parameters 选项。
      • ==getType==(): 返回参数的类型,以 Class 对象表示。
      • ==getParameterizedType==(): 返回参数的参数化类型。这对于泛型类型尤其有用,可以获取更详细的类型信息,例如 List<String> 的参数化类型是 String。
      • ==getAnnotatedType==(): 返回参数的带注解的类型,它包含了参数的类型信息以及参数上的注解。
      • getModifiers(): 返回参数的修饰符(例如 final)。 可以使用 java.lang.reflect.Modifier 类来解析这些修饰符。
      • isNamePresent(): 检查参数名称是否可用。 只有在编译时使用 -parameters 选项的情况下,参数名称才可用。
      • ==getAnnotations==(): 返回参数上的所有注解。
      • getAnnotation(Class<T> annotationClass): 返回参数上指定类型的注解。
      • getDeclaredAnnotations(): 返回直接在参数上声明的注解,不包括继承的注解。
      • isVarArgs(): 检查参数是否为可变参数(varargs)。

应用场景:

  • 框架开发 (例如 Spring): 用于实现依赖注入、AOP 等功能。
  • 测试工具 (例如 JUnit): 用于访问私有成员和模拟对象行为。
  • 代码生成工具: 用于生成代码。
  • ORM 框架 (例如 Hibernate): 用于将对象映射到数据库表。
  • 动态代理: 用于创建在运行时实现接口的代理对象。
import java.lang.reflect.*;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("java.lang.String"); // 声明一个名为 cls 的变量来存储 String 类的 Class 对象。? 表示通配符,可以代表任何类型。
System.out.println("Class Name: " + cls.getName()); //获取类的完全限定名(fully qualified name),对于 String 类,结果是 java.lang.String。
Method[] methods = cls.getMethods(); //获取 String 类及其父类中所有 public 方法,并返回一个 Method 对象数组。
for (Method method : methods) {
System.out.println("Method: " + method.getName());
}
Field[] fields = cls.getDeclaredFields(); //获取 String 类中声明的 所有 字段(
for (Field field : fields) {
System.out.println("Field: " + field.getName());
}
Constructor<?>[] constructors = cls.getConstructors(); //获取 String 类中所有 public 构造函数
for (Constructor<?> constructor : constructors) {
System.out.println("Constructor: " + constructor.getName());
}
// 创建String实例
String str = (String) cls.getConstructor(String.class).newInstance("Hello");
System.out.println(str);
}
}
import java.lang.reflect.*;
import java.util.List; // 导入 List 接口
public class ParameterExample {
// 定义一个方法,用于打印方法参数的信息
public static void printParameterInfo(Method method) {
// 获取方法的参数列表
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
// 打印参数名称
System.out.println("Parameter Name: " + parameter.getName());
// 打印参数类型
System.out.println("Parameter Type: " + parameter.getType());
// 打印参数化类型 (对于泛型参数很有用)
System.out.println("Parameterized Type: " + parameter.getParameterizedType());
// 打印参数是否为可变参数 (varargs)
System.out.println("Is VarArgs: " + parameter.isVarArgs());
// 打印参数上的注解
System.out.println("Annotations:");
//getAnnotations()内加上参数是获取此参数上该类型的注解
for (Annotation annotation : parameter.getAnnotations()) {
System.out.println(" " + annotation);
}
System.out.println("------");
}
}
// 示例方法,包含不同类型的参数和一个注解
public void exampleMethod(String name, int age, @Deprecated List<String> hobbies) {
}
public static void main(String[] args) throws NoSuchMethodException {
//ParameterExample.class: 获取 ParameterExample 类的 Class 对象。 Class 对象表示 Java 中的类,包含类的元数据信息。
Method method = ParameterExample.class.getMethod("exampleMethod", String.class, int.class, List.class);
printParameterInfo(method);
}
}
输出
Parameter Name: arg0
Parameter Type: class java.lang.String
Parameterized Type: class java.lang.String
Is VarArgs: false
Annotations:
------
Parameter Name: arg1
Parameter Type: int
Parameterized Type: int
Is VarArgs: false
Annotations:
------
Parameter Name: arg2
Parameter Type: interface java.util.List
Parameterized Type: java.util.List<java.lang.String>
Is VarArgs: false
Annotations:
@java.lang.Deprecated()
------

2024//11/17#

1.String.valueOf(value) 与value.String区别#

String.valueOf(value) 和 value.toString() 的主要区别在于如何处理 null 值:

  • String.valueOf(value): ==如果 value 为 null,String.valueOf(value) 会返回字符串 “null”==。 它不会抛出 NullPointerException。
  • value.toString(): 如果 value 为 null,调用 value.toString() ==会抛出 NullPointerException==。

2.java.util.regex#

** Pattern 类:**

Pattern 类表示编译后的正则表达式。它是一个不可变的类,一旦创建就不能修改。

  • ==Pattern.compile(String regex):== 将指定的正则表达式字符串编译成 Pattern 对象。这是使用正则表达式的第一步。
  • ==Pattern.matches(String regex, CharSequence input):== 这是一个静态方法,用于快速判断一个字符串是否匹配指定的正则表达式。==等价于 Pattern.compile(regex).matcher(input).matches()==。 如果只需要进行一次匹配,可以使用这个方法,效率更高,==但若多次使用一个正则表达式去匹配推荐先编译正则表达式==。
  • pattern.matcher(CharSequence input): 创建一个 Matcher 对象,用于将 Pattern 对象与输入字符串进行匹配。
  • pattern.flags(): 返回编译时指定的标志,例如 Pattern.CASE_INSENSITIVE。

Matcher 类:

Matcher 类用于对字符串执行匹配操作。

  • ==matcher.matches():== 尝试将整个输入序列与模式匹配。
  • matcher.find(): 尝试查找输入序列中与模式匹配的下一个子序列。

3.boolean 和 Boolean#

  • boolean 是 Java 的基本数据类型 (primitive type),而 Boolean 是 boolean 的包装类 (wrapper class)。
  1. 内存使用: boolean 直接存储布尔值(true 或 false),占用更少的内存。Boolean 则是一个对象,存储的是对象的引用,占用更多的内存。
  2. 默认值: boolean 的默认值为 false。Boolean 的默认值为 null。
  3. 性能: boolean 的操作通常更高效,因为它不需要进行对象的创建和销毁。Boolean 的操作涉及到对象的装箱和拆箱,会有一定的性能开销,但在现代 JVM 中,这个开销已经很小了。
  4. 空值: ==boolean 不能为 null==,而 ==Boolean 可以为 null==。 这使得 Boolean 更适合表示可能不存在的值,例如数据库中的一个可空字段。
  5. 方法: Boolean 作为包装类,提供了一些有用的方法,例如 parseBoolean()、toString()、equals() 等,而 boolean 没有这些方法。
  6. 泛型: ==Boolean 可以用于泛型类型==,而 boolean 不可以。 例如,你可以声明一个 List<Boolean>,但不能声明一个 List<boolean>。

2024/11/20#

1.java集合#

Collection 这是所有集合的根接口,定义了集合的基本操作,如添加、删除、检查元素是否存在等。 主要方法包括:add, remove, contains, size, isEmpty, iterator 等。

1. List 接口及其主要实现类

  • 特点: 有序集合,允许重复元素,可以通过索引访问元素。

  • 常用实现类:

    • ArrayList:基于动态数组实现,随机访问速度快,插入和删除操作相对较慢,线程不安全。
      • ==add(E element): 在列表末尾添加元素。==
      • add(int index, E element): 在指定索引位置插入元素。
      • ==get(int index): 获取指定索引位置的元素。==
      • ==set(int index, E element): 修改指定索引位置的元素。==
      • ==remove(int index): 删除指定索引位置的元素。==
      • ==remove(Object o): 删除指定的元素。==
      • ==size(): 返回列表中元素的数量。==
      • ==isEmpty(): 判断列表是否为空。==
      • ==contains(Object o): 判断列表是否包含指定的元素。==
      • clear(): 清空列表。
      • iterator(): 返回一个迭代器,用于遍历列表中的元素。
    • LinkedList:基于双向链表实现,插入和删除操作速度快,随机访问速度慢,线程不安全。
      • addFirst(E e): 在列表开头添加元素。
      • addLast(E e): 在列表末尾添加元素 (等同于 add(e))。
      • getFirst(): 获取列表的第一个元素。
      • getLast(): 获取列表的最后一个元素。
      • removeFirst(): 删除并返回列表的第一个元素。
      • removeLast(): 删除并返回列表的最后一个元素。
      • offerFirst(E e)/offerLast(E e): 添加元素到列表开头/末尾,如果操作成功返回 true,否则返回 false。
      • peekFirst()/peekLast(): 获取但不删除列表的第一个/最后一个元素,如果列表为空,则返回 null。
      • pollFirst()/pollLast(): 获取并删除列表的第一个/最后一个元素,如果列表为空,则返回 null
    • Vector:与 ArrayList 类似,但线程安全,性能略低于 ArrayList。
  • 适用场景: 存储需要保持顺序的数据,例如用户列表、商品列表等。

2. Set 接口及其主要实现类

  • 特点: 无序集合,不允许重复元素。

  • 常用实现类:

    • HashSet:基于哈希表实现,元素的添加和查找速度快,不保证元素的顺序,线程不安全。
    • TreeSet:基于红黑树实现,元素自动排序,线程不安全。
      • ==add(E element): 添加元素到集合中。==
      • ==remove(Object o): 从集合中删除指定的元素。==
      • ==contains(Object o): 判断集合中是否包含指定的元素。==
      • ==size(): 返回集合中元素的数量。==
      • ==first(): 返回集合中的第一个元素(最小的元素)。==
      • ==last(): 返回集合中的最后一个元素(最大的元素)。==
      • lower(E e): 返回小于给定元素的最大元素。
      • higher(E e): 返回大于给定元素的最小元素。
      • floor(E e): 返回小于等于给定元素的最大元素。
      • ceiling(E e): 返回大于等于给定元素的最小元素。
  • 适用场景: 去除重复元素,例如统计用户访问的页面、存储不重复的关键词等。

3. Queue 接口及其主要实现类

  • 特点: 队列,遵循 FIFO(先进先出)原则。

  • 常用实现类:

    • LinkedList:实现了 Deque 接口,可以用作双端队列,线程不安全。
    • PriorityQueue:优先队列,元素根据优先级排序,线程不安全。
    • ArrayBlockingQueue:有界阻塞队列,基于数组实现,线程安全。
    • LinkedBlockingQueue:可选有界阻塞队列,基于链表实现,线程安全。
    • ConcurrentLinkedQueue:无界非阻塞队列,基于链表实现,线程安全,适用于高并发场景。
  • 适用场景: 实现生产消费者模型、任务调度等。

4. Map 接口及其主要实现类

  • 特点: 键值对集合,每个键映射到一个值,键不能重复,值可以重复。

  • 常用实现类:

    • HashMap:基于哈希表实现,键值对的添加和查找速度快,不保证元素的顺序,线程不安全。
      • ==put(K key, V value): 将指定的键值对添加到 Map 中。==
      • ==get(Object key): 获取指定键对应的值。==
      • ==remove(Object key): 删除指定键对应的键值对。==
      • ==containsKey(Object key): 判断 Map 中是否包含指定的键。==
      • ==containsValue(Object value): 判断 Map 中是否包含指定的值。==
      • ==size(): 返回 Map 中键值对的数量。==
      • ==isEmpty(): 判断 Map 是否为空。==
      • clear(): 清空 Map。
      • keySet(): 返回 Map 中所有键的 Set 视图。
      • values(): 返回 Map 中所有值的 Collection 视图。
      • entrySet(): 返回 Map 中所有键值对的 Set 视图。
    • TreeMap:基于红黑树实现,键值对按照键排序,线程不安全。
      • put(K key, V value): 将指定的键值对添加到 Map 中。
      • get(Object key): 获取指定键对应的值。
      • remove(Object key): 删除指定键对应的键值对。
      • firstKey(): 返回 Map 中最小的键。
      • lastKey(): 返回 Map 中最大的键。
      • lowerKey(K key): 返回小于给定键的最大键。
      • higherKey(K key): 返回大于给定键的最小键。
      • floorKey(K key): 返回小于等于给定键的最大键。
      • ceilingKey(K key): 返回大于等于给定键的最小键。
    • LinkedHashMap:继承自 HashMap,使用链表维护键值对的插入顺序,线程不安全。
    • ConcurrentHashMap:线程安全的 HashMap,性能优于 Hashtable,适用于高并发场景。
  • 适用场景: 存储键值对数据,例如用户信息、配置信息等。

5. Deque 接口及其主要实现类

  • 特点: 双端队列,可以在队列的两端进行插入和删除操作。

  • 常用实现类:

    • LinkedList:实现了 Deque 接口,可以用作双端队列,线程不安全。
    • ArrayDeque:基于数组实现的双端队列,线程不安全。
    • ConcurrentLinkedDeque:线程安全的双端队列,适用于高并发场景。
  • 适用场景: 实现栈、队列等数据结构。

2.异常处理#

java中异常类结构:

**Throwable:**Java 语言中所有错误或异常的超类

  • Error (错误): Error 表示严重的系统问题,通常是应用程序无法恢复的。例如,OutOfMemoryError、StackOverflowError 等。一般情况下,应用程序不应该尝试捕获 Error。

  • Exception (异常): Exception 表示程序运行过程中可能出现的异常情况,通常是可以被应用程序处理的。

    • Checked Exception (受检异常): 除了 RuntimeException 及其子类以外,其他的异常都属于受检异常。编译器会强制要求你处理这类异常,要么使用 try-catch 块捕获,要么在方法签名中使用 throws 关键字声明抛出。例如,==IOException、SQLException、ClassNotFoundException== 等。
    • Unchecked Exception (非受检异常/运行时异常): ==RuntimeException== 及其子类都属于非受检异常。编译器不会强制要求你处理这类异常。例如,NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException、ArithmeticException 等。
  • 自定义异常:自定义异常通常继承自 Exception 或 RuntimeException。

Throwable 的常用方法

  • ==getMessage(): 返回异常的描述信息。==
  • ==getCause(): 返回导致当前异常的原因,可以形成异常链。==
  • ==printStackTrace(): 打印异常的堆栈跟踪信息,堆栈跟踪显示了异常发生时程序的调用栈,包括文件名、方法名和行号。 这可以帮助开发者精确定位代码中出现错误的位置。==
  • fillInStackTrace(): 填充堆栈跟踪信息。在自定义异常时,可以通过重写该方法来避免填充堆栈信息,从而提高性能 (就像你在 BusinessException 中做的那样)。
  • toString(): 返回异常的字符串表示形式,通常包含异常的类名和描述信息。
package com.easypan.exception;
import com.easypan.entity.enums.ResponseCodeEnum;
// BusinessException 类,继承自 RuntimeException,用于表示业务异常
public class BusinessException extends RuntimeException {
// 私有属性,用于存储异常的枚举码
private ResponseCodeEnum codeEnum;
// 私有属性,用于存储异常的代码值
private Integer code;
// 私有属性,用于存储异常的错误信息
private String message;
// 构造方法,接收错误信息和异常对象,用于创建 BusinessException 对象
public BusinessException(String message, Throwable e) {
// 调用父类 RuntimeException 的构造方法,初始化错误信息和异常对象
super(message, e);
// 设置 BusinessException 的错误信息
this.message = message;
}
// 构造方法,接收错误信息,用于创建 BusinessException 对象
public BusinessException(String message) {
// 调用父类 RuntimeException 的构造方法,初始化错误信息
super(message);
// 设置 BusinessException 的错误信息
this.message = message;
}
// 构造方法,接收异常对象,用于创建 BusinessException 对象
public BusinessException(Throwable e) {
// 调用父类 RuntimeException 的构造方法,初始化异常对象
super(e);
}
// 构造方法,接收 ResponseCodeEnum 对象,用于创建 BusinessException 对象
public BusinessException(ResponseCodeEnum codeEnum) {
// 调用父类 RuntimeException 的构造方法,初始化错误信息
super(codeEnum.getMsg());
// 设置 BusinessException 的枚举码、代码值和错误信息
this.codeEnum = codeEnum;
this.code = codeEnum.getCode();
this.message = codeEnum.getMsg();
}
// 构造方法,接收代码值和错误信息,用于创建 BusinessException 对象
public BusinessException(Integer code, String message) {
// 调用父类 RuntimeException 的构造方法,初始化错误信息
super(message);
// 设置 BusinessException 的代码值和错误信息
this.code = code;
this.message = message;
}
// 获取异常的枚举码
public ResponseCodeEnum getCodeEnum() {
return codeEnum;
}
// 获取异常的代码值
public Integer getCode() {
return code;
}
// 重写 getMessage 方法,返回异常的错误信息
@Override
public String getMessage() {
return message;
}
// 重写 fillInStackTrace 方法,用于避免填充堆栈信息,提高效率
// 业务异常一般不需要堆栈信息,因为堆栈信息可能包含敏感信息,而且会影响性能
@Override
public Throwable fillInStackTrace() {
return this;
}
}

3.日志记录#

Spring Boot 推荐的日志记录方式。

  • SLF4j (Simple Logging Facade for Java): 这是一个日志门面,它本身不提供具体的日志实现,而是允许你在部署时选择不同的日志库,例如 Logback、Log4j2、Java Util Logging 等。
  • Logback: 一个功能强大的日志实现库,通常作为 SLF4j 的默认实现。

SLF4j 和 Logback

private static Logger logger = LoggerFactory.getLogger(?.class);

配置 Logback:

Spring Boot 默认使用 logback-spring.xml 或 logback.xml 配置文件。

你可以在 src/main/resources 目录下创建这些文件来配置日志输出格式、级别、目标等。

记录日志:

  • logger.trace(“跟踪信息”);
  • logger.debug(“调试信息”);
  • logger.info(“普通信息”);
  • logger.warn(“警告信息”);
  • logger.error(“错误信息”, exception); // 记录异常信息,包含堆栈跟踪

2024/11/25#

1.Java String#

1. 基本操作:

  • ==length()====: 返回字符串的长度(字符数)。==
  • charAt(int index): 返回指定索引位置的字符。
  • indexOf(String str): 返回指定子字符串第一次出现的索引,如果找不到则返回 -1。
  • lastIndexOf(String str): 返回指定子字符串最后一次出现的索引,如果找不到则返回 -1。
  • ==substring(int beginIndex)====: 返回从指定索引开始到字符串末尾的子字符串。==
  • ==substring(int beginIndex, int endIndex)====: 返回从指定起始索引到结束索引(不包含)的子字符串。==
  • concat(String str): 将指定字符串连接到当前字符串的末尾。 等价于 + 操作符。
  • toCharArray(): 将字符串转换为字符数组。

2. 比较:

  • ==equals(Object anObject)====: 比较字符串是否相等(区分大小写)。==
  • ==equalsIgnoreCase(String anotherString)====: 比较字符串是否相等(忽略大小写)。==
  • compareTo(String anotherString): 按字典顺序比较两个字符串。
  • compareToIgnoreCase(String str): 按字典顺序比较两个字符串(忽略大小写)。
  • ==startsWith(String prefix)====: 检查字符串是否以指定前缀开头。==
  • ==endsWith(String suffix)====: 检查字符串是否以指定后缀结尾。==
  • ==contentEquals(StringBuffer sb)====: 将字符串与指定的 StringBuffer 比较。==

3. 查找和替换:

  • ==contains(CharSequence s)====: 检查字符串是否包含指定的字符序列。==
  • ==replace(char oldChar, char newChar)====: 将字符串中所有出现的旧字符替换为新字符。==
  • ==replaceAll(String regex, String replacement)====: 使用正则表达式替换所有匹配的子字符串。==
  • replaceFirst(String regex, String replacement): 使用正则表达式替换第一个匹配的子字符串。

4. 大小写转换:

  • toLowerCase(): 将字符串转换为小写。
  • toUpperCase(): 将字符串转换为大写。

5. 空白处理:

  • ==trim()====: 去除字符串开头和结尾的空白字符。==
  • isBlank() (Java 11+): 检查字符串是否为空白或只包含空白字符.
  • ==isEmpty()====: 检查字符串是否为空。==

6. 格式化:

  • ==format(String format, Object… args)====: 使用指定的格式字符串和参数创建格式化字符串。==
  • ==valueOf(Object obj)====: 将各种类型的值转换为字符串。==
    String name = "Alice"; int age = 30; double price = 19.99;
    String formattedString = String.format("My name is %s, I am %d years old, and the price is $%.2f", name, age,price); System.out.println(formattedString); // 输出: My name is Alice, I am 30 years old, and the price is $19.99

7. 分割和连接:

  • ==split(String regex)====: 使用正则表达式分割字符串。==
  • join(CharSequence delimiter, CharSequence… elements) (Java 8+): 使用指定的分隔符连接多个字符串。

8. 字符串构建器:

虽然不是 String 类的方法,但 StringBuilder 和 StringBuffer 类对于高效地构建和修改字符串非常重要,尤其是在循环中操作字符串时,应该使用 StringBuilder 或 StringBuffer,而不是直接使用 String 的 + 或 concat() 方法。

  • 可变性: StringBuilder 对象的内容是可以修改的,这意味着你可以追加、插入、删除或替换字符,而无需创建新的字符串对象。 与之相反,String 对象是不可变的,每次修改都会创建一个新的 String 对象,这在频繁修改字符串时会造成性能损耗。
  • 线程不安全: StringBuilder 不是线程安全的,如果需要在多线程环境下使用,应该使用 StringBuffer,它是线程安全的版本。 在单线程环境下,StringBuilder 的性能通常优于 StringBuffer。
  • 初始容量: StringBuilder 内部维护一个字符数组来存储字符串内容。 创建 StringBuilder 对象时可以指定初始容量,如果预计字符串会很长,预先分配足够的容量可以避免频繁的数组扩容,从而提高性能。

常用方法:

  • StringBuilder(): 创建一个空的 StringBuilder 对象。
  • StringBuilder(int capacity): 创建一个指定初始容量的 StringBuilder 对象。
  • StringBuilder(String str): 创建一个包含指定字符串的 StringBuilder 对象。
  • append(Object obj): 将任意对象的字符串表示形式追加到 StringBuilder 的末尾。
  • append(String str): 将指定的字符串追加到 StringBuilder 的末尾。
  • append(char c): 将指定的字符追加到 StringBuilder 的末尾。
  • insert(int offset, String str): 在指定位置插入字符串。
  • delete(int start, int end): 删除指定范围内的字符。
  • deleteCharAt(int index): 删除指定位置的字符。
  • replace(int start, int end, String str): 替换指定范围内的字符。
  • reverse(): 反转字符串。
  • toString(): 将 StringBuilder 对象转换为 String 对象。
  • length(): 返回当前 StringBuilder 对象的长度。
  • capacity(): 返回当前 StringBuilder 对象的容量。
  • setLength(int newLength): 设置 StringBuilder 对象的长度。 如果新长度小于当前长度,则截断字符串;如果新长度大于当前长度,则用空字符填充。

2.工具包(org.apache.commons.lang3)#

  • 1. 字符串工具类 StringUtils

    • isBlank(String str): 检查字符串是否为空白(null、空字符串或只包含空格)。比 isEmpty() 更常用,因为它考虑了空格的情况。
    • isEmpty(String str): 检查字符串是否为空(null 或空字符串)。
    • trim(String str): 去除字符串两端的空格。
    • substring(String str, int start): 截取字符串。
    • join(Object[] array, String separator): 将数组元素用指定分隔符连接成字符串。
    • split(String str, String separator): 用指定分隔符分割字符串。
    • replace(String text, String searchString, String replacement): 替换字符串。
    • equalsIgnoreCase(String str1, String str2): 忽略大小写比较两个字符串。
    • startsWith(String str, String prefix): 检查字符串是否以指定前缀开头。
    • endsWith(String str, String suffix): 检查字符串是否以指定后缀结尾。
    • contains(String str, String searchStr): 检查字符串是否包含指定子串。

    2. ==数字工具类 NumberUtils==

    • toInt(String str, int defaultValue): 将字符串转换为 int,如果转换失败则返回默认值。
    • toLong(String str, long defaultValue): 将字符串转换为 long,如果转换失败则返回默认值。
    • toDouble(String str, double defaultValue): 将字符串转换为 double,如果转换失败则返回默认值。
    • isDigits(String str): 检查字符串是否只包含数字。
    • isNumber(String str): 检查字符串是否是一个有效的数字(包括整数、小数、科学计数法等)。
    • min(int[] array)/max(int[] array): 查找数组中的最小/最大值。 类似方法也适用于 long, double, float 等。

    3. ==数组工具类 ArrayUtils==

    • isEmpty(Object[] array): 检查数组是否为空。
    • isNotEmpty(Object[] array): 检查数组是否非空。
    • ==contains(Object[] array, Object objectToFind): 检查数组是否包含指定元素。 ==ArrayUtils.contains(appConfig.getAdminEmails().split(”,”), email)
    • add(Object[] array, Object element): 向数组中添加元素,返回一个新的数组。
    • remove(Object[] array, int index): 从数组中移除指定索引的元素,返回一个新的数组。
    • clone(Object[] array): 克隆数组。

    4. 集合工具类 CollectionUtils

    • isEmpty(Collection<?> collection): 检查集合是否为空。
    • isNotEmpty(Collection<?> collection): 检查集合是否非空。
    • containsAny(Collection<?> coll1, Collection<?> coll2): 检查 coll1 是否包含 coll2 中的任何元素。
    • intersection(Collection<?> coll1, Collection<?> coll2): 返回 coll1 和 coll2 的交集。
    • union(Collection<?> coll1, Collection<?> coll2): 返回 coll1 和 coll2 的并集。

    5. 日期工具类 DateUtils

    • parseDate(String str, String… parsePatterns): 将字符串解析成日期对象。
    • format(Date date, String pattern): 将日期对象格式化成字符串。
    • addDays(Date date, int amount): 在日期上添加指定天数。 类似方法还有 addYears, addMonths, addWeeks, addHours, addMinutes, addSeconds, addMilliseconds.

    6.RandomStringUtils 生成随机字符串

  • ==random(int count, boolean letters, boolean numbers) 生成字符串长度,是否生成字母,是否生成数字==

  • 在SpringBoot中引入该工具包

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.x</version>
</dependency>

3.java.time#

  • ==LocalDate====: 表示日期,例如 2024-03-15,不包含时间和时区信息。==
  • ==LocalTime====: 表示时间,例如 10:30:45,不包含日期和时区信息。==
  • ==LocalDateTime====: 表示日期和时间,例如 2024-03-15T10:30:45,不包含时区信息。==
  • ZonedDateTime: 表示日期、时间和时区,例如 2024-03-15T10:30:45+08:00[Asia/Shanghai]。
  • Instant: 表示时间戳,以 Unix 元年(1970-01-01T00:00:00Z)开始的秒数和纳秒数来表示时间。
  • Duration: 表示两个时间点之间的时间差,以秒和纳秒为单位。
  • Period: 表示两个日期之间的时间差,以年、月、日为单位。
  • ==DateTimeFormatter====: 用于格式化和解析日期时间字符串。==
  • ZoneId & ZoneOffset: 表示时区。ZoneOffset 用于表示固定的时区偏移量,例如 +08:00;ZoneId 用于表示更复杂的时区规则,例如 “Asia/Shanghai”。
import java.time.*;
import java.util.Date;
public class DateTimeExamples {
public static void main(String[] args) {
// 2.1 获取当前时间
LocalDate currentDate = LocalDate.now(); // 获取当前日期
System.out.println("Current Date: " + currentDate);
LocalTime currentTime = LocalTime.now(); // 获取当前时间
System.out.println("Current Time: " + currentTime);
LocalDateTime currentDateTime = LocalDateTime.now(); // 获取当前日期时间
System.out.println("Current DateTime: " + currentDateTime);
ZonedDateTime currentZonedDateTime = ZonedDateTime.now(); // 获取当前日期时间,包含时区信息
System.out.println("Current ZonedDateTime: " + currentZonedDateTime);
Instant currentInstant = Instant.now(); // 获取当前时间戳,以UTC时间表示
System.out.println("Current Instant: " + currentInstant);
// 2.2 创建指定时间
LocalDate date = LocalDate.of(2024, 3, 15); // 创建指定日期
System.out.println("Specific Date: " + date);
LocalTime time = LocalTime.of(10, 30, 45); // 创建指定时间
System.out.println("Specific Time: " + time);
LocalDateTime dateTime = LocalDateTime.of(2024, 3, 15, 10, 30, 45); // 创建指定日期时间
System.out.println("Specific DateTime: " + dateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 3, 15, 10, 30, 45, 0, ZoneId.of("Asia/Shanghai")); // 创建指定日期时间,包含时区信息
System.out.println("Specific ZonedDateTime: " + zonedDateTime);
Instant instant = Instant.parse("2024-03-15T02:30:45Z"); // 从字符串解析时间戳,注意"Z"表示UTC时间
System.out.println("Specific Instant: " + instant);
// 2.3 格式化和解析
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 自定义格式
String formattedDateTime = currentDateTime.format(formatter); // 将日期时间格式化为字符串
System.out.println("Formatted DateTime: " + formattedDateTime);
LocalDateTime parsedDateTime = LocalDateTime.parse("2024-03-15 10:30:45", formatter); // 将字符串转换为日期/时间对象
System.out.println("Parsed DateTime: " + parsedDateTime);
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_DATE_TIME; // ISO 8601 标准格式
String isoFormatted = currentDateTime.format(isoFormatter); // 使用
System.out.println("ISO Formatted DateTime: " + isoFormatted);
// 2.4 日期时间计算
LocalDateTime nextWeek = currentDateTime.plusWeeks(1); // 加一周
System.out.println("Next Week: " + nextWeek);
LocalDate yesterday = currentDate.minusDays(1); // 减一天
System.out.println("Yesterday: " + yesterday);
Duration duration = Duration.between(instant, Instant.now()); // 计算两个时间点之间的时间差(秒和纳秒)
System.out.println("Duration: " + duration.getSeconds() + " seconds");
Period period = Period.between(LocalDate.of(2023, 1, 1), LocalDate.now()); // 计算两个日期之间的时间差(年、月、日)
System.out.println("Period: " + period.getYears() + " years, " + period.getMonths() + " months, " + period.getDays() + " days");
long daysBetween = ChronoUnit.DAYS.between(LocalDate.of(2023, 1, 1), LocalDate.now()); // 计算两个日期之间的天数差
System.out.println("Days Between: " + daysBetween);
// 2.5 时区转换
ZonedDateTime zonedDateTimeTokyo = currentDateTime.atZone(ZoneId.of("Asia/Tokyo")); // 将 LocalDateTime 转换为 ZonedDateTime,指定时区
System.out.println("Tokyo ZonedDateTime: " + zonedDateTimeTokyo);
LocalDateTime localDateTimeFromTokyo = zonedDateTimeTokyo.toLocalDateTime(); // 将 ZonedDateTime 转换为 LocalDateTime,丢失时区信息
System.out.println("LocalDateTime from Tokyo ZonedDateTime: " + localDateTimeFromTokyo);
Instant utcInstant = zonedDateTimeTokyo.toInstant(); // 获取 UTC 时间戳
System.out.println("UTC Instant from Tokyo ZonedDateTime: " + utcInstant);
// 3. 与旧版 java.util.Date 的转换
// java.time -> java.util.Date
Instant instantToConvert = currentDateTime.toInstant(ZoneOffset.UTC); // 将 LocalDateTime 转换为 Instant,指定UTC偏移量
Date dateFromInstant = Date.from(instantToConvert); // 将 Instant 转换为 Date
System.out.println("Date from Instant: " + dateFromInstant);
// java.util.Date -> java.time
Date oldDate = new Date(); // 旧版Date对象
Instant instantFromDate = oldDate.toInstant(); // 将 Date 转换为 Instant
LocalDateTime dateTimeFromOldDate = LocalDateTime.ofInstant(instantFromDate, ZoneId.systemDefault()); // 将 Instant 转换为 LocalDateTime,使用系统默认时区
System.out.println("LocalDateTime from Date: " + dateTimeFromOldDate);
}
}

java.util.date(传统会用)

  • Date(): 创建一个表示当前日期和时间的 Date 对象。
  • Date(long date): 创建一个表示指定毫秒数的 Date 对象。 毫秒数是从 1970 年 1 月 1 日 00:00:00 GMT 开始计算的。
  • getTime(): 返回 Date 对象表示的毫秒数。
  • setTime(long time): 设置 Date 对象表示的毫秒数。
  • before(Date when): 判断当前 Date 对象是否在指定日期之前。
  • after(Date when): 判断当前 Date 对象是否在指定日期之后。
  • equals(Object obj): 判断两个 Date 对象是否相等。
  • compareTo(Date anotherDate): 比较两个 Date 对象的时间先后顺序。
  • toString(): 将 Date 对象转换为字符串表示,格式取决于本地设置。 不推荐依赖此方法进行格式化,因为它依赖于本地设置,可能导致不一致的结果。

格式化注解

  • @JsonFormat: 主要用于控制序列化和反序列化日期时间的格式。它是由 Jackson 库提供的注解,通常用于将 Java 对象转换为 JSON 格式,或将 JSON 格式转换为 Java 对象。

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date registerTime; // 或 java.time 类型,例如 LocalDateTime
    • pattern: 指定日期时间的格式。
    • timezone: 指定时区。 如果不指定,则使用系统默认时区。 “GMT+8” 等同于 “Asia/Shanghai”。
  • @DateTimeFormat: 主要用于控制 Spring MVC 在绑定请求参数到 Java 对象时,日期时间的解析格式。 它是由 Spring 框架提供的注解。

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date registerTime; // 或 java.time 类型,例如 LocalDateTime
    • pattern: 指定日期时间的格式。 注意,这里不需要指定时区,Spring 会自动处理。

4.Java Arrays#

  • 1. Arrays.asList(T… a)

    • 功能: 将一个数组转换为一个 List 集合。

    • 语法: public static <T> List<T> asList(T… a)

    • 说明:

      • T… a 是一个可变参数,允许传入任意数量的相同类型的参数,会被自动封装成一个数组。
      • 返回的是 Arrays 类内部的一个静态内部类 ArrayList 的实例,注意不是 java.util.ArrayList
      • 这个内部类 ArrayList 没有实现 add 和 remove 方法,因此不能直接对返回的 List 进行添加或删除操作,否则会抛出 UnsupportedOperationException 异常。
      • 返回的 List 是 数组的视图,对 List 的修改会反映到原始数组上。
    • 示例:

    String[] arr = {"A", "B", "C"};
    List<String> list = Arrays.asList(arr);
    System.out.println(list); // 输出: [A, B, C]
    // 尝试添加元素会抛出异常
    // list.add("D"); // UnsupportedOperationException
    // 修改 List 会影响原数组
    list.set(0, "X");
    System.out.println(Arrays.toString(arr)); // 输出: [X, B, C]
    • 适用场景: 将数组转换为 List,方便使用 List 的方法进行遍历、查找等操作。 但需要注意返回的 List 的特殊性,避免进行修改操作。

    2. Arrays.stream(T[] array) / Arrays.stream(int[] array) / Arrays.stream(long[] array) / Arrays.stream(double[] array)

    • 功能: 将一个数组转换为一个 Stream 流。

    • 语法:

      • public static <T> Stream<T> stream(T[] array) (适用于对象数组)
      • public static IntStream stream(int[] array) (适用于 int 数组)
      • public static LongStream stream(long[] array) (适用于 long 数组)
      • public static DoubleStream stream(double[] array) (适用于 double 数组)
    • 说明:

      • 针对不同类型的数组,提供了不同的 stream 方法,返回对应的 Stream 类型。 IntStream、LongStream 和 DoubleStream 分别是针对基本类型 int、long 和 double 的特殊 Stream,提供了更高效的操作。
      • 可以将数组转换为 Stream 流,从而可以使用 Stream API 进行各种复杂的过滤、映射、排序、聚合等操作。
    • 示例:

    int[] numbers = {1, 2, 3, 4, 5};
    // 将 int 数组转换为 IntStream,然后进行过滤和求和操作
    int sum = Arrays.stream(numbers)
    .filter(n -> n % 2 == 0) // 过滤偶数
    .sum(); // 求和
    System.out.println("偶数之和: " + sum); // 输出: 偶数之和: 6
    String[] names = {"Alice", "Bob", "Charlie"};
    // 将 String 数组转换为 Stream<String>,然后进行映射和收集操作
    List<String> upperCaseNames = Arrays.stream(names)
    .map(String::toUpperCase) // 转换为大写
    .collect(Collectors.toList()); // 收集到 List
    System.out.println(upperCaseNames); // 输出: [ALICE, BOB, CHARLIE]
    • 适用场景: 需要对数组进行复杂的数据处理,例如过滤、映射、排序、聚合等操作时,转换为 Stream 流可以充分利用 Stream API 的强大功能。

    3. Arrays.sort(T[] a) / Arrays.sort(int[] a) / Arrays.sort(long[] a) / Arrays.sort(double[] a) 等重载方法

    • 功能: 对数组进行排序。

    • 语法: 提供了多种重载方法,可以对不同类型的数组进行排序,可以指定排序范围,也可以传入自定义的 Comparator 进行排序。

      • public static void sort(int[] a) (对整个 int 数组进行升序排序)
      • public static void sort(int[] a, int fromIndex, int toIndex) (对 int 数组指定范围进行升序排序)
      • public static <T> void sort(T[] a, Comparator<? super T> c) (对对象数组使用自定义 Comparator 进行排序)
    • 说明:

      • 对于基本类型数组,默认进行升序排序。
      • 对于对象数组,需要实现 Comparable 接口,或者传入自定义的 Comparator。
      • fromIndex(包含)和 toIndex(不包含)指定了排序的范围。
      • Arrays.sort() 使用了高效的排序算法(例如 Dual-Pivot Quicksort)。
    • 示例:

    int[] numbers = {5, 2, 8, 1, 9};
    Arrays.sort(numbers);
    System.out.println(Arrays.toString(numbers)); // 输出: [1, 2, 5, 8, 9]
    String[] names = {"Charlie", "Alice", "Bob"};
    Arrays.sort(names, (s1, s2) -> s1.compareToIgnoreCase(s2)); // 忽略大小写排序
    System.out.println(Arrays.toString(names)); // 输出: [Alice, Bob, Charlie]
    int[] subArray = {5, 2, 8, 1, 9};
    Arrays.sort(subArray, 1, 4); // 对索引 1 到 3 (不包括 4) 的元素进行排序
    System.out.println(Arrays.toString(subArray)); // 输出: [5, 1, 2, 8, 9]
    • 适用场景: 需要对数组进行排序时,可以使用 Arrays.sort() 方法。 可以使用默认的升序排序,也可以自定义排序规则。

    4. Arrays.binarySearch(T[] a, T key) / Arrays.binarySearch(int[] a, int key) / Arrays.binarySearch(long[] a, long key) / Arrays.binarySearch(double[] a, double key) 等重载方法

    • 功能: 在排序后的数组中查找指定元素的索引。

    • 语法:

      • public static int binarySearch(int[] a, int key) (在 int 数组中查找 key)
      • public static <T> int binarySearch(T[] a, T key) (在对象数组中查找 key)
      • public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) (在 int 数组的指定范围内查找 key)
    • 说明:

      • 前提条件:数组必须已经排序。
      • 如果找到 key,则返回其索引。
      • 如果未找到 key,则返回 -(insertion point) - 1,其中 insertion point 是 key 应该插入的位置(保持排序)。
      • 如果数组包含多个与 key 相等的元素,则不保证返回哪个元素的索引。
      • fromIndex(包含)和 toIndex(不包含)指定了查找的范围。
    • 示例:

    int[] numbers = {1, 2, 5, 8, 9};
    int index = Arrays.binarySearch(numbers, 5);
    System.out.println("5 的索引: " + index); // 输出: 5 的索引: 2
    int index2 = Arrays.binarySearch(numbers, 3);
    System.out.println("3 的索引: " + index2); // 输出: 3 的索引: -3 (表示应该插入到索引 2 的位置)
    String[] names = {"Alice", "Bob", "Charlie"};
    Arrays.sort(names); // 必须先排序
    int index3 = Arrays.binarySearch(names, "Bob");
    System.out.println("Bob 的索引: " + index3); // 输出: Bob 的索引: 1
    • 适用场景: 需要在排序后的数组中快速查找指定元素时,可以使用 Arrays.binarySearch() 方法。

2024/11/26#

1.mybatis(动态sql)#

  • 核心关键字:

    • select: 定义查询语句,用于从数据库检索数据。
    • insert: 定义插入语句,用于向数据库添加新记录。
    • update: 定义更新语句,用于修改数据库中的现有记录。
    • delete: 定义删除语句,用于从数据库中删除记录。

    动态 SQL 相关关键字 (非常重要):

    • if: 根据条件执行 SQL 片段。 常与test结合使用判断
    • choose / when / otherwise: 类似于 Java 中的 switch 语句,用于多条件选择。
    • ==trim / where / set====: 用于去除 SQL 语句中多余的关键字或字符,例如避免 where 条件为空时出现 “WHERE AND” 的情况. where 会自动处理第一个 and 或 or,set 会自动处理最后一个逗号。==
    • foreach: 遍历集合,用于构建 IN 条件或批量操作。
    • bind: 创建一个变量并将其绑定到一个值。

    参数和结果映射:

    • parameterType: 指定输入参数的类型。 在 MyBatis 3.4.0 之后,通常可以省略,MyBatis 会自动推断类型。 ==@Param(“bean”) 注入参数并命名==
    • resultMap: 定义结果集与 Java 对象之间的映射关系。
    • resultType: 指定结果的类型,适用于简单的查询结果。 在 MyBatis 3.4.0 之后,通常可以省略,MyBatis 会自动推断类型。

    其他常用关键字:

    • sql: 定义可重用的 SQL 片段。
    • include: 引入已定义的 SQL 片段。
    • resultMap: 定义结果集与 Java 对象之间的映射关系,可以处理复杂的映射场景。
    • collection / association: 用于处理一对多和一对一关系的嵌套查询。
    • discriminator / case: 用于处理继承关系的映射。
INSERT INTO email_code
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bean.email != null">
email,
</if>
<if test="bean.code != null">
code,
</if>
<if test="bean.createTime != null">
create_time,
</if>
<if test="bean.status != null">
status,
</if>
</trim>

<trim prefix=”(” suffix=”)” suffixOverrides=”,”> 的作用是动态构建 INSERT 语句中列名的部分,并确保 SQL 语法正确,==即使某些字段为空也不会出现多余的逗号。 它通过 prefix 和 suffix 添加括号,通过 suffixOverrides 去除最后一个逗号。==

2.MyBatis 传入参数的问题#

对于传入参数的指定常用parameterType 一般基本数据类型会自动映射不需要指定,对于复杂数据类型,例如对象,集合需要指定。

对于返回类型使用resultType/resultMap,同样基本数据类型可以不指定,对于复杂数据类型需要。

@Param注解

== 对于@Param注解的使用,一般如果使用了该注解传参,那么在xml中就不再需要parameterType声明==

  • MyBatis 如何处理多个参数? 在 MyBatis 中,==如果一个 Mapper 方法只有一个参数,MyBatis 默认会将这个参数当作 parameter== 传递给 SQL 语句,我们可以在 SQL 中直接引用它。==但当有多个参数时,MyBatis 会把它们封装成一个 Map 对象==,参数名是系统生成的,形式类似于 param1、param2 等。

    例如,如果我们有一个方法如下: List<RadiatedAreaVo> getScanAreaLine(String startDate, String endDate);

  • MyBatis 会默认地将参数封装成:{param1=startDateValue, param2=endDateValue} 这时,在 XML 中引用参数时,就必须通过 #{param1} 和 #{param2} 来访问这两个参数。

    如果想要在 SQL 中明确使用自己定义的名字而不是 param1、param2,那么就需要使用 @Param 注解了。我们可以通过 @Param 明确指定每个参数的名称。

    例如:

    List<RadiatedAreaVo> getScanAreaLine(@Param(“startDate”) String startDate, @Param(“endDate”) String endDate); MyBatis 会将参数封装成:=={startDate=startDateValue, endDate=endDateValue}== 现在,我们就可以在 SQL 中通过 #{startDate} 和 #{endDate} 访问这些参数。

一、单个参数#

  1. 基本数据类型

    • 不使用注解:当只有一个基本类型的参数时,可以直接在 #{} 中使用参数名,MyBatis 会自动识别。

      List<ChargeRuleDO> tests(long id);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{id}
      </select>
    • 使用 @Param 注解:当需要明确指定参数名时,可以在方法参数前加上 @Param 注解。

      List<ChargeRuleDO> tests(@Param("aid") long bid);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{aid}
      </select>
  2. 复杂数据类型(Java 实体类)

    • 不使用注解:如果传递的是一个实体对象,可以直接在 #{} 中使用对象的属性名。

      List<ChargeRuleDO> tests(TestQO testQO);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{id} and t.rule_type=#{ruleType}
      </select>
    • 使用 @Param 注解:如果使用了 @Param 注解,需要在 #{} 中指定对象的完整路径。

      List<ChargeRuleDO> tests(@Param("atestQO") TestQO btestQO);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{atestQO.id} and t.rule_type=#{atestQO.ruleType}
      </select>

二、多个参数#

  1. 基本数据类型

    • 不使用注解:可以通过位置参数(如 #{0}, #{1})来引用参数。

      List<ChargeRuleDO> tests(long id, String ruleType);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{0} and t.rule_type=#{1}
      </select>
    • ==使用 @Param 注解====:推荐使用 ====@Param==== 注解来明确指定每个参数的名称。==

      List<ChargeRuleDO> tests(@Param("id") long id, @Param("ruleType") String ruleType);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{id} and t.rule_type=#{ruleType}
      </select>
  2. 复杂数据类型

    • 不使用注解:直接传递多个对象时,可能需要额外处理,因为 MyBatis 默认情况下不支持直接传递多个对象。

    • ==使用 @Param 注解====:使用 ====@Param==== 注解来明确指定每个对象的名称,并在 ====#{}==== 中使用完整的路径。==

      List<ChargeRuleDO> tests(@Param("testQO") TestQO testQO, @Param("testQO2") TestQO2 testQO2);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{testQO.id} and t.rule_type=#{testQO.ruleType} and t.user_id=#{testQO2.uesrId}
      </select>
  3. 基本数据类型与复杂数据类型混合使用

    • 使用 @Param 注解:基本数据类型可以直接使用,复杂数据类型需要使用 @Param 注解。
      List<ChargeRuleDO> tests(long id, String ruleType, @Param("testQO2") TestQO2 testQO2);
      <select resultType="com.xxx.bean.ChargeRuleDO">
      select * from t_charge_rule t where t.id = #{0} and t.rule_type=#{1} and t.user_id=#{testQO2.uesrId}
      </select>

三、List 参数#

  • 传递 List 类型参数:使用 <foreach> 标签来遍历列表。
    List<ChargeRuleDO> tests(List<Long> list);
    <select resultType="com.xxx.bean.ChargeRuleDO">
    select * from t_charge_rule t where t.id in
    <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
    #{item}
    </foreach>
    </select>

四、Map 参数#

  • 传递 Map 类型参数:使用 <foreach> 标签遍历 Map,并通过 keyvalue 访问 Map 的键值对。
    int updateInventoryBatch(@Param("inventoryMap") HashMap<String, Integer> inventoryMap);
    <update parameterType="java.util.Map">
    <foreach item="value" index="key" collection="inventoryMap" separator=";">
    UPDATE yanxuan_inventory_transfer
    SET
    inventory = #{value},
    is_inventory='1',
    create_time=sysdate()
    WHERE
    sku_id=#{key}
    </foreach>
    </update>

2024/11/29#

1.序列化#

==序列化==是==将对象的状态信息转换为可以存储或传输的形式的过程==。==反序列化==则是将==存储或传输形式的数据==转换回==对象==的过程。

  • 数据持久化: 将对象的状态保存到磁盘或数据库中,以便以后恢复。
  • 数据传输: 通过网络传输对象的数据,例如在 Web 服务或分布式系统中。
  • 缓存: 将对象的状态存储在缓存中,以提高访问速度。
  • 深度拷贝: 创建对象的副本,包括对象内部的嵌套对象。

各种序列化方式的比较:

  • 二进制序列化:

    • 优点:速度快,体积小。
    • 缺点:可读性差,不同语言或平台之间难以兼容。
  • JSON 序列化:

    • 优点:可读性好,跨语言兼容性好,体积相对较小。
    • 缺点:速度略慢于二进制序列化。
  • XML 序列化:

    • 优点:可读性好,可扩展性强。
    • 缺点:体积较大,速度较慢。
//Serializable 接口:
// Serializable 接口是 Java 中用于序列化对象的接口。实现 Serializable 接口的类可以被序列化,这意味着可以将对象转换为字节流,并将其写入文件或网络流中。反序列化则可以将字节流恢复为对象。
/*
* 一般什么时候需要使用序列化呢?
* 将对象写入数据库(mysql,redis等)或其在网络传输中,另一端是不能理解对象的复杂结构的,
* 他们一般智能理解字符串(一般文本,json,xml等)因此需要将对象进行序列化处理
* 如果我只是单一的对象与数据库对应,不需要对对象进行传输则不需要序列化操作。
*
* 注意,在前后端开发中,后端我们这里使用springboot框架Spring Boot 内置了 Spring MVC 框架,Spring MVC 提供了强大的 RESTful API 开发支持,
* 并集成了 Jackson 库,默认使用 JSON 作为数据格式进行序列化和反序列化。*/

==Spring Boot 可以自动将前端传递的 JSON 字符串反序列化为 Java 对象。 例如:==

  • @RequestBody 注解: 在 Controller 方法的参数上使用 @RequestBody 注解,表明该参数需要从请求体中获取数据。 #此处详细介绍在参数接收
  • HttpMessageConverter: Spring Boot 使用 HttpMessageConverter 来处理请求和响应的数据转换。当请求的 Content-Type 为 application/json 时,Spring Boot 会自动使用 MappingJackson2HttpMessageConverter(如果 Jackson 依赖存在于 classpath 中)将 JSON 字符串反序列化为 Java 对象。
  • Jackson 库: Jackson 是 Spring Boot 默认使用的 JSON 处理库。它负责将 JSON 字符串解析成 Java 对象,以及将 Java 对象序列化成 JSON 字符串。

2.java泛型#

在 Java 泛型中,V、P 和 T 都是常用的类型参数名称,但它们各自通常代表不同的含义。这些字母并没有固定的语义,而是根据上下文和开发者的选择来定义的。

T (Type):通常用来表示任意类型的对象。这是最通用的类型参数,当你不需要指定具体的类型时可以使用它。 V (Value):常用于表示值类型,尤其是在键值对(如 Map)中,V 代表值的类型。 P (Parameter):有时用于表示方法参数的类型,或者在某些情况下,可能表示生产者(Producer)的类型。

使用 V 表示值类型
import java.util.Map;
import java.util.HashMap;
public class RedisUtils<V> {
private final Map<String, V> cache = new HashMap<>();
public void set(String key, V value) {
cache.put(key, value);
}
public V get(String key) {
return cache.get(key);
}
}
在这个例子中,V 代表 Redis 缓存中存储的值的类型。
使用 T 表示通用类型
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
这里 T 是一个通用类型参数,可以是任何类型,比如 Integer、String 或自定义的对象。
使用 P 表示参数类型
public class Processor {
public void process(P parameter) {
// 处理参数的逻辑
}
}
在这个例子中,P 代表处理器方法接受的参数类型。
组合使用多个类型参数
你也可以在一个类或方法中同时使用多个泛型参数。例如,如果你有一个方法需要处理键值对,并且键和值的类型不同。
public class KeyValueProcessor<K, V> {
private final Map<K, V> data = new HashMap<>();
public void put(K key, V value) {
data.put(key, value);
}
public V get(K key) {
return data.get(key);
}
}
在这里,K 代表键的类型,而 V 代表值的类型。
泛型方法示例
你还可以为单个方法定义类型参数,而不必在整个类上使用泛型。这使得方法可以在不改变类的情况下支持多种类型。
public class Util {
// 泛型方法,T 作为类型参数
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// 泛型方法,P 作为参数类型
public static <P> void process(P parameter) {
// 处理参数的逻辑
System.out.println("Processing: " + parameter);
}
}

2024/11/30#

1. equals 与 ==#

/* 字符串常量池: Java 中有一个字符串常量池,用来存储字符串常量。
当程序创建字符串常量时,如果字符串常量池中已经存在该字符串,则不会创建新的对象,而是返回指向常量池中该字符串的引用。*/
/* String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
System.out.println(str1 == str2); // true 因为 str1 和 str2 指向同一个字符串常量池中的 "Hello" 对象
System.out.println(str1 == str3); // false 因为 str3 创建了一个新的字符串对象
System.out.println(str1.equals(str2)); // true 因为 str1 和 str2 的内容相同
System.out.println(str1.equals(str3)); // true 因为 str1 和 str3 的内容相同*/

2.instanceof#

instanceof 是 Java 中的一个关键字,用于在运行时检查对象是否是指定类型或其子类的实例。

public Long getFileSizeFromRedis(String key) {
//从redis中获取名为key的键值
Object object = redisUtils.get(key);
//如果redis中没有返回0L
if (null == object) {
return 0L;
}
//TODO instanceof
//如果返回的键值类型为Integer,则转化为Long类型
if (object instanceof Integer) {
return ((Integer) object).longValue();
}
//如果类型为Long,直接返回
else if (object instanceof Long) {
return (Long) object;
}
//否则返回0L
return 0L;
}

2024/12/4#

1.后端如何接收前端的参数#

当与前端交互的过程当中,常用json数据与前端进行交互,这样想取出前端传送过来的json数据的时候,我们常有以下几种方式:

  • 1. 无注解传参

    • 适用场景: 简单参数,参数名与请求参数名一致。
    • 原理: Spring 会根据方法参数名与请求参数名进行匹配。
    • 局限性: 仅适用于参数名完全匹配的情况,灵活性较差。==如果参数名不匹配或需要处理复杂对象,则无法使用==。
    @GetMapping("/users")
    public List<User> getUsers(String name, int age) {
    // ...
    }

    2. @PathVariable

    • 适用场景: 获取 URL 路径中的参数。
    • 原理: ==从 URL 模板中提取参数值==。
    • 示例:
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
    // ...
    }

    3. @RequestParam

    • 适用场景: ==获取请求参数(查询参数或表单参数)==。

    • 原理: 从请求参数中提取值,可以处理多个参数。

    • 特性:

      • required 属性:指定参数是否必需。
      • defaultValue 属性:设置参数的默认值。
    • 示例:

    @GetMapping("/users")
    public List<User> getUsers(@RequestParam(required = false) String name,
    @RequestParam(defaultValue = "10") int pageSize) {
    // ...
    }

    4. @RequestBody

    • 适用场景: ==接收请求体中的数据==,通常用于处理 JSON 或 XML 格式的数据。
    • 原理: 使用 HttpMessageConverter 将请求体数据反序列化为 Java 对象。
    • 示例:
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
    // ...
    }

    5. 其他参数类型

    除了以上几种方式,Spring Boot 还支持其他一些参数类型:

    • MultipartFile: 用于处理文件上传。
    • HttpHeaders: 用于访问 HTTP 请求头信息。
    • HttpEntity<T>: 包含请求头和请求体的泛型类型。
    • Map<String, String> 或 MultiValueMap<String, String>: 用于接收所有请求参数。
    • HttpServletRequest、HttpServletResponse、HttpSession: 用于访问 Servlet API。 (尽量避免直接使用,推荐使用更高级的抽象)
      虽然Java Servlet可以满足需求,但在springboot中可以使用更高级的抽象
      1. 更高级的抽象及其使用示例:
      获取请求参数: 使用 @RequestParam、@PathVariable、@ModelAttribute 等注解,而不是 HttpServletRequest.getParameter()。
      @GetMapping("/users")
      public String getUsers(@RequestParam String name, @PathVariable Long id) {
      // ...
      }
      设置响应头: 使用 ResponseEntity 或 @ResponseStatus 注解,而不是 HttpServletResponse.setHeader()。
      @GetMapping("/users/{id}")
      public ResponseEntity<User> getUser(@PathVariable Long id) {
      User user = userService.findById(id);
      return ResponseEntity.ok().header("X-Custom-Header", "someValue").body(user);
      }
      重定向: 使用 RedirectView、ModelAndView 或 @RedirectAttributes,而不是 HttpServletResponse.sendRedirect()。
      @PostMapping("/users")
      public ModelAndView createUser(@ModelAttribute User user, RedirectAttributes redirectAttributes) {
      userService.save(user);
      redirectAttributes.addFlashAttribute("message", "User created successfully!"); // 添加 Flash Attribute
      return new ModelAndView("redirect:/users");
      }
      访问请求体: 使用 @RequestBody,而不是直接读取 HttpServletRequest 的输入流。
      @PostMapping("/users")
      public void createUser(@RequestBody User user) {
      // ...
      }
      2.对于session的操控
      添加依赖:
      <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      Xml
      配置 Redis 连接信息 (例如在 application.properties 中):
      spring.redis.host=localhost
      spring.redis.port=6379
      Properties
      启用 Spring Session:
      @EnableRedisHttpSession
      public class Config {
      // ... other configurations
      }
      使用 Session 或 MapSession 访问 Session:
      @GetMapping("/users")
      public String getUsers(Session session) { // 或 MapSession session
      session.setAttribute("userName", "John");
      // ...
      }

6. 前端 paramsdata 对应后端的接收方式总结

前端方式后端接收方式示例
params@RequestParam@RequestParam String keyword
data@RequestBody(推荐)@RequestBody UserDTO user
data@RequestParam(form-urlencoded)@RequestParam String username

** 说明**:

  • @RequestParam

    • 必需参数:默认是必需的,如果参数缺失会抛出异常。

    • 可选参数:可以通过 required = false 设置为可选,并指定默认值 defaultValue

      @RequestParam(required = false, defaultValue = "1") int page

说明

  • @RequestBody

    • 自动将请求体(JSON)反序列化为 Java 对象。
    • 必须确保请求头 Content-Type: application/json

2.后端返回数据给前端#

1.返回 JSON 数据 (最常用)

  • 适用场景: 前后端分离的 Web 应用、RESTful API。

    依靠 Spring Boot 内置的 HttpMessageConverter 机制,特别是 MappingJackson2HttpMessageConverter  引入依赖

  • 方法: 使用 @ResponseBody 注解或 ResponseEntity 对象。

  • 优点: 轻量级、易于解析、跨平台兼容性好。

  • 示例:

  • @ResponseBody 注解:

    • 作用: @ResponseBody 注解告诉 Spring Boot 将方法的返回值直接写入 HTTP 响应体,而不是作为视图名称解析。
    • 适用场景: 适合简单的 RESTful API,只需要返回数据,不需要设置 HTTP 状态码或自定义响应头。
    • 示例:
    @GetMapping("/users")
    @ResponseBody
    public List<User> getUsers() {
    List<User> users = userService.findAll();
    return users; // userList 会被自动序列化为 JSON 数组
    }

    getUsers() 方法返回一个 List<User> 对象。@ResponseBody 注解会使 Spring Boot 使用 Jackson 将 users 列表序列化为 JSON 数组,并将其写入 HTTP 响应体。

    ResponseEntity 对象

    • 作用: ResponseEntity 对象代表整个 HTTP 响应,包括状态码、响应头和响应体。它提供了更精细的控制,可以自定义 HTTP 响应的各个方面。
    • 适用场景: 适合需要设置 HTTP 状态码、自定义响应头或处理不同响应类型的场景。
    • 示例:
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
    Optional<User> userOptional = userService.findById(id);
    if (userOptional.isPresent()) {
    User user = userOptional.get();
    return ResponseEntity.ok(user); // 返回 200 OK 和 User 对象的 JSON 数据
    } else {
    return ResponseEntity.notFound().build(); // 返回 404 Not Found
    }
    }

    getUser() 方法返回一个 ResponseEntity<User> 对象。ResponseEntity.ok(user) 会创建一个状态码为 200 OK 的 ResponseEntity,并将 user 对象作为响应体。 ResponseEntity.notFound().build() 则创建一个状态码为 404 Not Found 的 ResponseEntity,没有响应体。

    ResponseEntity 的常用方法:

    • ==ok(): 创建 200 OK 响应。==
    • created(URI location): 创建 201 Created 响应,并设置 Location 头部信息。
    • accepted(): 创建 202 Accepted 响应。
    • ==badRequest(): 创建 400 Bad Request 响应。==
    • ==notFound(): 创建 404 Not Found 响应。==
    • status(HttpStatus status): 创建自定义状态码的响应。
    • headers(HttpHeaders headers): 设置响应头。
    • body(T body): 设置响应体。

2.自定义返回体,或直接返回,引入Jackson后springboot回自动实现序列化。

3.前端如何发送/接收后端的数据#

使用 Axios (第三方库)

Axios 是一个流行的基于 Promise 的 HTTP 客户端,它简化了网络请求的发送和处理。

  • 发送 GET 请求:
axios.get('/users')
.then(response => {
console.log(response.data); // 数据直接在 response.data 中
})
.catch(error => {
console.error(error);
});
  • 发送 POST 请求:
axios.post('/users', { name: 'John', age: 30 })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});

(1)params#

  • 定义params 是用于配置 查询参数,它会将参数附加到 URL 的末尾,以查询字符串的形式发送。

  • 使用场景:适用于 GET 请求 或需要在 URL 中显示参数的场景。

  • 数据形式:参数会以 key=value 的形式出现在 URL 中。

  • 示例

    axios.get('/api/users', {
    params: { id: 123, name: 'John' }
    });

    请求 URL

    /api/users?id=123&name=John

(2)data#

  • 定义data 是用于发送请求体中的数据,主要用于 POST、PUT、PATCH 等非 GET 请求。

  • 使用场景:适用于发送大规模或敏感数据(如表单数据、JSON 对象)时,不希望参数暴露在 URL 中。

  • 数据形式:参数会包含在请求体(Body)中,而不会出现在 URL。

  • 示例

    axios.post('/api/users', {
    data: { id: 123, name: 'John' }
    });

    请求 URL

    bash复制代码/api/users

    请求体

    {
    "id": 123,
    "name": "John"
    }

(3)主要区别总结#

特性paramsdata
发送方式附加在 URL 上包含在请求体(Body)中
适用方法GET 请求为主POSTPUTPATCHDELETE
参数暴露性参数会暴露在 URL 中参数不会暴露,适合发送敏感信息
数据大小适合较小的数据(如过滤条件)适合较大的复杂数据(如 JSON)
浏览器缓存因为在 URL 中,可能被浏览器缓存不会被缓存
  • params:查询条件、分页信息、过滤参数等,可以放在 URL 中。
  • data:涉及敏感信息(如密码)或需要传递复杂数据时,建议使用 data

2024/12/9#

1.java IO#

File类#

  • java.io.File 类是专门对文件进行操作的类,==只能对文件本身进行操作,不能对文件内容进行操作==。
    java.io.File 类==是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作==。

    File类构造方法不会给你检验这个文件或文件夹是否真实存在,==因此无论该路径下是否存在文件或者目录,都不影响File对象的创建。==

    • 1.构造函数

    • ==public File(String pathne)==

      String path = "D:\\123.txt"; File file1 = new File(path);
    • 2.获取功能的方法

      public String getAbsolutePath() :返回此File的绝对路径名字符串。

      public String getPath() :将此File转换为路径名字符串。

      public String getName() :返回由此File表示的文件或目录的名称。

      public long length() :返回由此File表示的文件的长度。

      public class FileGet {
      public static void main(String[] args) {
      File f = new File("d:/aaa/bbb.java");
      System.out.println("文件绝对路径:"+f.getAbsolutePath());
      System.out.println("文件构造路径:"+f.getPath());
      System.out.println("文件名称:"+f.getName());
      System.out.println("文件长度:"+f.length()+"字节");
      File f2 = new File("d:/aaa");
      System.out.println("目录绝对路径:"+f2.getAbsolutePath());
      System.out.println("目录构造路径:"+f2.getPath());
      System.out.println("目录名称:"+f2.getName());
      System.out.println("目录长度:"+f2.length());
      }
      }
      输出结果:
      文件绝对路径:d:\aaa\bbb.java
      文件构造路径:d:\aaa\bbb.java
      文件名称:bbb.java
      文件长度:2116字节
      目录绝对路径:d:\aaa
      目录构造路径:d:\aaa
      目录名称:aaa
      目录长度:3236

      注意:length(),表示文件的长度。但是File对象表示目录,则返回值未指定。

      • 3. 绝对路径和相对路径

      绝对路径:一个完整的路径,以盘符开头,例如F://aaa.txt
      相对路径:一个简化的路径,不以盘符开头,例如//aaa.txt//b.txt

      1、路径是不区分大小写
      2、路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

      //绝对路径
      public class FilePath {
      public static void main(String[] args) {
      // D盘下的bbb.java文件
      File f = new File("D:\\bbb.java");
      System.out.println(f.getAbsolutePath());
      // 项目下的bbb.java文件
      File f2 = new File("bbb.java");
      System.out.println(f2.getAbsolutePath());
      }
      }
      输出结果:
      D:\bbb.java
      D:\java\bbb.java
      • 4.判断功能的方法

      1、 public boolean exists() :此File表示的文件或目录是否实际存在。
      2、 public boolean isDirectory() :此File表示的是否为目录。
      3、public boolean isFile() :此File表示的是否为文件。

      方法演示,代码如下:

      public class FileIs {
      public static void main(String[] args) {
      File f = new File("d:\\aaa\\bbb.java");
      File f2 = new File("d:\\aaa");
      // 判断是否存在
      System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());
      System.out.println("d:\\aaa 是否存在:"+f2.exists());
      // 判断是文件还是目录
      System.out.println("d:\\aaa 文件?:"+f2.isFile());
      System.out.println("d:\\aaa 目录?:"+f2.isDirectory());
      }
      }
      输出结果:
      d:\aaa\bbb.java 是否存在:true
      d:\aaa 是否存在:true
      d:\aaa 文件?:false
      d:\aaa 目录?:true

      5.创建删除功能的方法

      • public boolean createNewFile() :文件不存在,创建一个新的空文件并返回true,文件存在,不创建文件并返回false
      • public boolean delete() :删除由此File表示的文件或目录。
      • public boolean mkdir() :创建由此File表示的目录。
      • public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。

      其中,mkdirs()mkdir()方法类似,但mkdir(),只能创建一级目录,mkdirs()可以创建多级目录比如//a//b//c,所以开发中一般用mkdirs();

      这些方法中值得注意的是createNewFile方法以及mkdirmkdirs的区别

      方法测试,代码如下:

      public class FileCreateDelete {
      public static void main(String[] args) throws IOException {
      // 文件的创建
      File f = new File("aaa.txt");
      System.out.println("是否存在:"+f.exists()); // false
      System.out.println("是否创建:"+f.createNewFile()); // true
      System.out.println("是否创建:"+f.createNewFile()); // 以及创建过了所以再使用createNewFile返回false
      System.out.println("是否存在:"+f.exists()); // true
      // 目录的创建
      File f2= new File("newDir");
      System.out.println("是否存在:"+f2.exists());// false
      System.out.println("是否创建:"+f2.mkdir()); // true
      System.out.println("是否存在:"+f2.exists());// true
      // 创建多级目录
      File f3= new File("newDira\\newDirb");
      System.out.println(f3.mkdir());// false
      File f4= new File("newDira\\newDirb");
      System.out.println(f4.mkdirs());// true
      // 文件的删除
      System.out.println(f.delete());// true
      // 目录的删除
      System.out.println(f2.delete());// true
      System.out.println(f4.delete());// false
      }
      }

      注意:delete方法,如果此File表示目录,则目录必须为空才能删除。

      6.目录的遍历

      • public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
      • public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
      public class FileFor {
      public static void main(String[] args) {
      File dir = new File("G:\光标");
      //获取当前目录下的文件以及文件夹的名称。
      String[] names = dir.list();
      for(String name : names){
      System.out.println(name);
      }
      //获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息
      File[] files = dir.listFiles();
      for (File file : files) {
      System.out.println(file);
      }
      }
      }

IO流#

在这里插入图片描述

在这里插入图片描述

字节流:

  • FileInputStream: 从文件中读取字节数据。

    • ==read(): 读取单个字节,返回读取的字节值(0-255),如果已到达文件末尾则返回 -1。==
    • ==read(byte[] b): 将数据读入字节数组 b,返回读取的字节数,如果已到达文件末尾则返回 -1。==
    • ==read(byte[] b, int off, int len): 将数据读入字节数组 b 的指定偏移量 off 开始的 len 个字节,返回读取的字节数==。
    • available(): 返回可读取的字节数的估计值。
    • ==close(): 关闭输入流,释放资源。==
  • FileOutputStream: 将字节数据写入文件。

    • ==write(int b): 写入单个字节。==
    • ==write(byte[] b): 写入字节数组 b 中的所有字节。==
    • ==write(byte[] b, int off, int len): 写入字节数组 b 的指定偏移量 off 开始的 len 个字节。==
    • ==flush(): 将缓冲区中的数据强制写入文件。==
    • ==close(): 关闭输出流,释放资源。==
  • ByteArrayInputStream: 从字节数组中读取数据。

    • read(), read(byte[] b), read(byte[] b, int off, int len), available(), close(): 与 FileInputStream 类似。
  • ByteArrayOutputStream: 将数据写入字节数组。

    • write(int b), write(byte[] b), write(byte[] b, int off, int len), flush(), close(): 与 FileOutputStream 类似。
    • ==toByteArray(): 将写入的字节数据转换为字节数组。==
    • toString(): 将写入的字节数据转换为字符串(使用平台默认字符集)。
    • ==toString(String charsetName): 将写入的字节数据转换为字符串(使用指定的字符集)。==
  • BufferedInputStream: 为另一个输入流提供缓冲功能,提高读取效率。

    • read(), read(byte[] b), read(byte[] b, int off, int len), available(), close(): 与 FileInputStream 类似。
  • BufferedOutputStream: 为另一个输出流提供缓冲功能,提高写入效率。

    • write(int b), write(byte[] b), write(byte[] b, int off, int len), flush(), close(): 与 FileOutputStream 类似。
  • ObjectInputStream: 从字节流中读取对象(反序列化)。

    • ==readObject(): 读取一个对象。==
    • close(): 关闭输入流。
  • ObjectOutputStream: 将对象写入字节流(序列化)。

    • ==writeObject(Object obj): 写入一个对象。==
    • flush(): 将缓冲区中的数据强制写入流。
    • close(): 关闭输出流。

字符流:

  • FileReader: 从文件中读取字符数据.

    • read(), read(char[] cbuf), read(char[] cbuf, int off, int len), close(): 与 FileInputStream 类似,但操作的是字符而不是字节。
  • FileWriter: 将字符数据写入文件.

    • write(int c), write(char[] cbuf), write(char[] cbuf, int off, int len), flush(), close(): 与 FileOutputStream 类似,但操作的是字符而不是字节。
  • StringReader: 从字符串中读取字符数据.

    • read(), read(char[] cbuf), read(char[] cbuf, int off, int len), close(): 与 FileReader 类似.
  • StringWriter: 将字符数据写入字符串.

    • write(int c), write(char[] cbuf), write(char[] cbuf, int off, int len), flush(), close(): 与 FileWriter 类似.
    • toString(): 返回写入的字符串.
  • BufferedReader: 为另一个字符输入流提供缓冲功能,提高读取效率.

    • read(), read(char[] cbuf), read(char[] cbuf, int off, int len), readLine(): 读取一行文本. close(): 与 FileReader 类似,并增加了 readLine() 方法.
  • BufferedWriter: 为另一个字符输出流提供缓冲功能,提高写入效率.

    • write(int c), write(char[] cbuf), write(char[] cbuf, int off, int len), newLine(): 写入一个换行符, flush(), close(): 与 FileWriter 类似,并增加了 newLine() 方法.
  • InputStreamReader: 将字节输入流转换为字符输入流,用于处理字符编码。

    • read(), read(char[] cbuf), read(char[] cbuf, int off, int len), close(): 与 FileReader 类似.
  • OutputStreamWriter: 将字节输出流转换为字符输出流,用于处理字符编码.

    • write(int c), write(char[] cbuf), write(char[] cbuf, int off, int len), flush(), close(): 与 FileWriter 类似.

==创建流对象的时候,系统会自动去对应位置创建对应文件,而创建输出流对象的时候,文件不存在则会报FileNotFoundException异常,也就是系统找不到指定的文件异常。==

字节流写入内存
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class input2 {
public static void main(String args[]){
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("a.txt");
int len = 0 ;
// 定义字节数组,作为装字节数据的容器
byte[] bys = new byte[1024];
while ((len = inputStream.read(bys)) != -1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(bys,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流写出磁盘
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamExample {
public static void main(String[] args) {
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream("b.txt"); // 创建FileOutputStream,如果文件不存在则创建,存在则覆盖
String data = "Hello, this is byte stream output!"; // 要写入的数据
byte[] bytes = data.getBytes(); // 将字符串转换为字节数组
outputStream.write(bytes); // 将字节数组写入文件
System.out.println("数据已写入 b.txt");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close(); // 关闭输出流,释放资源
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流写入内存
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("c.txt"); // 创建 FileReader
int character;
while ((character = reader.read()) != -1) { // 逐个字符读取
System.out.print((char) character); // 打印字符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close(); // 关闭 reader,释放资源
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流写出磁盘
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterExample {
public static void main(String[] args) {
FileWriter writer = null;
try {
writer = new FileWriter("c.txt"); // 创建 FileWriter
String data = "Hello, this is character stream output!"; // 要写入的数据
writer.write(data); // 将字符串写入文件
System.out.println("数据已写入 c.txt");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close(); // 关闭writer,释放资源
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用缓冲区
import java.io.*;
public class BufferedReaderExample {
public static void main(String[] args) {
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader("e.txt"); // 创建 FileReader
bufferedReader = new BufferedReader(fileReader); // 使用 BufferedReader 包装 FileReader
String line;
while ((line = bufferedReader.readLine()) != null) { // 逐行读取
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close(); // 关闭 BufferedReader,会自动关闭底层 FileReader
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 写入e.txt文件
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterExample {
public static void main(String[] args) {
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
fileWriter = new FileWriter("e.txt");
bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("Hello, buffered writer!");
bufferedWriter.newLine(); // 写入换行符
bufferedWriter.write("This is another line.");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java/SpringBoot 学习笔记
https://minikou.cloud/posts/springboot/
作者
minikou
发布于
2024-11-10
许可协议
Unlicensed

部分信息可能已经过时

目录