JAVA|后端编码规范

目录

零、引言

一、基础

二、集合

三、并发

四、日志

五、安全


零、引言

规范等级:

  • 【强制】:强制遵守,来源于线上历史故障,将通过工具进行检查。
  • 【推荐】:推荐遵守,来源于日常代码审查、开发人员反馈和行业经验。

一、基础

序号

等级

规范

示例  

说明

1

强制

在POJO类中定义布尔类型成员变量时,禁止用is作变量名前缀。

反例:                                                                                                                                          

private boolean isDeleted;

// Getter方法:IDEA生成,与变量同名。

public boolean isDeleted() {

    return isDeleted;

}

// Setter方法:IDEA生成

public void setDeleted(boolean deleted) {

    isDeleted = deleted;

}

is作变量名前缀的布尔型成员变量,在一些IDE(如:IDEA)中,默认生成的getter方法与变量名相同,导致部分框架(如:Jackson、Fastjson)在反向解析时会引发“找不到指定成员变量名”的错误。
2

强制

在对象之间做相等比较时,应当使用Objects工具类(java.util.Objects)的equals方法。

正例:

String versionNo = "6.6.1";

// 如param为null,不会出现异常。

Objects.equals(param, versionNo);

反例:

String versionNo = "6.6.1";

// 如param为null,会出现异常。

param.equals(versionNo);

当对象为null时,直接调用equals会出现空指针异常。

注意:Objects的equals方法内部会利用参数对象的equals方法进行比较,对数组、集合之类的对象并不会做内部元素的一一比较。

3

强制

在BigDecimal之间做等值比较时,禁止使用equals方法。

正例:

// 商品原价格

BigDecimal skuPrice = new BigDecimal("120.00");

// 商品优惠后价格

BigDecimal skuPromoPrice = new BigDecimal("120.0");

// 使用compareTo做比较,不会比较精度。

if (skuPrice.compareTo(skuPromoPrice) == 0) { // true

    // ...

}

反例:

// 商品原价格

BigDecimal skuPrice = new BigDecimal("120.00");

// 商品优惠后价格

BigDecimal skuPromoPrice = new BigDecimal("120.0");

// 使用equals做比较,会比较精度。

if (skuPrice.equals(skuPromoPrice)) { // false

    // ...

}

BigDecimal的equals方法会比较精度,如1.0与1.00比较的结果为false,推荐使用其

compareTo方法做比较。

4强制在浮点数之间做等值比较时,基本类型禁止使用==,包装类型禁止使用equals。

正例:

// 下方减法运算是为了故意引起浮点误差

// 时段一的单价:充电站单价 - 优惠价

float timePrice1 = 1.0F - 0.9F;

// 时段二的单价:充电站单价 - 优惠价

float timePrice2 = 0.9F - 0.8F;

// 工具类推荐:org.apache.commons.lang3.math.NumberUtils

// 将浮点数转为BigDecimal并指定保留位数和取舍方式

BigDecimal decimalTimePrice1 = NumberUtils.toScaledBigDecimal(timePrice1, 2, RoundingMode.HALF_UP);

BigDecimal decimalTimePrice2 = NumberUtils.toScaledBigDecimal(timePrice2, 2, RoundingMode.HALF_UP);

if (decimalTimePrice1.compareTo(decimalTimePrice2) == 0) { // true

    // ...

}

反例:

// 下方减法运算是为了故意引起浮点误差

// 时段一的单价:充电站单价 - 优惠价

float timePrice1 = 1.0F - 0.9F;

// 时段二的单价:充电站单价 - 优惠价

float timePrice2 = 0.9F - 0.8F;

//  可能存在浮点误差,等式不成立。

if (timePrice1 == timePrice2) { // false

    // ...

}

反例:

// 下方减法运算是为了故意引起浮点误差

// 时段一的单价:充电站单价 - 优惠价

Float timePrice1 = 1.0F - 0.9F;

// 时段二的单价:充电站单价 - 优惠价

Float timePrice2 = 0.9F - 0.8F;

// 可能存在浮点误差,等式不成立。

