侧边栏壁纸
“学习日记” 共(42)篇
  • Java 枚举中的参数化常量与遍历查找逻辑 在 Java 开发中,枚举(enum)不仅仅是用来定义一组常量。通过参数化枚举常量和实现特定的逻辑,枚举可以变得功能强大且灵活。本文将针对以下两段代码进行详细解析:枚举常量的参数化定义FAIL(500, "失败")枚举值的遍历与查找逻辑for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) { if (resultCodeEnum.code == codeVal) { return resultCodeEnum; } }枚举常量的参数化定义基本概念在 Java 中,枚举(enum)用于定义一组固定的常量。通常,我们会看到类似以下的简单枚举:public enum Status { ACTIVE, INACTIVE, PENDING }然而,Java 枚举的强大之处在于它允许为每个枚举常量定义参数,从而为每个常量关联特定的数据。这种方式使得枚举不仅仅是简单的常量集合,而是可以携带更多信息的对象。参数化枚举常量的定义以您的代码为例:FAIL(500, "失败")这里,FAIL 是枚举 ResultCodeEnum 的一个常量,它被参数化为两个值:状态码(code):500描述信息(desc):"失败"这种参数化的定义方式使得每个枚举常量不仅有名称(如 FAIL),还携带了额外的属性信息。构造方法与字段为了支持参数化的枚举常量,需要在枚举中定义相应的字段和构造方法。例如:@Getter public enum ResultCodeEnum { SUCCESS(200, "成功"), FAIL(500, "失败"); // 状态码 private final int code; // 描述 private final String desc; ResultCodeEnum(int code, String desc) { this.code = code; this.desc = desc; } }解析:字段定义:private final int code; 和 private final String desc; 分别用于存储状态码和描述信息。使用 final 修饰确保这些字段的不可变性。构造方法:ResultCodeEnum(int code, String desc) 是一个私有构造方法,用于初始化每个枚举常量的 code 和 desc 字段。Lombok 的 @Getter 注解:自动为所有字段生成 getter 方法,简化代码编写。优点清晰的关联关系:每个枚举常量与其对应的状态码和描述信息紧密关联,提升代码的可读性和维护性。类型安全:避免了使用魔法数字或字符串,减少了出错的可能性。封装性:通过 private final 字段和 getter 方法,确保了数据的封装与不可变性。枚举值的遍历与查找逻辑背景在实际应用中,常常需要根据某个值(如状态码)来查找对应的枚举常量。例如,当接收到一个状态码 500 时,需要找到对应的 ResultCodeEnum.FAIL 枚举实例。遍历查找的实现以下是您提供的遍历查找逻辑:public static ResultCodeEnum getByCode(int codeVal) { for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) { if (resultCodeEnum.code == codeVal) { return resultCodeEnum; } } return null; }解析:ResultCodeEnum.values() 方法:这是一个由 Java 自动为枚举类型生成的静态方法,返回包含所有枚举常量的数组。在本例中,ResultCodeEnum.values() 返回 [SUCCESS, FAIL]。增强型 for 循环:for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) 遍历所有枚举常量。每次循环中,resultCodeEnum 分别代表 SUCCESS 和 FAIL。条件判断:if (resultCodeEnum.code == codeVal) 检查当前枚举常量的 code 是否与传入的 codeVal 相等。如果相等,返回该枚举常量。返回 null:如果遍历完所有枚举常量后仍未找到匹配的 codeVal,则返回 null。优点与缺点优点:简单直观:逻辑清晰,易于理解和实现。通用性:适用于枚举常量数量较少的情况。缺点:性能问题:随着枚举常量数量的增加,遍历查找的效率会下降(时间复杂度为 O(n))。空指针风险:方法返回 null 可能导致调用者在未进行 null 检查时发生 NullPointerException。优化建议为了提升查找效率和代码的健壮性,可以考虑以下优化措施:使用静态缓存(Map):通过一个静态的 Map 来缓存 code 与枚举实例的映射关系,将查找时间复杂度降低到 O(1)。private static final Map<Integer, ResultCodeEnum> CODE_MAP = new HashMap<>(); static { for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) { CODE_MAP.put(resultCodeEnum.getCode(), resultCodeEnum); } } public static ResultCodeEnum getByCode(int codeVal) { return CODE_MAP.get(codeVal); }使用 Optional 避免返回 null:返回 Optional<ResultCodeEnum>,强制调用者处理可能的空值,减少 NullPointerException 风险。public static Optional<ResultCodeEnum> getByCode(int codeVal) { return Optional.ofNullable(CODE_MAP.get(codeVal)); }抛出自定义异常:在未找到匹配枚举时,抛出自定义异常,明确告知调用者错误情况。public static ResultCodeEnum getByCode(int codeVal) { ResultCodeEnum result = CODE_MAP.get(codeVal); if (result == null) { throw new IllegalArgumentException("无效的状态码: " + codeVal); } return result; }综合应用示例结合上述两部分的解析,以下是一个完整的 ResultCodeEnum 实现示例:package com.jingdianjichi.subject.common.enums; import lombok.Getter; import java.util.HashMap; import java.util.Map; import java.util.Optional; @Getter public enum ResultCodeEnum { SUCCESS(200, "成功"), FAIL(500, "失败"); // 状态码 private final int code; // 描述 private final String desc; // 静态缓存 Map,用于快速查找 private static final Map<Integer, ResultCodeEnum> CODE_MAP = new HashMap<>(); // 静态代码块,初始化缓存 Map static { for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) { CODE_MAP.put(resultCodeEnum.getCode(), resultCodeEnum); } } // 构造方法 ResultCodeEnum(int code, String desc) { this.code = code; this.desc = desc; } public static Optional<ResultCodeEnum> getByCode(int codeVal) { return Optional.ofNullable(CODE_MAP.get(codeVal)); } }使用示例以下是如何在实际项目中使用 ResultCodeEnum 的示例:public class ApiResponse { private int code; private String message; private Object data; // 构造方法 public ApiResponse(int code, String message, Object data) { this.code = code; this.message = message; this.data = data; } // 静态方法:成功响应 public static ApiResponse success(Object data) { return new ApiResponse(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getDesc(), data); } // 静态方法:失败响应 public static ApiResponse fail(ResultCodeEnum resultCodeEnum) { return new ApiResponse(resultCodeEnum.getCode(), resultCodeEnum.getDesc(), null); } // Getter 和 Setter 略 }调用示例:@RestController @RequestMapping("/api") public class UserController { @GetMapping("/user/{id}") public ResponseEntity<ApiResponse> getUser(@PathVariable Long id) { Optional<User> userOpt = userService.findById(id); if (userOpt.isPresent()) { return ResponseEntity.ok(ApiResponse.success(userOpt.get())); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(ApiResponse.fail(ResultCodeEnum.FAIL)); } } }
    • 5个月前
    • 165
    • 0
    • 1
  • 使用 Guava 的 Preconditions 和 Apache Commons Lang3 的 StringUtils 进行参数校验 在软件开发中,参数校验是确保应用程序健壮性和可靠性的关键环节。合理的参数校验不仅可以防止潜在的错误,还能提供清晰的错误信息,便于调试和维护。本文将介绍如何结合使用 Google 的 Guava 库中的 Preconditions 类与 Apache Commons Lang3 的 StringUtils 类进行高效的参数校验。Guava 的 Preconditions 概述Guava 是 Google 开发的一个开源 Java 基础库,提供了许多实用的工具类和方法。其中,Preconditions 类专门用于参数校验,确保方法的输入参数满足预期条件。常用的方法包括:checkNotNull(T reference, String errorMessage): 检查引用是否为 null。checkArgument(boolean expression, String errorMessage): 检查布尔表达式是否为 true。checkState(boolean expression, String errorMessage): 检查对象状态是否符合预期。优点:简洁性:减少了繁琐的 if 语句。一致性:提供统一的参数校验方式。明确的错误信息:通过自定义错误信息,便于调试。Apache Commons Lang3 的 StringUtils 概述Apache Commons Lang 是 Apache 提供的一个常用的 Java 工具库,StringUtils 类是其中的一个核心组件,提供了丰富的字符串处理方法。常用的方法包括:isEmpty(String str): 检查字符串是否为空 (null 或 "")。isBlank(String str): 检查字符串是否为空 (null、"" 或仅包含空白字符)。isNotEmpty(String str): 检查字符串是否不为空。isNotBlank(String str): 检查字符串是否不为空且不全为空白字符。优点:丰富的功能:提供了多种字符串处理方法,覆盖大多数常见需求。提高代码可读性:方法命名清晰,意图明确。减少重复代码:避免了开发者编写重复的字符串处理逻辑。结合使用 Preconditions 和 StringUtils 进行参数校验将 Guava 的 Preconditions 与 Apache Commons Lang3 的 StringUtils 结合使用,可以实现高效且简洁的参数校验。例如:import com.google.common.base.Preconditions; import org.apache.commons.lang3.StringUtils; public class SubjectCategoryService { public void createCategory(SubjectCategoryDTO subjectCategoryDTO) { Preconditions.checkNotNull(subjectCategoryDTO.getCategoryType(), "分类类型不能为空"); Preconditions.checkArgument(!StringUtils.isBlank(subjectCategoryDTO.getCategoryName()), "分类的名称不能为空"); Preconditions.checkNotNull(subjectCategoryDTO.getParentId(), "分类的父级id不能为空"); // 业务逻辑处理 } }在上述代码中,Preconditions 用于检查对象是否为 null 或布尔表达式是否为 true,而 StringUtils.isBlank 用于判断字符串是否为空或仅包含空白字符。这种组合使用方式使得参数校验简洁明了,代码可读性和维护性显著提高。代码示例解析让我们详细解析上述代码示例中的各个部分。1. 检查分类类型是否为空Preconditions.checkNotNull(subjectCategoryDTO.getCategoryType(), "分类类型不能为空");功能:确保 categoryType 不为 null。方法:checkNotNull,如果 categoryType 为 null,则抛出 NullPointerException,并附带错误信息 "分类类型不能为空"。优点:避免了后续业务逻辑中因 null 导致的潜在 NullPointerException。2. 检查分类名称是否为空或仅包含空白字符Preconditions.checkArgument(!StringUtils.isBlank(subjectCategoryDTO.getCategoryName()), "分类的名称不能为空");功能:确保 categoryName 不为空,且不全为空白字符。方法:StringUtils.isBlank(subjectCategoryDTO.getCategoryName()):判断 categoryName 是否为 null、"" 或仅包含空白字符。checkArgument:如果表达式 !StringUtils.isBlank(...) 为 false,则抛出 IllegalArgumentException,并附带错误信息 "分类的名称不能为空"。优点:确保分类名称有效,防止用户输入无效数据。3. 检查分类的父级 ID 是否为空Preconditions.checkNotNull(subjectCategoryDTO.getParentId(), "分类的父级id不能为空");功能:确保 parentId 不为 null。方法:同上,使用 checkNotNull。优点:确保分类有一个有效的父级 ID,维护分类层级关系。最佳实践在实际开发中,结合使用 Preconditions 和 StringUtils 进行参数校验时,以下最佳实践值得遵循:1. 明确错误信息提供清晰、简洁且具描述性的错误信息,便于快速定位问题。例如:Preconditions.checkNotNull(user.getEmail(), "用户邮箱不能为空");2. 避免过度校验仅在必要的地方进行参数校验,避免冗余的检查。例如,如果某个参数在调用前已经被校验,可以考虑不再重复校验。3. 使用适当的校验方法根据具体需求选择合适的 Preconditions 方法:使用 checkNotNull 检查引用是否为 null。使用 checkArgument 检查方法参数是否符合预期条件。使用 checkState 检查对象的内部状态。参考资料Guava Documentation - PreconditionsApache Commons Lang - StringUtilsEffective Java - Joshua Bloch(推荐阅读,深入理解参数校验和其他最佳实践)
    • 5个月前
    • 174
    • 0
    • 2
  • 深入理解后端开发中的VO、PO、DTO、BO 在后端开发中,我们经常会接触到VO、PO、DTO、BO等概念。对于初学者来说,这些名词可能有些陌生,甚至有些混淆。本篇文章将详细介绍这些概念,并以Spring Cloud用户登录为例,帮助大家更好地理解它们的应用场景。一、VO、PO、DTO、BO是什么?1. VO(Value Object)——值对象VO是值对象的简称,主要用于在展示层(View)与业务层之间传递数据。它通常用于封装从前端接收的数据或者需要返回给前端展示的数据。特点:仅包含数据,没有业务逻辑。用于表示一个数值的对象,通常是只读的。生命周期短暂,一般只在一次请求或响应中存在。示例:当用户在登录页面输入用户名和密码时,这些数据可以封装为一个UserLoginVO对象,传递给后端处理。2. PO(Persistent Object)——持久化对象PO是持久化对象,与数据库中的表结构一一对应,通过ORM框架(如MyBatis、Hibernate)将数据库中的数据映射为Java对象。特点:与数据库表结构直接对应。用于持久化数据的存储和读取。可能包含数据库中的所有字段,包括一些不常用的字段。示例:用户信息在数据库中存储为user表,后端使用UserPO对象来映射这张表的数据。3. DTO(Data Transfer Object)——数据传输对象DTO是数据传输对象,用于在不同的层或远程调用之间传输数据。它可以对传输的数据进行裁剪,只保留必要的信息,减少数据传输量。特点:用于远程调用或长时间保存的数据传输。可以对数据进行裁剪,避免传输不必要的数据。通常只包含数据,不包含业务逻辑。示例:在微服务架构中,用户服务需要将用户信息传递给订单服务,可以使用UserDTO来传输必要的用户信息。4. BO(Business Object)——业务对象BO是业务对象,封装了业务逻辑的Java对象。它通过对多个PO、VO或其他BO进行组合,完成具体的业务操作。特点:包含业务逻辑或业务方法。可以由多个PO或其他对象组合而成。用于业务层的处理,贯穿整个业务流程。示例:在用户登录过程中,UserLoginBO封装了验证用户身份、记录登录日志等业务逻辑。二、以Spring Cloud用户登录为例下面,我们以Spring Cloud框架下的用户登录功能为例,来具体说明VO、PO、DTO、BO的应用。1. 用户登录流程用户在前端输入用户名和密码,点击登录。前端将用户名和密码封装为UserLoginVO对象,发送给后端。后端Controller接收到UserLoginVO,将其转换为UserDTO,传递给服务层。服务层调用UserLoginBO,处理登录逻辑。UserLoginBO使用DAO层查询数据库,获取UserPO对象。验证通过后,生成UserInfoVO,返回给前端展示。2. 代码示例(1)UserLoginVO —— 接收前端数据public class UserLoginVO { private String username; private String password; // Getter and Setter }说明:用于接收前端传递的用户名和密码。只包含数据,不包含任何业务逻辑。(2)UserDTO —— 服务间数据传输public class UserDTO { private String username; private String encryptedPassword; // Getter and Setter }说明:用于在服务层之间传输数据,比如在微服务之间的调用。可以对数据进行必要的处理,如密码加密。(3)UserPO —— 数据库持久化对象public class UserPO { private Long id; private String username; private String encryptedPassword; private Date createTime; private Date updateTime; // Getter and Setter }说明:与数据库中的user表结构对应。包含数据库中的所有字段,用于数据的持久化存储。(4)UserLoginBO —— 业务逻辑处理@Service public class UserLoginBO { @Autowired private UserDAO userDAO; public UserDTO login(UserDTO userDTO) { // 1. 根据用户名查询用户信息 UserPO userPO = userDAO.findByUsername(userDTO.getUsername()); // 2. 验证密码是否正确 if (userPO != null && userPO.getEncryptedPassword().equals(userDTO.getEncryptedPassword())) { // 3. 登录成功,返回用户信息 userDTO.setId(userPO.getId()); return userDTO; } else { // 4. 登录失败,抛出异常或返回null return null; } } }说明:封装了用户登录的业务逻辑。处理用户验证、异常处理等业务操作。(5)UserInfoVO —— 返回前端的数据public class UserInfoVO { private Long id; private String username; private String token; // Getter and Setter }说明:登录成功后,返回给前端的用户信息。包含必要的数据,敏感信息不会返回。3. 流程总结Controller层:接收UserLoginVO,调用服务层。服务层:将UserLoginVO转换为UserDTO,调用业务逻辑层UserLoginBO。业务逻辑层:UserLoginBO处理登录逻辑,使用UserPO与数据库交互。返回结果:登录成功后,将结果封装为UserInfoVO,返回给前端。三、总结通过上述示例,我们可以清晰地看到VO、PO、DTO、BO在后端开发中的角色和作用:VO(Value Object):用于界面层与服务层之间的数据传递,表示一次请求或响应的数据。PO(Persistent Object):用于持久化数据,与数据库表结构对应,进行数据的存储和读取。DTO(Data Transfer Object):用于服务层之间的数据传输,可能需要序列化,传输的数据可能经过裁剪和处理。BO(Business Object):封装业务逻辑,处理具体的业务操作,是业务层的核心。理解并合理运用这些对象,可以使我们的代码结构更加清晰,职责更加明确,提升代码的可维护性和可读性。还可参考这篇文章来进行理解: 一文讲清DTO、BO、PO、VO,为什么可以不需要VO?
    • 5个月前
    • 158
    • 0
    • 1
  • 数据库加密工具类编写(springboot) 在配置文件中,明文编写一些敏感数据是不安全的,所以通常会进行加密,这里我使用druid连接池的工具来进行加密处理package com.jingdianjichi.subject.infra.basic.service.utils; import com.alibaba.druid.filter.config.ConfigTools; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; public class DruidEncryptUtil { private static String publicKey; private static String privateKey; static { try { String[] keyPair = ConfigTools.genKeyPair(512); privateKey = keyPair[0]; System.out.println("privateKey:" + privateKey); publicKey = keyPair[1]; System.out.println("publicKey:" + publicKey); } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new RuntimeException(e); } } public static String encrypt(String plainText) throws Exception { String encrypt = ConfigTools.encrypt(privateKey, plainText); System.out.println("encrypt:" + encrypt); return encrypt; } public static String decrypt(String encryptText) throws Exception { String decrypt = ConfigTools.decrypt(publicKey, encryptText); System.out.println("decrypt:" + decrypt); return decrypt; } public static void main(String[] args) throws Exception { //这里我设置的密码是123456 String encrypt = encrypt("123456"); System.out.println("encrypt:" + encrypt); } } 运行此类后,控制台将打印出私钥和公钥以及解密后的密文,随后将相应数据填入yml配置文件中。password放密文;publicKey放公钥;还需要配置config:enabled以及connect-propertie;如下所示server: port: 3000 spring: datasource: username: root password: da6IO6pMIxKQS2Ir2E2WHVChJCBM++Jj86xWX8QFUC9P8GWwVmUI6JcC9eiS4CvC6OYdslgQX0CS0Bkr+9VGhQ== url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&useSSL=true driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: # 初始的连接数量 initial-size: 20 # 最小的空闲连接数 min-idle: 20 # 最大的活动连接数 max-active: 100 # 最大等待时间 max-wait: 60000 # 启用统计功能 stat-view-servlet: enabled: true # 统计功能的URL模式 url-pattern: /druid public class BaoZiPu { //代表包子的count private int count; //代表是否有包子的flag private boolean flag; public BaoZiPu() { } public BaoZiPu(int count, boolean flag) { this.count = count; this.flag = flag; } public void getCount() { System.out.println("消费了..............第"+count+"个包子"); } public void setCount() { count++; System.out.println("生产了...第"+count+"个包子"); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } } public class Product implements Runnable{ private BaoZiPu baoZiPu; public Product(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (baoZiPu){ //1.判断flag是否为true,如果是true,证明有包子,生产线程等待 if (baoZiPu.isFlag()==true){ try { baoZiPu.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为false,证明没有包子,开始生产 baoZiPu.setCount(); //3.改变flag状态,为true,证明生产完了,有包子了 baoZiPu.setFlag(true); //4.唤醒消费线程 baoZiPu.notify(); } } } } public class Consumer implements Runnable{ private BaoZiPu baoZiPu; public Consumer(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (baoZiPu){ //1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 if (baoZiPu.isFlag()==false){ try { baoZiPu.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为true,证明有包子,开始消费 baoZiPu.getCount(); //3.改变flag状态,为false,证明消费完了,没 有包子了 baoZiPu.setFlag(false); //4.唤醒生产线程 baoZiPu.notify(); } } } } public class Test01 { public static void main(String[] args) { BaoZiPu baoZiPu = new BaoZiPu(); Product product = new Product(baoZiPu); Consumer consumer = new Consumer(baoZiPu); Thread t1 = new Thread(product); Thread t2 = new Thread(consumer); t1.start(); t2.start(); } }3.用同步方法改造等待唤醒案例 public class BaoZiPu { //代表包子的count private int count; //代表是否有包子的flag private boolean flag; public BaoZiPu() { } public BaoZiPu(int count, boolean flag) { this.count = count; this.flag = flag; } public synchronized void getCount() { //1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 if (this.flag == false) { try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为true,证明有包子,开始消费 System.out.println("消费了..............第" + count + "个包子"); //3.改变flag状态,为false,证明消费完了,没 有包子了 this.flag = false; //4.唤醒生产线程 this.notify(); } public synchronized void setCount() { //1.判断flag是否为true,如果是true,证明有包子,生产线程等待 if (this.flag == true) { try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为false,证明没有包子,开始生产 count++; System.out.println("生产了...第" + count + "个包子"); //3.改变flag状态,为true,证明生产完了,有包子了 this.flag = true; //4.唤醒消费线程 this.notify(); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } } public class Product implements Runnable{ private BaoZiPu baoZiPu; public Product(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } baoZiPu.setCount(); } } } public class Consumer implements Runnable{ private BaoZiPu baoZiPu; public Consumer(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } baoZiPu.getCount(); } } } public class Test01 { public static void main(String[] args) { BaoZiPu baoZiPu = new BaoZiPu(); Product product = new Product(baoZiPu); Consumer consumer = new Consumer(baoZiPu); Thread t1 = new Thread(product); Thread t2 = new Thread(consumer); t1.start(); t2.start(); } }第二章.多等待多唤醒1.解决多生产多消费问题(if改为while,将notify改为notifyAll)public class Test01 { public static void main(String[] args) { BaoZiPu baoZiPu = new BaoZiPu(); Product product = new Product(baoZiPu); Consumer consumer = new Consumer(baoZiPu); new Thread(product).start(); new Thread(product).start(); new Thread(product).start(); new Thread(consumer).start(); new Thread(consumer).start(); new Thread(consumer).start(); } } public class BaoZiPu { //代表包子的count private int count; //代表是否有包子的flag private boolean flag; public BaoZiPu() { } public BaoZiPu(int count, boolean flag) { this.count = count; this.flag = flag; } public synchronized void getCount() { //1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 while (this.flag == false) { try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为true,证明有包子,开始消费 System.out.println("消费了..............第" + count + "个包子"); //3.改变flag状态,为false,证明消费完了,没 有包子了 this.flag = false; //4.唤醒所有等待线程 this.notifyAll(); } public synchronized void setCount() { //1.判断flag是否为true,如果是true,证明有包子,生产线程等待 while (this.flag == true) { try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为false,证明没有包子,开始生产 count++; System.out.println("生产了...第" + count + "个包子"); //3.改变flag状态,为true,证明生产完了,有包子了 this.flag = true; //4.唤醒所有等待线程 this.notifyAll(); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } } public class Product implements Runnable{ private BaoZiPu baoZiPu; public Product(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } baoZiPu.setCount(); } } } public class Consumer implements Runnable{ private BaoZiPu baoZiPu; public Consumer(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } baoZiPu.getCount(); } } } 第三章.Lock锁1.Lock对象的介绍和基本使用1.概述:Lock是一个接口 2.实现类:ReentrantLock 3.方法: lock() 获取锁 unlock() 释放锁public class MyTicket implements Runnable { //定义100张票 int ticket = 100; //创建Lock对象 Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { Thread.sleep(100L); //获取锁 lock.lock(); if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票"); ticket--; } } catch (InterruptedException e) { throw new RuntimeException(e); }finally { //释放锁 lock.unlock(); } } } } public class Test01 { public static void main(String[] args) { MyTicket myTicket = new MyTicket(); Thread t1 = new Thread(myTicket, "赵四"); Thread t2 = new Thread(myTicket, "刘能"); Thread t3 = new Thread(myTicket, "广坤"); t1.start(); t2.start(); t3.start(); } } synchronized:不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象 Lock:是通过两个方法控制需要被同步的代码,更灵活第四章.Callable接口_实现多线程方式三1.概述:Callable<V>是一个接口,类似于Runnable 2.方法: V call() -> 设置线程任务的,类似于run方法 3.call方法和run方法的区别: a.相同点:都是设置线程任务的 b.不同点: call方法有返回值,而且有异常可以throws run方法没有返回值,而且有异常不可以throws 4.<V> a.<V>叫做泛型 b.泛型:用于指定我们操作什么类型的数据,<>中只能写引用数据类型,如果泛型不写,默认是Object类型数据 c.实现Callable接口时,指定泛型是什么类型的,重写的call方法返回值就是什么类型的 5.获取call方法返回值: FutureTask<V> a. FutureTask<V> 实现了一个接口: Future <V> b. FutureTask<V>中有一个方法: V get() -> 获取call方法的返回值public class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "涛哥和金莲...的故事"; } }public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable myCallable = new MyCallable(); FutureTask<String> futureTask = new FutureTask<>(myCallable); //创建Thread对象-> Thread(Runnable target) Thread t1 = new Thread(futureTask); t1.start(); //调用get方法获取call方法返回值 System.out.println(futureTask.get()); } } 第五章.线程池_实现多线程方式四1.问题:之前来一个线程任务,就需要创建一个线程对象去执行,用完还要销毁线程对象,如果线程任务多了,就需要频繁创建线程对象和销毁线程对象,这样会耗费内存资源,所以我们就想线程对象能不能循环利用,用的时候直接拿线程对象,用完还回去1.如何创建线程池对象:用具类-> Executors 2.获取线程池对象:Executors中的静态方法: static ExecutorService newFixedThreadPool(int nThreads) a.参数:指定线程池中最多创建的线程对象条数 b.返回值ExecutorService 是线程池,用来管理线程对象 3.执行线程任务: ExecutorService中的方法 Future<?> submit(Runnable task) 提交一个Runnable任务用于执行 Future<T> submit(Callable<T> task) 提交一个Callable任务用于执行 4.submit方法的返回值:Future接口 用于接收run方法或者call方法返回值的,但是run方法没有返回值,所以可以不用Future接收,执行call方法需要用Future接收 Future中有一个方法:V get() 用于获取call方法返回值 5. ExecutorService中的方法: void shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务public class MyRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"...执行了"); } }public class Test01 { public static void main(String[] args) { //创建线程池对象 ExecutorService es = Executors.newFixedThreadPool(2); es.submit(new MyRunnable()); es.submit(new MyRunnable()); es.submit(new MyRunnable()); //es.shutdown();//关闭线程池对象 } }public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { return 1; } }public class Test02 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService es = Executors.newFixedThreadPool(2); Future<Integer> future = es.submit(new MyCallable()); System.out.println(future.get()); } } 练习需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串public class MyString implements Callable<String> { @Override public String call() throws Exception { return "那一夜,你没有拒绝我,那一夜,你伤害了我"; } }public class MySum implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { sum+=i; } return sum; } }public class Test01 { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建线程池对象 ExecutorService es = Executors.newFixedThreadPool(2); Future<String> f1 = es.submit(new MyString()); Future<Integer> f2 = es.submit(new MySum()); System.out.println(f1.get()); System.out.println(f2.get()); } }第六章.定时器_Timer1.概述:定时器 2.构造: Timer() 3.方法: void schedule(TimerTask task, Date firstTime, long period) task:抽象类,是Runnable的实现类 firstTime:从什么时间开始执行 period: 每隔多长时间执行一次,设置的是毫秒值 public class Demo01Timer { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("金莲对涛哥说:涛哥,快起床了~~~"); } },new Date(),2000L); } }
    • 1年前
    • 243
    • 2
    • 0
  • 尚硅谷java多线程笔记(上) java多线程 重点: 1.会使用多线程方法,主要是start() 2.会使用继承Thread的方式创建多线程 3.会使用实现Runnable接口的方式实现多线程 4.会使用同步代码块解决线程不安全问题 5.会使用同步方法解决线程不安全问题第一章.多线程基本了解1.多线程_线程和进程进程:在内存中执行的应用程序线程:是进程中最小的执行单元 线程作用:负责当前进程中程序的运行.一个进程中至少有一个线程,一个进程还可以有多个线程,这样的应用程序就称之为多线程程序 简单理解:一个功能就需要一条线程取去执行 1.使用场景: 软件中的耗时操作 -> 拷贝大文件, 加载大量的资源​ 所有的聊天软件​ 所有的后台服务器​ 一个线程可以干一件事,我们就可以同时做多件事了,提高了CPU的利用率2.并发和并行并行:在同一个时刻,有多个执行在多个CPU上(同时)执行(好比是多个人做不同的事儿) 比如:多个厨师在炒多个菜并发:在同一个时刻,有多个指令在单个CPU上(交替)执行 比如:一个厨师在炒多个菜细节: 1.之前CPU是单核,但是在执行多个程序的时候好像是在同时执行,原因是CPU在多个线程之间做高速切换 2.现在咱们的CPU都是多核多线程的了,比如2核4线程,那么CPU可以同时运行4个线程,此时不同切换,但是如果多了,CPU就要切换了,所以现在CPU在执行程序的时候并发和并行都存在 3.CPU调度1.分时调度:值的是让所有的线程轮流获取CPU使用权,并且平均分配每个线程占用CPU的时间片 2.抢占式调度:多个线程轮流抢占CPU使用权,哪个线程先抢到了,哪个线程先执行,一般都是优先级高的先抢到CPU使用权的几率大,我们java程序就是抢占式调度 4.主线程介绍主线程:CPU和内存之间开辟的转门为main方法服务的线程第二章.创建线程的方式(重点)1.第一种方式_extends Thread1.定义一个类,继承Thread 2.重写run方法,在run方法中设置线程任务(所谓的线程任务指的是此线程要干的具体的事儿,具体执行的代码) 3.创建自定义线程类的对象 4.调用Thread中的start方法,开启线程,jvm自动调用run方法public class Test01 { public static void main(String[] args) { //创建线程对象 MyThread t1 = new MyThread(); //调用start方法,开启线程,jvm自动调用run方法 t1.start(); for (int i = 0; i < 10; i++) { System.out.println("main线程..........执行了"+i); } } }public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyThread...执行了"+i); } } }2.多线程在内存中的运行原理注意:同一个线程对象不能连续调用多次start,如果想要再次调用start,那么咱们就new一个新的线程对象3.Thread类中的方法void start() -> 开启线程,jvm自动调用run方法 void run() -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法 String getName() -> 获取线程名字 void setName(String name) -> 给线程设置名字 static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象) static void sleep(long millis)->线程睡眠,超时后自动醒来继续执行,传递的是毫秒值 public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { //线程睡眠 try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } } public class Test01 { public static void main(String[] args) throws InterruptedException { //创建线程对象 MyThread t1 = new MyThread(); //给线程设置名字 t1.setName("金莲"); //调用start方法,开启线程,jvm自动调用run方法 t1.start(); for (int i = 0; i < 10; i++) { Thread.sleep(1000L); System.out.println(Thread.currentThread().getName()+"线程..........执行了"+i); } } } 问题:为啥在重写的run方法中有异常只能try,不能throws原因:继承的Thread中的run方法没有抛异常,所以在子类中重写完run方法之后就不能抛,只能try4.Thread中其他的方法void setPriority(int newPriority) -> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不是每次都先抢到 int getPriority() -> 获取线程优先级 void setDaemon(boolean on) -> 设置为守护线程,当非守护线程执行完毕,守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,系统会告诉守护线程人家结束了,你也结束吧,在告知的过程中,守护线程会执行,只不过执行到半路就结束了 static void yield() -> 礼让线程,让当前线程让出CPU使用权 void join() -> 插入线程或者叫做插队线程 4.1.线程优先级public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); } } }public class Test01 { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("金莲"); MyThread1 t2 = new MyThread1(); t2.setName("阿庆"); //System.out.println(t1.getPriority()); //System.out.println(t2.getPriority()); //设置优先级 t1.setPriority(1); t2.setPriority(10); t1.start(); t2.start(); } } 4.2.守护线程public class Test01 { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("金莲"); MyThread2 t2 = new MyThread2(); t2.setName("阿庆"); //将t2设置成守护线程 t2.setDaemon(true); t1.start(); t2.start(); } } public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); } } }public class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"执行了..."+i); } } } 4.3.礼让线程场景说明:如果两个线程一起执行,可能会执行一会儿线程A,再执行一会线程B,或者可能线程A执行完毕了,线程B在执行 那么我们能不能让两个线程尽可能的平衡一点 -> 尽量让两个线程交替执行 注意:只是尽可能的平衡,不是绝对的你来我往,有可能线程A线程执行,然后礼让了,但是回头A又抢到CPU使用权了 public class Test01 { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("金莲"); MyThread1 t2 = new MyThread1(); t2.setName("阿庆"); t1.start(); t2.start(); } }public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); Thread.yield(); } } } 4.4.插入线程public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); } } }public class Test01 { public static void main(String[] args) throws InterruptedException { MyThread1 t1 = new MyThread1(); t1.setName("金莲"); t1.start(); t1.join(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); } } }5.第二种方式_实现Runnable接口1.创建类,实现Runnable接口 2.重写run方法,设置线程任务 3.利用Thread类的构造方法:Thread(Runnable target),创建Thread对象(线程对象),将自定义的类当参数传递到Thread构造中 -> 这一步是让我们自己定义的类成为一个真正的线程类对象 4.调用Thread中的start方法,开启线程,jvm自动调用run方法 public class Test01 { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread t1 = new Thread(myRunnable); //调用Thread中的start方法,开启线程 t1.start(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } }public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } }6.两种实现多线程的方式区别1.继承Thread:继承只支持单继承,有继承的局限性 2.实现Runnable:没有继承的局限性, MyThread extends Fu implements Runnable7.第三种方式_匿名内部类创建多线程严格意义上来说,匿名内部类方式不属于创建多线程方式其中之一,因为匿名内部类形式建立在实现Runnable接口的基础上完成的匿名内部类回顾: 1.new 接口/抽象类(){ 重写方法 }.重写的方法(); 2.接口名/类名 对象名 = new 接口/抽象类(){ 重写方法 } 对象名.重写的方法();public class Test02 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } },"阿庆").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } },"金莲").start(); } } 第三章.线程安全1.什么时候发生:当多个线程访问同一个资源时,导致了数据有问题1.线程安全问题-->线程不安全的代码public class MyTicket implements Runnable{ //定义100张票 int ticket = 100; @Override public void run() { while(true){ if (ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } } public class Test01 { public static void main(String[] args) { MyTicket myTicket = new MyTicket(); Thread t1 = new Thread(myTicket, "赵四"); Thread t2 = new Thread(myTicket, "刘能"); Thread t3 = new Thread(myTicket, "广坤"); t1.start(); t2.start(); t3.start(); } }原因:CPU在多个线程之间做高速切换导致的2.解决线程安全问题的第一种方式(使用同步代码块)1.格式: synchronized(任意对象){ 线程可能出现不安全的代码 } 2.任意对象:就是我们的锁对象 3.执行: 一个线程拿到锁之后,会进入到同步代码块中执行,在此期间,其他线程拿不到锁,就进不去同步代码块,需要在同步代码块外面等待排队,需要等着执行的线程执行完毕,出了同步代码块,相当于释放锁了,等待的线程才能抢到锁,才能进入到同步代码块中执行public class MyTicket implements Runnable{ //定义100张票 int ticket = 100; //任意new一个对象 Object obj = new Object(); @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (obj){ if (ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } } }public class Test01 { public static void main(String[] args) { MyTicket myTicket = new MyTicket(); Thread t1 = new Thread(myTicket, "赵四"); Thread t2 = new Thread(myTicket, "刘能"); Thread t3 = new Thread(myTicket, "广坤"); t1.start(); t2.start(); t3.start(); } }3.解决线程安全问题的第二种方式:同步方法3.1.普通同步方法_非静态1.格式: 修饰符 synchronized 返回值类型 方法名(参数){ 方法体 return 结果 } 2.默认锁:thispublic class MyTicket implements Runnable{ //定义100张票 int ticket = 100; @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } //method01(); method02(); } } public void method02(){ synchronized(this){ System.out.println(this+".........."); if (ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } } public class Test01 { public static void main(String[] args) { MyTicket myTicket = new MyTicket(); System.out.println(myTicket); Thread t1 = new Thread(myTicket, "赵四"); Thread t2 = new Thread(myTicket, "刘能"); Thread t3 = new Thread(myTicket, "广坤"); t1.start(); t2.start(); t3.start(); } } 3.2.静态同步方法1.格式: 修饰符 static synchronized 返回值类型 方法名(参数){ 方法体 return 结果 } 2.默认锁:class对象public class MyTicket implements Runnable{ //定义100张票 static int ticket = 100; @Override public void run() { while(true){ try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } //method01(); method02(); } } public static void method02(){ synchronized(MyTicket.class){ if (ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } }public class Test01 { public static void main(String[] args) { MyTicket myTicket = new MyTicket(); Thread t1 = new Thread(myTicket, "赵四"); Thread t2 = new Thread(myTicket, "刘能"); Thread t3 = new Thread(myTicket, "广坤"); t1.start(); t2.start(); t3.start(); } } 第四章.死锁(了解)1.死锁介绍(锁嵌套就有可能产生死锁)指的是两个或者两个以上的线程在执行的过程中由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况称之为死锁根据上图所示:线程1正在持有锁1,但是线程1必须再拿到锁2,才能继续执行 而线程2正在持有锁2,但是线程2需要再拿到锁1,才能继续执行 此时两个线程处于互相等待的状态,就是死锁,在程序中的死锁将出现在同步代码块的嵌套中2.死锁的分析3.代码实现public class LockA { public static LockA lockA = new LockA(); } public class LockB { public static LockB lockB = new LockB(); }public class DieLock implements Runnable{ private boolean flag; public DieLock(boolean flag) { this.flag = flag; } @Override public void run() { if (flag){ synchronized (LockA.lockA){ System.out.println("if...lockA"); synchronized (LockB.lockB){ System.out.println("if...lockB"); } } }else{ synchronized (LockB.lockB){ System.out.println("else...lockB"); synchronized (LockA.lockA){ System.out.println("else...lockA"); } } } } } public class Test01 { public static void main(String[] args) { DieLock dieLock1 = new DieLock(true); DieLock dieLock2 = new DieLock(false); new Thread(dieLock1).start(); new Thread(dieLock2).start(); } } 只需要知道死锁出现的原因即可(锁嵌套),以后尽量避免锁嵌套第五章.线程状态1.线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态: 这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。线程状态导致状态发生条件NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。Terminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。或者调用过时方法stop()
    • 1年前
    • 406
    • 1
    • 0
  • JDBC三种参数传入方式 第一种(三种参数)三种参数分别是URL,user,password//当数据库ip和端口为本机3306端口(localhost:3306/127.0.0.1:3306)时,可省略为“/” Connection connection = DriverManager.getConnection("jdbc:mysql:///example","root","123456");第二种(两种参数)两种参数分别是URL和外部文件Properties info = new Properties(); //这里模拟外部文件 info.put("user","root"); info.put("password","123456"); Connection connection = DriverManager.getConnection("jdbc:mysql:///example", info);第三种(一个参数)URLConnection connection = DriverManager.getConnection("jdbc:mysql:///example?user=root&password=123456");URL参数以下是MySQL 8及更新版本中用于URL的常见属性设置:属性描述user数据库连接的用户名。password连接数据库所需的密码。host数据库主机地址或IP地址。port数据库服务器监听的端口号。database要连接的数据库名称。sslmodeSSL连接模式,可能的值包括:'DISABLED'(禁用SSL)、'REQUIRED'(必须启用SSL)、'VERIFY_CA'(验证CA)、'VERIFY_IDENTITY'(验证身份)。sslcert客户端SSL证书文件的路径。sslkey客户端SSL私钥文件的路径。sslrootcert用于验证服务器证书的CA证书文件的路径。charset连接使用的字符集。connectTimeout连接超时时间,以秒为单位。maxRetryDelay在重新连接之前的最大延迟时间,以毫秒为单位。loginTimeout登录超时时间,以秒为单位。socketTimeout套接字超时时间,以毫秒为单位。allowPublicKeyRetrieval是否允许在安全连接期间从服务器检索公钥。useSSL是否使用SSL连接。allowLoadLocalInfile是否允许使用LOAD DATA LOCAL INFILE语句。serverSslCert服务器SSL证书文件的路径。serverSslKey服务器SSL私钥文件的路径。serverSslCaCert服务器SSL CA证书文件的路径。useAffectedRows是否使用受影响的行数作为UPDATE和DELETE查询的返回值。readOnly是否将连接设置为只读模式。allowPublicKeyRetrieval是否允许在安全连接期间从服务器检索公钥。useCursorFetch是否使用游标获取来提取大结果集。useCursorFetch是否使用游标获取来提取大结果集。socket指定连接到MySQL服务器的UNIX套接字文件的路径。以上是常用的一些属性设置,具体可以根据实际情况进行配置。
    • 1年前
    • 309
    • 0
    • 1
  • JDBC的基本使用 JDBC使用教程JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、特定网络和特定硬件平台的接口,用于执行SQL语句。以下是使用JDBC的基本步骤:1. 注册驱动要与数据库建立连接,首先需要注册数据库的驱动。这一步是告诉Java应用程序要连接的数据库类型。try{ // 加载数据库驱动类 //DriverManager.registerDriver(new Driver())这一步在驱动4+版本已不是必须的 //驱动版本8+:com.mysql.cj.jdbc.Driver //驱动版本5+:com.mysql.jdbc.Driver forName("com.mysql.cj.jdbc.Driver") ; } catch (ClassNotFoundException e) { e. printStackTrace(); }这里以MySQL为例,com.mysql.jdbc.Driver是MySQL的驱动类名称。2.获取连接注册驱动后,下一步是使用DriverManager获取数据库连接。String url = "jdbc:mysql://localhost:3306/数据库名?useSSL=false" ; String username = "用户名" ; String password = "密码" ; try{ Connection conn = DriverManager.getConnection (url, username, password) ; } catch (SQLException e){ e. printStackTrace(); }网址是数据库的URL,其中包含数据库的地址、端口号和数据库名。用户名和密码分别是数据库登录的用户名和密码。3.创建发送SQL语句的对象有了数据库连接后,需要创建一个用于发送SQL语句的对象。try (Connection conn = DriverManager.getConnection ( url ,username,password) ) {​ Statement stmt = conn.createStatement() ; } catch (SQLException e){ e.printStackTrace(); }Statement对象用于执行静态SQL语句并返回其生成的结果。4.发送SQL语句,获取返回结果通过Statement对象发送SQL语句结果,并接收查询。String sql = "SELECT * FROM 表名" ; try (Connection conn = DriverManager.getConnection (url ,username,password) ;​ Statement stmt = conn.createStatement()){ ResultSet rs = stmt.executeQuery(sql) ; // 处理结果集 } catch (SQLException e){ e.printStackTrace(); }5.结果集解析获取到结果集后,可以搜索结果集获取数据。try (Connection conn = DriverManager.getConnection (url ,username,password) ;​ Statement stmt = conn.createStatement( ) ; ResultSet rs = stmt.executeQuery(sql)){ while (rs.next()){ // 通过列名或列索引获取数据 String data = rs.getString("列名"); // 处理数据 } } catch (SQLException e) { e.printStackTrace(); }6. 资源关闭最后,确保打开的数据库资源被关闭,避免内存泄漏。try (Connection conn = DriverManager.getConnection (url ,username,password) ;​ Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)){ // 使用资源 // 手动关闭 //rs.close(); //stmt.close(); //conn.close() } catch ( SQLException e ) { e.printStackTrace(); } // 自动关闭资源,因为使用了try-with-resources语句 使用try-with-resources语句可以自动关闭资源,这是Java 7及以上版本提供的功能,可以自动管理资源的关闭。statement接口常用方法以下是 JDBC 中 Statement 接口常用的方法及其功能的详细解释:方法描述execute(String sql)在数据库中执行给定的 SQL 语句,可以是任何类型的 SQL 语句(例如,SELECT、INSERT、UPDATE、DELETE),返回一个 boolean 值,指示第一个结果是一个 ResultSet 对象还是更新计数或没有结果。executeQuery(String sql)在数据库中执行给定的 SELECT 语句,返回一个 ResultSet 对象,它包含满足查询条件的结果集。executeUpdate(String sql)在数据库中执行给定的 INSERT、UPDATE 或 DELETE 语句,或者是 SQL 数据定义语言(DDL)语句,例如 CREATE TABLE、ALTER TABLE、DROP TABLE 等。返回一个整数,表示受影响的行数。addBatch(String sql)将 SQL 语句添加到批处理中,以便一次性执行多个 SQL 语句。clearBatch()清除当前的批处理命令列表。executeBatch()执行当前的批处理中的所有 SQL 语句。close()关闭该 Statement 对象。getResultSet()获取当前结果集(如果有)。getUpdateCount()获取受上一条 SQL 语句影响的行数。getMoreResults()将当前结果集关闭,并将游标移动到下一个结果集(如果有)。getGeneratedKeys()获取由数据库自动生成的键,如果上一条 SQL 语句执行的是插入语句,并且指定了自动生成键的字段,则返回包含生成键的 ResultSet 对象。setFetchSize(int rows)设置获取的行数。setMaxRows(int max)设置最大的结果集行数限制。setQueryTimeout(int seconds)设置查询的超时时间(以秒为单位)。getFetchSize()获取获取的行数。getMaxRows()获取结果集的最大行数限制。getQueryTimeout()获取查询的超时时间(以秒为单位)。
    • 1年前
    • 250
    • 0
    • 1
  • 人生倒计时
    舔狗日记