if (timePrice1.equals(timePrice2)) { // false

    // ...

}

浮点数先转成BigDecimal,再用compareTo方法做比较,以避免精度问题影响结果。

浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数。

5

强制

将浮点数转换为BigDecimal 时,禁止直接使用构造方法。

正例:

// 商品SKU单价

double skuPrice = 0.1D;

// 工具类推荐:org.apache.commons.lang3.math.NumberUtils

// 精度不影响,仍然是0.1。

BigDecimal decimalSkuPrice = NumberUtils.createBigDecimal(Double.toString(skuPrice));

反例:

// 商品SKU单价

double skuPrice = 0.1D;

// 精度受影响,可能是0.1000000000000000055511151231257827021181583404541015625。

BigDecimal decimalSkuPrice = new BigDecimal(skuPrice);

BigDecimal的浮点数构造方法存在精度损失风险,在精确计算或值比较的场景中会导致业务逻辑异常。

注意:和上一条的正例不同的是,前者在经过浮点计算后已经形成了误差,在转换时用toScaledBigDecimal方法限定了精度。在此处,是为了避免因直接利用浮点数构造而导致的误差。

6强制在空指针异常易发的场景中,应当对对象做null判断。

正例:

@Resource

private UserService userService;

public String queryCityName(String userId) {

    User userObj = userService.queryUserInfo(userId);

    Optional<User> userData = Optional.ofNullable(userObj);

    Optional<UserAddress> userAddress = userData.map(User::getUserAddress);

    // 如用户地址为null,不会空指针异常。

    Optional<String> optionalCityName = userAddress.map(UserAddress::getCiyName);

    // null时返回默认值

    String cityName = optionalCityName.orElse(DEFAULT_CITY);

    return cityName;

}  

反例:

@Resource

private UserService userService;

public String queryCityName(String userId) {

    User userObj = userService.queryUserInfo(userId);

    // 如用户地址为null,会空指针异常。

    String cityName = userObj.getUserAddress().getCiyName();

    return cityName;

}

在对象未判断null的情况下直接引用,容易发生空指针异常,推荐用Optional类更优雅的处理null对象。

一些空指针异常(NPE)易发的场景:

  1. 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
  2. 数据库的查询结果可能为null
  3. 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
  4. 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
  5. 对于Session中获取的数据,建议进行NPE检查,避免空指针。
  6. 级联调用obj.getA().getB().getC(),一连串调用,易产生NPE。

7

强制

在日期格式化时,应当使用DateUtils工具类

正例

Calendar calendar = Calendar.getInstance();

// 日期是2023-12-31

calendar.set(20231131);

// strDateTime:2023-12-31

String strDateTime = DateUtils.datesToString(calendar.getTime());

反例

Calendar calendar = Calendar.getInstance();

// 日期是2023-12-31

calendar.set(20231131);

// 误用了大写Y格式化年份

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd");

// strDateTime:2024-12-31

String strDateTime = simpleDateFormat.format(calendar.getTime());

DateUtils包装了常用的日期格式,避免了手动格式化时的误用风险,如:年份格式化误书写成YYYY,如本周存在跨年的情况,返回的就是下一年。

8强制POJO类属性,禁止使用基本数据类型。

正例

@Resource

private UserRepository userRepository;

public Integer queryUserId(String phone) {

    // getUserIdByPhone返回的是Integer类型,当返回null时,不会出现空指针异常。

    Integer userId = userRepository.getUserIdByPhone(phone);

    // 可以做防御性校验,以避免直接空指针

    if(userId != null){

        // …

    }

    // …

}

反例:

@Resource

private UserRepository userRepository;

public int queryUserId(String phone) {

    //  getUserIdByPhone返回的是Integer类型,当返回null时,会出现空指针异常。

    int userId = userRepository.getUserIdByPhone(phone);

    // …

}

POJO类属性使用包装数据类型有如下优点:

  1. 若值为null时,能显式的提醒使用者做相应的处理,而不是使用基本数据类型的默认值。
  2. 避免对象之间的自动拆装箱时出现NPE。

二、集合

序号

等级

规范

示例

说明

1

强制

在需要对List的subList方法返回结果进行遍历、增加、删除元素时,禁止直接变更原List中的元素。

反例:

List<String> cityList = new ArrayList<>();

cityList.add("北京");

cityList.add("上海");

cityList.add("广州");

List<String> citySubList = cityList.subList(01);

// subList之后变更原List

cityList.add("深圳");

// 出现ConcurrentModificationException异常

for (int i = 0; i < citySubList.size(); i++) {

    // ...

}

List的subList方法返回的是一个List的内部类对象,它是List的一个视图,对原List所有的操作都会反映到这个对象上。

同时,对原List进行元素的增加或删除,会被计数(modCount,结构变更次数),此数值和原subList时记录的数值(expectedModCount)不一致,会引发ConcurrentModification

Exception异常。

2强制

对集合进行for循环时,禁止在循环体内用remove/add方法。

正例:

List<String> cityList = new ArrayList<>();

cityList.add("北京");

cityList.add("上海");

cityList.add("广州");

Iterator<String> iterator = cityList.iterator();

while (iterator.hasNext()) {

    String city = iterator.next();

    // 工具类推荐:org.apache.commons.lang3.StringUtils

    if (StringUtils.equals("广州", city)) {

        iterator.remove();

    }

}

反例:

List<String> cityList = new ArrayList<>();

cityList.add("北京");

cityList.add("上海");

cityList.add("广州");

// 出现ConcurrentModificationException异常

for (String city : cityList) {

    // 工具类推荐:org.apache.commons.lang3.StringUtils

    if (StringUtils.equals("广州", city)) {

        cityList.remove(city);

    }

}

若在for循环体内对集合元素进行remove/add操作,可能导致异常,建议使用iterator方式处理。

三、并发

序号

等级

规范

示例

说明

1

强制

动态线程池只允许使用通义管理平台定义的(比如Poseidon),禁止自行创建。

正例:

// poseidonThreadPool在Poseidon管理平台已创建

@Resource(name = "poseidonThreadPool")

private ThreadPoolExecutor poseidonThreadPool;

CompletableFuture.supplyAsync(() -> {

    // ...

}, poseidonThreadPool);

反例:

// 自建线程池,未接入Poseidon。

ThreadPoolExecutor customThreadPool = new ThreadPoolExecutor(corePoolSize,

                    maximumPoolSize,

                    keepAliveTime,

                    unit,

                    workQueue,

                    new ThreadPoolExecutor.AbortPolicy());

CompletableFuture.supplyAsync(() -> {

    // ...

}, customThreadPool);

通过Poseidon创建的线程池将能较好的进行管理和监控:

  1. 支持通过Poseidon动态修改线上线程池参数(线程数量、队列长度、拒绝策略),无需重启服务即可生效。
  2. CAT平台能对线程池状态进行监控和告警。
2强制

在使用线程池时,禁止将拒绝策略设置为DiscardPolicy。

反例:

// poseidonThreadPool在Poseidon管理平台已创建,且配置了DiscardPolicy拒绝策略。

@Resource(name = "poseidonThreadPool")

private ThreadPoolExecutor poseidonThreadPool;

public void process(){

    // ...

    // 队列满且已达到最大线程数后,任务会被自动抛弃。

    Future<String> resultFuture = poseidonThreadPool.submit(task);

    // ...

}

若配置了DiscardPolicy,当线程池队列排满且已达到了最大线程数后,新增任务会被直接丢弃,无任何提示,并且在结合future.get()运行时,存在阻塞的风险。

3强制

父子任务禁止使用同一个线程池。

反例:

class OrderManager {

    // Poseidon配置的线程池

    @Resource(name = "poseidonThreadPool"

    private ThreadPoolExecutor poseidonThreadPool;

    @Autowired

    private ProductManager productManager;

    public List<Order> queryOrders() throws Exception {

        // ...

        // 如果子任务出现阻塞,父任务一直等待。

        Future<Order> orderFuture = poseidonThreadPool.submit(() -> {

            // ...

            ProductInfo product = productManager.queryProducts(orderId);

            // ...

        });

        orderFuture.get();

        // ...

    }

}

class ProductManager {

    // Poseidon配置的线程池

    @Resource(name = "poseidonThreadPool")

    private ThreadPoolExecutor poseidonThreadPool;

    public List<ProductInfo> queryProducts(String orderId) throws Exception {

        // ...

        // 子任务存在多线程处理,且使用了同一个线程池。

        Future<ProductInfo> productFuture = poseidonThreadPool.submit(() -> {

            // ...

        });

        productFuture.get();

        // ...

    }

}

父子任务使用同一个线程池容易相互影响,线程数达上限时,子任务等待线程资源,而同时,父任务因子任务未完成,其资源得不到释放,最终可能导致相互等待或死锁。

4强制在多线程环境下,禁止直接使用HashMap。

正例:

// poseidonThreadPool在Poseidon管理平台已创建

@Resource(name = "poseidonThreadPool")

private ThreadPoolExecutor poseidonThreadPool;

public void process() {

     // ConcurrentHashMap是线程安全的

    Map<String, String> map = new ConcurrentHashMap<>();

    CompletableFuture.supplyAsync(() -> {

        // ...

        map.put("key1""value1");

        // ...

    }, poseidonThreadPool);

    CompletableFuture.supplyAsync(() -> {

        // ...

        map.put("key2""value2");

        // ...

    }, poseidonThreadPool);

}

反例:

// poseidonThreadPool在Poseidon管理平台已创建

@Resource(name = "poseidonThreadPool")

private ThreadPoolExecutor poseidonThreadPool;

public void process() {

    // HashMap是线程不安全的

    Map<String, String> map = new HashMap<>();

    CompletableFuture.supplyAsync(() -> {

        // ...

        map.put("key1""value1");

        // ...

    }, poseidonThreadPool);

    CompletableFuture.supplyAsync(() -> {

        // ...

       map.put("key2""value2");

        // ...

    }, poseidonThreadPool);

}

HashMap是线程不安全的,在容量不够进行resize时,可能因并发出现死链,导致CPU飙升。

四、日志

序号

等级

规范

示例

说明

1

强制

日志级别只允许使用ERROR、WARN、INFO、DEBUG。

正例:

// ...

// info打印业务关键信息

log.info("操作人id:{}", operatorUserId);

// ...

List<UserData> userDataList = userListService.selectUsers();

// debug打印开发调试信息

log.debug("开始执行时间:{}", System.currentTimeMillis());

userDataList.stream().forEach(userData -> {

    // ...

});

log.debug("结束执行时间:{}", System.currentTimeMillis());

  • ERROR:业务功能受损或无法完成预期操作,可能会造成线上故障需要预警并及时解决,否则该功能将无法正常运行。
  • WARN:异常符合预期的情况且业务不受损,不会出现线上故障,可根据实际情况选择性预警,解决时效要求不高,但需要关注。
  • INFO:用于记录系统运行过程或重要信息点,主要为故障定位、过程追溯、数据分析等提供辅助。
  • DEBUG:用于在测试或本地的非生产环境中使用,可以记录详细的信息,主要为了方便开发调试程序,在生产环境中禁止使用。
2

强制

业务受损或预期外的异常场景,应当打印ERROR日志。

正例:

try {

    // ...

    // 发送短信验证码

    loginService.sendVerificationCode(cellphone);

    // ...

catch (Exception ex) {

    // 预期外的系统错误,业务受损。

    log.error("异常场景:{}, 异常数据:{}""用户H5登录", userId, ex);

    // 异常处理

}

反例:

try {

    // ...

    // 发送短信验证码

    loginService.sendVerificationCode(cellphone);

    // ...

catch (Exception ex) {

    log.info("异常场景:{}, 异常数据:{}""用户H5登录", userId, ex);

    // 异常处理

}

ERROR日志用于描述异常不可控的场景,当该类异常发生的时候会给业务和系统带来伤害,需要第一时间告警并介入排查修复。

3强制业务不受损且预期内的异常场景,应当打印WARN日志。

正例:

public List<User> selectUserByHeight(Integer height) {

    // ...

    // 该校验偶发于用户录入出错的情况下,需观测是否频繁以指导进一步处理。

    // 如:可能上游系统BUG导致度量单位搞错

    if (height > 270) {

        log.warn("异常场景:{},身高:{} 不能大于270cm""用户H5注册",  height);

        // ...

    }

    // ...

}

WARN日志用于描述异常可控的场景,当该类异常发生的时候不会给业务和系统带来伤害,用于记录和观测,指导进一步处理。
4

推荐

打印日志时,建议使用占位符的方式拼装内容。

正例:

// 用{}填充

log.info("用户注册ID:{}", userId);

反例:

// 用+拼接

log.info("用户注册ID:" + userId);

”+“ 拼接会多次调用StringBuilder的append()方式,每一次append的时候会计算字符串的长度以及重新分配一次内存,对性能有一定的损耗。

此外,“+”拼接方式无论本条日志是否打印都会计算长度和分配内存,而占位符的方式仅在打印的时候才进行内存分配。

5

推荐

打印日志时,不建议使用JSON工具将对象转换成String。

正例

public class UserInfo {

    private String id;

    public String getUserName() throws Exception {

        throw new Exception();

    }

    @Override

    public String toString() {

        return "id:" + id;

    }

}

public void doSth (UserInfo user) {

    // ...

   if(user != null){

        // ...

        log.info("user = {}", user.toString());

    }

    // ...

}

反例:

public class UserInfo {

    // ...

    public String getUserName() throws Exception {

        throw new Exception();

    }

}

public void doSth(UserInfo user) {

    // 会抛出异常。

    log.info("user = {}", JSON.toJSONString(user));

    // ...

}

如果对象里某些get方法被覆写,存在抛出异常的风险,进而影响正常业务流程。

6

推荐

异常日志内容中应当包含三要素:异常场景、异常数据、异常堆栈。

正例:

try {

    // ...

catch (Exception ex) {

    // 日志内容里记录异常场景、异常数据、异常堆栈信息。

    log.error("异常场景:{}, 异常数据:{}""用户H5登录", userId, ex);

    // 异常处理

}

反例:

try {       

    // ...

catch (Exception ex) {

    // 打印日志不完整,没有打印异常堆栈信息、异常数据。

    log.error("系统异常");

    // 异常处理

}

异常日志内容应记录关键的信息(异常场景、异常数据、异常堆栈),为问题排查提供有效帮助,能更高效的处理线上故障。

三要素包含:

异常场景:出现异常的业务场景说明。

异常数据:出现异常的数据(比如下单场景,需要记录商品ID、用户ID等信息)。

异常堆栈:异常堆栈信息。

五、安全

序号

等级

规范

示例

说明

1强制

用户敏感数据禁止直接展示、禁止用Get方式提交。

反例:

@Resource

private UserService userService;

public BizResponse<List<UserDTO>> getAllUsers() {

    List<User> users = userService.selectAllUsers();

    List<UserDTO> userDTOs = new ArrayList<>();

    users.forEach(user -> {

        UserDTO userDTO = new UserDTO();

        // 手机号码此处没有脱敏,直接返回到前端。

        userDTO.setPhone(user.getPhone());

        // ...

        userDTOs.add(userDTO);

    });

    return BizResponse.success(userDTOs);

}

手机号、银行卡卡号、身份证、车牌、车架号等都属于用户敏感信息,不能直接展示。

脱敏方式:

  • 手机号保留前3位和后4位;
  • 身份证号保留前6位和后3位;
  • 银行卡前6位和后4位;
  • 车牌将视情况按照保留地区和流水号后2位;
  • 车架号将视情况按照保留后6位;

禁止用Get方式提交,这种方式在URL上带有敏感数据,将会在wan/lan日志中出现这些元数据。

2强制

用户输入的参数,禁止直接拼接到SQL访问数据库。

反例:

<!-- Mapper XML -->

<select id="findUser" resultType="com.tuhu.mysql.User">

    select * from userInfo where username = ${userName}

</select>

// Mapper接口

public UserInfo findUser(String userName)

用户输入的参数可能带有SQL片段,存在SQL注入的风险,需要使用参数绑定的技术来防范。

3强制未经许可,禁止外发公司任何程序代码。

反例:

为了交流学习,将公司代码上传至GitHub,以方便各方来阅读和讨论,导致APPID、API等信息泄漏。

程序代码属于公司资产,在未经许可的情况下不得以任何方式(邮件、IM软件、纸质打印等)向外传输或公开,包括但不仅限于:

  • 第三方代码托管平台(Github、Gitee等)
  • 第三方网盘(百度网盘、阿里网盘等)
  • 第三方网站(CSDN、博客园等)

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

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

相关文章

2025-05-21 Python深度学习5——数据读取

文章目录 1 数据准备2 Dataset2.1 自定义 Dataset2.2 使用示例 3 TensorBoard3.1 安装3.2 标量可视化&#xff08;Scalars&#xff09;3.3 图像可视化&#xff08;Images&#xff09;3.4 其他常用功能 4 transform4.1 ToTensor()4.2 Normalize()4.3 Resize()4.4 Compose()4.5 C…

5月21日学习笔记

MYSQL三层结构 表1 数据库DB1 表2 数据库管理系统 客户端命令终端&#xff08;Dos&#xff09; DBMS 数据库DB2 表1 表2 数据库………. Mysql数据库-表的本质仍然是文件 表的一行称之为一条记录->在java程序中一行记录往往使用对象表示 SQL语…

二十、面向对象底层逻辑-ServiceRegistry接口设计集成注册中心

一、服务治理的基石接口 在微服务架构中&#xff0c;服务实例的动态注册与发现是保证系统弹性的关键机制。Spring Cloud Commons模块通过ServiceRegistry与Registration接口定义了服务注册的标准化模型&#xff0c;为不同服务发现组件&#xff08;Eureka、Consul、Nacos等&…

DeepSeek:以开源之力,引领AI技术新风潮

在年春节&#xff0c;大语言模型DeepSeek如同一枚震撼弹&#xff0c;在全球范围内引发了轰动&#xff0c;成功“破圈”&#xff0c;将中国的人工智能&#xff08;AI&#xff09;技术成果推向了世界舞台。 开源策略&#xff1a;打破技术壁垒 在AI行业&#xff0c;OpenAI等巨头…

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取

1 简介 为了有效地利用雾状冰生长的物理现象&#xff0c;最近开发了一种优化算法——雾状优化算法&#xff08;RIME&#xff09;。它模拟硬雾状和软雾状过程&#xff0c;构建硬雾状穿刺和软雾状搜索机制。在本研究中&#xff0c;引入了一种增强版本&#xff0c;称为修改的RIME…

PyTorch可视化工具——使用Visdom进行深度学习可视化

文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…

Java使用Collections集合工具类

1、Collections 集合工具类 Java 中的 Collections 是一个非常有用的工具类&#xff0c;它提供了许多静态方法来操作或返回集合。这个类位于 java.util 包中&#xff0c;主要包含对集合进行操作的方法&#xff0c;比如排序、搜索、线程安全化等。 Java集合工具类的使用&#x…

Unity基础学习(五)Mono中的重要内容(1)延时函数

目录 一、Mono中的延时函数Invoke 1. Invoke作用&#xff1a;延迟指定时间后执行一次函数。API&#xff1a; 2. InvokeRepeating作用&#xff1a;延迟后开始重复执行函数。API&#xff1a; 3. CancelInvoke作用&#xff1a;停止所有延时函数&#xff0c;或停止指定函数的延时…

180KHz 60V 5A开关电流升压/升降压型DC-DC转换器XL4019升降压芯片

介绍 XL6019是一款专为升压、升降压设计的 单片集成电路&#xff08;升压和降压是由外围电路拓扑确定的&#xff09;&#xff0c;可工作在DC5V到40V输入电 压范围&#xff0c;低纹波&#xff0c;内置功率MOS。XL6019内 置固定频率振荡器与频率补偿电路&#xff0c;简化了电 路…

如何畅通需求收集渠道,获取用户反馈?

要畅通需求收集渠道、有效获取用户反馈&#xff0c;核心在于多样化反馈入口、闭环反馈机制、用户分层管理、反馈数据结构化分析等四个方面。其中&#xff0c;多样化反馈入口至关重要&#xff0c;不同用户有不同的沟通偏好&#xff0c;只有覆盖多个反馈路径&#xff0c;才能捕捉…

Python结合ollama和stramlit开发聊天机器人

Python结合ollama和stramlit开发聊天机器人 一、环境准备1、streamlit安装2、langchain安装3、ollama的安装 二、Ollama平台聊天机器人实现1、需求2、模型调用3、前端实现页面呈现代码实现 三、详细代码地址四、参考资源 一、环境准备 1、streamlit安装 # 通过 pip 安装 pip …

java jdbc执行Oracle sql文件

执行代码 import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager;import org.apache.ibatis.jdbc.ScriptRunner;public class ExecSqlFileController {pu…

[Java实战]Spring Boot整合MinIO:分布式文件存储与管理实战(三十)

[Java实战]Spring Boot整合MinIO&#xff1a;分布式文件存储与管理实战&#xff08;三十&#xff09; 一、MinIO简介与核心原理 MinIO 是一款高性能、开源的分布式对象存储系统&#xff0c;兼容 Amazon S3 API&#xff0c;适用于存储图片、视频、日志等非结构化数据。其核心特…

开发指南115-CSS中选择器关系

1、选择后代 可以用选择器1 选择器2&#xff08;中间用空格分隔&#xff09;来表达&#xff0c;也可以在大括号里通过包含关系来表达 举例 .a .b 举例.a { .b{} } 注意css本身并不支持嵌套的写法&#xff0c;是scss等提供的扩展能力。 2、选择直系后代 选择器1 > 选择器2&a…

创建型:抽象工厂模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 实现案例 3、优缺点分析 4、适用场景 1、核心思想 目的&#xff1a;统一管理相关对象的创建&#xff0c;确保产品兼容性。优先用于需要强约束产品兼容性的场景&#xff08;如UI主题、跨平台适配&#xff09; 概念&#…

乘最多水的容器 | 算法 | 给定一个整数数组。有n条垂线。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

在我们日常生活中&#xff0c;蓄水似乎是一个极为朴素的物理行为&#xff1a;两堵墙之间&#xff0c;注入水&#xff0c;看谁能装得更多。可如果换个角度&#xff0c;从算法的视角去看这个问题&#xff0c;它会变得怎样&#xff1f;你是否意识到&#xff0c;这样一个简单的问题…

无人机避障——深蓝学院浙大Ego-Planner规划部分

ESDF-free&#xff1a; 被这种类型的障碍物死死卡住的情况&#xff1a; 在一定范围内建立ESDF&#xff1a; Ego-Planner框架&#xff1a; 找到{p,v} pair&#xff1a; 【注意】&#xff1a;首先根据在障碍物内航迹上的点Q&#xff0c;以及与它相邻但不在障碍物内的两个点&#…

零基础设计模式——大纲汇总

零基础学设计模式 - 大纲 前言 本教程旨在帮助零基础的同学快速入门设计模式&#xff0c;理解其核心思想和应用场景。我们将通过清晰的讲解和简单的示例&#xff0c;逐步引导你掌握常用的设计模式。 第一部分&#xff1a;设计模式入门 什么是设计模式&#xff1f; 设计模式…

leetcode 92. Reverse Linked List II

题目描述 92. Reverse Linked List II 是第206题的进阶版206. Reverse Linked List 思路很简单&#xff0c;但一次性通过还是有点难度的。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(n…

CUDA的设备,流处理器(Streams),核,线程块(threadblock),线程,网格(‌gridDim),块(block)和多gpu设备同步数据概念

CUDA的设备,流处理器&#xff0c;核&#xff0c;线程块&#xff08;threadblock&#xff09;&#xff0c;线程&#xff0c;网格&#xff08;‌gridDim&#xff09;&#xff0c;块&#xff08;block&#xff09;和多gpu设备同步数据概念 CUDA的设备,流处理器&#xff0c;核&…