【好文鉴赏】优秀的后端应该有哪些开发习惯

news2024/11/17 13:46:31

关键词:[后端] [编程习惯] [代码风格]
原文链接:

  • https://juejin.cn/post/7072252275002966030
  • https://www.51cto.com/article/721104.html

在这里插入图片描述

这里涉及到的主要是 Java 语言,当然也有通用的一些习惯;

拆分合理的目录结构

受传统的 MVC 模式影响,传统做法大多是几个固定的文件夹 controller、service、mapper、entity,然后无限制添加,到最后你就会发现一个 service 文件夹下面有几十上百个 Service 类,根本没法分清业务模块。正确的做法是在写 Service 上层新建一个 modules 文件夹,在 moudles 文件夹下根据不同业务建立不同的包,在这些包下面写具体的 service、controller、entity、enums 包或者继续拆分;

在这里插入图片描述
等以后开发版本迭代,如果某个包可以继续拆领域就继续往下拆,可以很清楚的一览项目业务模块。后续拆微服务也简单;
ps:个人理解为把 功能模块化 转换为 业务模块化,但是又保证了单个业务下的功能模块化;随着整个系统不断扩张时,后者无论是在查询效率还是在扩展效率上都有显著提高;但应该要留意不同业务之间的交互;

封装方法形参

当你的方法形参过多时请封装一个对象出来… 下面是一个反面教材,谁特么教你这样写代码的!

public void updateCustomerDeviceAndInstallInfo(long customerId, String channelKey,
                   String androidId, String imei, String gaId,
                   String gcmPushToken, String instanceId) {}

写个对象出来并给它赋予相应的意义,使用对象传参清晰明了,也方便后期扩展和维护;

public class CustomerDeviceRequest {
    private Long customerId;
    private String channelKey;
    private String androidId;
    private String imei;
    private String gaId;
    private String gcmPushToken;
    private String instanceId;
}

public void updateCustomerDeviceAndInstallInfo(CustomerDeviceRequest cdr) {}

为什么要这么写?比如你这方法是用来查询的,万一以后加个查询条件是不是要修改方法?每次加每次都要改方法参数列表。封装个对象,以后无论加多少查询条件都只需要在对象里面加字段就行。而且关键是看起来代码也很舒服啊!

封装业务逻辑

如果你看过“屎山”你就会有深刻的感触,这特么一个方法能写几千行代码,还无任何规则可言…往往负责的人会说,这个业务太复杂,没有办法改善,实际上这都是懒的借口。不管业务再复杂,我们都能够用合理的设计、封装去提升代码可读性。下面贴两段高级开发(作者)写的代码

@Transactional
public ChildOrder submit(Long orderId, OrderSubmitRequest.Shop shop) {
    ChildOrder childOrder = this.generateOrder(shop);
    childOrder.setOrderId(orderId);
    //订单来源 APP/微信小程序
    childOrder.setSource(userService.getOrderSource());
    // 校验优惠券
    orderAdjustmentService.validate(shop.getOrderAdjustments());
    // 订单商品
    orderProductService.add(childOrder, shop);
    // 订单附件
    orderAnnexService.add(childOrder.getId(), shop.getOrderAnnexes());
    // 处理订单地址信息
    processAddress(childOrder, shop);
    // 最后插入订单
    childOrderMapper.insert(childOrder);
    this.updateSkuInventory(shop, childOrder);
    // 发送订单创建事件
    applicationEventPublisher.publishEvent(new ChildOrderCreatedEvent(this, shop, childOrder));
    return childOrder;
}
@Transactional
public void clearBills(Long customerId) {
    // 获取清算需要的账单、deposit等信息
    ClearContext context = getClearContext(customerId);
    // 校验金额合法
    checkAmount(context);
    // 判断是否可用优惠券,返回可抵扣金额
    CouponDeductibleResponse deductibleResponse = couponDeducted(context);
    // 清算所有账单
    DepositClearResponse response = clearBills(context);
    // 更新 l_pay_deposit
    lPayDepositService.clear(context.getDeposit(), response);
    // 发送还款对账消息
    repaymentService.sendVerifyBillMessage(customerId, context.getDeposit(), EventName.DEPOSIT_SUCCEED_FLOW_REMINDER);
    // 更新账户余额
    accountService.clear(context, response);
    // 处理清算的优惠券,被用掉或者解绑
    couponService.clear(deductibleResponse);
    // 保存券抵扣记录
    clearCouponDeductService.add(context, deductibleResponse);
}

这两段代码里面其实业务很复杂,内部估计保守干了五万件事情,但是不同水平的人写出来就完全不同,不得不赞一下这个注释,这个业务的拆分和方法的封装。一个大业务里面有多个小业务,不同的业务调用不同的 Service 方法即可,后续接手的人即使没有流程图等相关文档也能快速理解这里的业务,而很多初级开发写出来的业务方法就是上一行代码是 A 业务的,下一行代码是 B 业务的,在下面一行代码又是 A 业务的,业务调用之间还嵌套这一堆单元逻辑,显得非常混乱,代码还多;

ps: 把独立功能的代码抽象成方法,避免一个方法内镶嵌若干个功能/业务逻辑,很好的习惯

判断集合类型不为空的正确方式

很多人喜欢写这样的代码去判断集合

if (list == null || list.size() == 0) {
  return null;
}

当然你硬要这么写也没什么问题…但是不觉得难受么,现在框架中随便一个 jar 包都有集合工具类,比如 org.springframework.util.CollectionUtils等等;以后请这么写:

if (CollectionUtils.isEmpty(list) || CollectionUtils.isNotEmpty(list)) {
  return null;
}

ps: 两者功能应该一样,我自己一般用的前者,但看公司代码和一些项目都是倾向于后者

集合类型返回值不要 return null

当你的业务方法返回值是集合类型时,请不要返回 null,正确的操作是返回一个空集合。你看 Bybatis 的列表查询,如果没查询到元素返回的就是一个空集合,而不是 null。否则调用方得去做 NULL 判断,多数场景下对于对象也是如此;

映射数据库的属性尽量不要用基本类型

我们都知道 int/long 等基本数据类型作为成员变量默认值是 0。现在流行使用 mybatisplus 、mybatis 等 ORM 框架,在进行插入或者更新的时候很容易会带着默认值插入更新到数据库。我特么真想砍了之前的开发,重构的项目里面实体类里面全都是基本数据类型。当场裂开…

对多个判断条件进行封装

public void method(LoanAppEntity loanAppEntity, long operatorId) {
  if (LoanAppEntity.LoanAppStatus.OVERDUE != loanAppEntity.getStatus()
          && LoanAppEntity.LoanAppStatus.CURRENT != loanAppEntity.getStatus()
          && LoanAppEntity.LoanAppStatus.GRACE_PERIOD != loanAppEntity.getStatus()) {
    //...
    return;
  }

这段代码的可读性很差,这 if 里面谁知道干啥的?我们用面向对象的思想去给 loanApp 这个对象里面封装个方法不就行了么?

public void method(LoanAppEntity loan, long operatorId) {
  if (!loan.finished()) {
    //...
    return;
  }

LoanApp 这个类中封装一个方法,简单来说就是这个逻辑判断细节不该出现在业务方法中;

/**
 * 贷款单是否完成
 */
public boolean finished() {
  return LoanAppEntity.LoanAppStatus.OVERDUE != this.getStatus()
          && LoanAppEntity.LoanAppStatus.CURRENT != this.getStatus()
          && LoanAppEntity.LoanAppStatus.GRACE_PERIOD != this.getStatus();
}

控制方法复杂度

推荐一款 IDEA 插件 CodeMetrics ,它能显示出方法的复杂度,它是对方法中的表达式进行计算,布尔表达式,if/else 分支,循环等。

点击可以查看哪些代码增加了方法的复杂度,可以适当进行参考,毕竟我们通常写的是业务代码,在保证正常工作的前提下最重要的是要让别人能够快速看懂。当你的方法复杂度超过 10 就要考虑是否可以优化了;

使用 @ConfigurationProperties 代替 @Value

列举一下 @ConfigurationProperties 的好处

  • 在项目 application.yml 配置文件中按住 ctrl + 鼠标左键 点击配置属性可以快速导航到配置类。写配置时也能自动补全、联想到注释。需要额外引入一个依赖 org.springframework.boot:spring-boot-configuration-processor

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rOFl6HQW-1668648167363)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5621193de8ac40479d5b402da1801a0b~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)]

  • @ConfigurationProperties 支持 NACOS 配置自动刷新,使用 @Value 需要在 BEAN 上面使用 @RefreshScope 注解才能实现自动刷新;

  • @ConfigurationProperties 可以结合 Validation 校验,@NotNull@Length 等注解,如果配置校验没通过程序将启动不起来,及早的发现生产丢失配置等问题

  • @ConfigurationProperties 可以注入多个属性@Value 只能一个一个写

  • @ConfigurationProperties 可以支持复杂类型,无论嵌套多少层,都可以正确映射成对象

相比之下我不明白为什么那么多人不愿意接受新的东西,裂开…你可以看下所有的 springboot-starter 里面用的都是 @ConfigurationProperties 来接配置属性;

推荐使用 lombok

当然这是一个有争议的问题,我的习惯是使用它省去 getter、setter、toString 等等;

不要在 AService 调用 BMapper (有异议)

我们一定要遵循从 AService -> BService -> BMapper,如果每个 Service 都能直接调用其他的 Mapper,那特么还要其他 Service 干嘛?老项目还有从 Controller 调用 Mapper 的,把控制器当 Service 来处理了。。

ps: 理论上一个 Service 职责最好单一,尽量别耦合,不然你会发现当一个调用的 Service里的业务逻辑发生变化后不一定跟你现在要的是一个东西。这么干,那势必会出多层、递归调用;具体地说:" AService -> BService " ,也可能需要" BService -> AService ",这样就会造成循环依赖,虽然目前的版本 Spring 是默认解决循环依赖,对于低版本的 Spring 是有问题的,报循环依赖异常;// 一个评论区佬的看法是:Service 层不应该循环调用,不止 Service 层,同层间都不应该互相调用,上面说的情况可以通过加中间层 manager 解决;相当于提供一个仓储,里面包含所有的dao,对外还可以隐藏内部细节;// 不过说到底感觉都过度追求规范了,都能举出反例,看是规范和简单怎么抉择,出现了反例势必就要提供解决方法

会使用基础工具类,会写自定义工具类

少写基础工具类,因为你写的大部分基础点的工具类,在你无形中引入的 jar 包里面就有,String 的,Assert 断言的,IO 上传文件,拷贝流的,Bigdecimal 的等等。自己写容易错还要加载多余的类;

ps:自定义的一些必要的工具类还是要写,自己知道的有 jar 包直接拿来用的可以省略,当然这也是个积累的过程;我在视频上边学边敲项目时就发现很多工具类写的很巧妙

要不要包裹 OpenFeign 接口返回值

不使用 code、message 封装

搞不懂为什么那么多人喜欢把接口的返回值用 Response 包装起来…加个 code、message、success 字段,然后每次调用方就变成这样

CouponCommonResult bindResult = couponApi.useCoupon(request.getCustomerId(), order.getLoanId(), coupon.getCode());
if (Objects.isNull(bindResult) || !bindResult.getResult()) {
  throw new AppException(CouponErrorCode.ERR_REC_COUPON_USED_FAILED);
}

这样就相当于

  1. coupon-api 抛出异常
  2. coupon-api 拦截异常,修改 Response.code
  3. 在调用方判断 response.code 如果是 FAIELD 再把异常抛出去…

你直接在服务提供方抛异常不就行了么。。。而且这样一包装 HTTP 请求永远都是 200,没法做重试和监控。当然这个问题涉及到接口响应体该如何设计,目前网上大多是三种流派

  • 接口响应状态一律 200
  • 接口响应状态遵从 HTTP 真实状态
  • 佛系开发,领导怎么说就怎么做

不接受反驳,我推荐使用 HTTP 标准状态。特定场景包括参数校验失败等一律使用 400 给前端弹 toast

ps:作者说的有道理啊,对接口返回值进行封装虽然一定程度上提高了通用性,简洁性,emm 但确实存在上述问题,看怎么取舍吧,以后写出了可能有自己的见解 // 借评论区一个大佬:个人建议各自服务自行抛异常,微服务通过网关进行分发请求,在网关这一层拦截响应信息,给它包一层code\data\message再返回到前端,而服务间调用,直接报异常得啦,两方都兼容
当然,有些观点是”网关要轻量“这类的,当我没说

使用 code message data 封装

要记住 RESTfuljsonrpc 是两回事;就是因为以下原因固定 200 并且使用 code、message、data 来返回的

  1. 能够知道非 200 的错误一定不是来自程序,而是来自可能是 nginx 等,说明压根没进入到 Controller 里,方便排查;
  2. 服务间调用可以通过 code 来决定是否要捕捉、抛出、忽略这个错误,举个例子比如我要注册并登陆一个用户,下层服务返回 USER_EXISTED_ALREADY,那我就继续走登陆接口了。
  3. OpenFeign 是可以自定义Decoder来自动拆掉范型的,比如你服务上的 Controller 是 Response<User> getById(),在写 FeignClient 时可以直接写 User getById()

写有意义的方法注释

这种注释你写出来是怕后面接手的人瞎么…你在后面写字段参数的意义啊…

/**
* 请求电话验证
*
* @param credentialNum
* @param callback
* @param param
* @return phoneVerifyResult
*/

public void method(String credentialNum,String callback,String param,String phoneVerifyResult){

}

要么就别写,要么就在后面加上描述…写这样的注释被 IDEA 报一堆警告看着不难受?

和前端交互的 DTO 对象命名

什么 VO、BO、DTO、PO 我倒真是觉得没有那么大必要分那么详细,至少我们在和前端交互的时候类名要起的合适,不要直接用映射数据库的类返回给前端,这会返回很多不必要的信息,如果有敏感信息还要特殊处理

推荐的做法是接受前端请求的类定义为 XxxRequest,响应的定义为 XxxResponse。以订单为例:接受保存更新订单信息的实体类可以定义为 OrderRequest,订单查询响应定义为 OrderResponse,订单的查询条件请求定义为 OrderQueryRequest

不要跨服务循环访问数据库

跨服务查询时,如果有批量数据查询的场景,直接写一个批量的 Feign 查询接口,不要像下面这样

list.foreach(id -> {
    UserResponse user = userClient.findById(id);
});

因为每一次 OpenFeign 的请求都是一个 Http 请求、一个数据库 IO 操作,还要经过各种框架内的拦截器、解码器等等,全都是损耗。

直接定义一个批量查询接口

@PostMapping("/user/batch-info")
List<UserResponse> batchInfo(@RequestBody List<Long> userIds);

这就结束了吗?并没有,如果你遇到这种 userIds 的数量非常大,在 2000 以上,那么你在实现方不能在数据库中直接用 in() 去查询。在实现方要拆分这个 useIds 。有索引的情况下 in() 1000 个元素以下通常问题不大

public List<XxxResponse> list(List<Long> userIds) {
  List<List<Long>> partition = Lists.partition(userIds, 500); //拆分 List

  List<XxxResponse> list = new ArrayList<>();
  partition.forEach(item -> list.addAll(xxxMapper.list(item)));
  return list;
}

尽量别让 IDEA 报警

我是很反感看到 IDEA 代码窗口一串警告的,非常难受。因为有警告就代表代码还可以优化,或者说存在问题。 前几天捕捉了一个团队内部的小 bug,其实本来和我没有关系,但是同事都在一头雾水的看外面的业务判断为什么走的分支不对,我一眼就扫到了问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qocS6hvj-1668648167364)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5d59b8e61f5841eaa8036be98f4174e1~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)]

因为 java 中整数字面量都是 int 类型,到集合中就变成了 Integer,然后 stepId 点上去一看是 long 类型,在集合中就是 Long,那这个 contains 妥妥的返回 false,都不是一个类型。

你看如果注重到警告,鼠标移过去看一眼提示就清楚了,少了一个生产 bug。

ps: 确实,强迫症是一方面,时常提供有价值的信息也是一方面;以前在实习的时候找一个bug,忽视了 IDEA 的警告,硬是没找到地方,结果吃个饭回来一个警告信息就是出错的地方

保持优化性能的嗅觉

比如避免创建比必要的对象、异步处理、使用缓冲流,减少IO操作等等。

比如,我们设计一个APP首页的接口,它需要查用户信息、需要查banner信息、需要查弹窗信息等等。假设耗时如下:

图片

查用户信息200ms,查banner信息100ms、查弹窗信息50ms,那一共就耗时350ms了。如果还查其他信息,那耗时就更大了。如何优化它呢?可以并行发起,耗时可以降为200ms。如下:

图片

可变参数的配置化处理

日常开发中,我们经常会遇到一些可变参数,比如用户多少天没登录注销、运营活动,不同节日红包皮肤切换、订单多久没付款就删除等等。对于这些可变的参数,不用该直接写死在代码。优秀的后端,要做配置化处理,你可以把这些可变参数,放到数据库一个配置表里面,也可以放到项目的配置文件或者 apollo 上。

比如产品经理提了个红包需求,圣诞节的时候,红包皮肤为圣诞节相关的,春节的时候,为春节红包皮肤等。如果在代码写死控制,可有类似以下代码:

if(duringChristmas){
   img = redPacketChristmasSkin;
}else if(duringSpringFestival){
   img =  redSpringFestivalSkin;
}

如果到了元宵节的时候,运营小姐姐突然又有想法,红包皮肤换成灯笼相关的,这时候,是不是要去修改代码了,重新发布了?

从一开始接口设计时,可以实现一张红包皮肤的配置表,将红包皮肤做成配置化呢?更换红包皮肤,只需修改一下表数据就好了。当然,还有一些场景适合一些配置化的参数:一个分页多少数量控制、某个抢红包多久时间过期这些,都可以搞到参数配置化表里面。这也是扩展性思想的一种体现。

尽可能使用新技术组件

我觉得这是一个程序员应该具备的素养…反正我是喜欢用新的技术组件,因为新的技术组件出现必定是解决旧技术组件的不足,而且作为一个技术人员我们应该要与时俱进~~ 当然前提是要做好准备工作,不能无脑升级。举个最简单的例子,Java 17 都出来了,新项目现在还有人用 Date 来处理日期时间…

总结

节的时候,运营小姐姐突然又有想法,红包皮肤换成灯笼相关的,这时候,是不是要去修改代码了,重新发布了?

从一开始接口设计时,可以实现一张红包皮肤的配置表,将红包皮肤做成配置化呢?更换红包皮肤,只需修改一下表数据就好了。当然,还有一些场景适合一些配置化的参数:一个分页多少数量控制、某个抢红包多久时间过期这些,都可以搞到参数配置化表里面。这也是扩展性思想的一种体现。

尽可能使用新技术组件

我觉得这是一个程序员应该具备的素养…反正我是喜欢用新的技术组件,因为新的技术组件出现必定是解决旧技术组件的不足,而且作为一个技术人员我们应该要与时俱进~~ 当然前提是要做好准备工作,不能无脑升级。举个最简单的例子,Java 17 都出来了,新项目现在还有人用 Date 来处理日期时间…

总结

养成一些好的开发习惯肯定是需要通过不断的实践来沉淀下来的,但若事先有这个意识,在实践的过程中就会多留心,多注重代码的规范性、可读性、可扩展性等等;实习的时候便是对两个 Demo 进行重构,对代码的质量问题深有感触;

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

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

相关文章

Spring Cloud网关路由谓词

目录 一、前言 1、注意事项 2、词汇表 3、工作原理 二、配置路由谓词工厂 1、快捷方式 1.1、快捷方式配置 1.2、完全展开的参数 2、路由谓词工厂 2.1、路由谓词工厂之后 2.2、路线谓词工厂之前 2.3、路由谓词工厂之间 3、Cookie路线谓词工厂 4、标头路由谓词工厂 …

Rust机器学习之Polars

Rust机器学习之Polars 本文将带领大家学习Polars的基础用法&#xff0c;通过数据加载→\rarr→数据探索→\rarr→数据清洗→\rarr→数据操作一整个完整数据处理闭环&#xff0c;让大家学会如何用Polars代替Pandas进行数据处理。 本文是“Rust替代Python进行机器学习”系列文…

Golang入门笔记(5)—— 流程控制之switch分支

switch 有切换&#xff0c;开关的意思&#xff0c;我想这也许就是代码上的意义&#xff0c;通过表达式计算出一个值&#xff0c;然后进行状态的匹配和然后进行流程上的切换。 基本语法&#xff1a; switch 表达式 {case 值a1,值a2, ...语句块case 值b1&#xff0c;值b2, ...语…

[附源码]计算机毕业设计JAVA高校资源共享平台

[附源码]计算机毕业设计JAVA高校资源共享平台 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

IOT云平台 simple(4)springboot netty实现简单的mqtt broker

常见的开源mqttBroker很多&#xff0c;如&#xff1a; Mosquitto、emqx&#xff1b; 这里简单的介绍了mqtt&#xff0c;然后利用springboot netty实现了简单的mqtt Broker。 mqtt Broker&#xff1a;springboot netty实现&#xff1b; mqtt client&#xff1a;MQTT.fx工具软件…

Java Tomcat内存马——filter内存马

目录 前言&#xff1a; &#xff08;一) 内存马简介 0X01 原理 0X02 内存马类型 2.1 servlet-api类 2.2 spring类 2.3 Java Instrumentation类 &#xff08;二&#xff09; filter 内存马 &#xff08;三&#xff09;Tomcat Filter 流程分析 0x01 项目搭建 0x02 在访…

【Spring】——5、@Lazy懒加载

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

【BOOST C++ 12 函数式编程】(5) Boost.Lambda

一、说明Boost.Lambda 在 C11 之前&#xff0c;您需要使用像 Boost.Lambda 这样的库来利用 lambda 函数。从 C11 开始&#xff0c;这个库可以被视为已弃用&#xff0c;因为 lambda 函数现在是编程语言的一部分。如果您在不支持 C11 的开发环境中工作&#xff0c;您应该在转向 B…

大空间享大智慧 奇瑞新能源奇瑞大蚂蚁

在亲子消费市场上家庭消费已经成为了主力军。亲子消费的重心已经从以饮食、服装为主向教育、游乐等方向多元化发展。而在出行方面汽车的品质与驾乘感受也是如今消费者选择的主要需求。所以实惠、安全、环保的新能源大空间SUV成为了越来越多二胎、三胎家庭的最终选择。奇瑞新能源…

线程池使用

转载&#xff1a;线程池详解&#xff08;通俗易懂超级好&#xff09;_拉格朗日(Lagrange)的博客-CSDN博客_线程池 目录 基本概念 什么是线程池 线程池优点 线程池源码 ThreadPoolExecutor 参数解释 具体使用 线程池的工作原理 线程池的参数 任务队列&#xff08;w…

元数据管理-解决方案调研一:元数据概述

一、元数据概述 1.1、定义 元数据定义&#xff1a;描述数据的数据&#xff0c;对数据及信息资源的描述性信息。小编认为元数据不仅仅是关于数据的数据&#xff0c;它还是一种上下文&#xff0c;赋予信息更加丰富的身份。 以图片为例&#xff0c;其图片本身是一种数据&#xf…

操作系统的内存究竟是怎么一回事?

摘要&#xff1a;操作系统的内存究竟是怎么一回事&#xff1f;带你完整复习一遍《操作系统》一书中有关内存的所有知识点本文分享自华为云社区《操作系统的内存究竟是怎么一回事&#xff1f;带你完整复习一遍《操作系统》一书中有关内存的所有知识点》&#xff0c;作者&#xf…

【图神经网络】使用DGL框架实现简单图分类任务

使用DGL框架实现简单图分类任务简单图分类任务实现过程打包一个图的小批量定义图分类器图卷积读出和分类准备和训练核心代码参考资料图分类&#xff08;预测图的标签&#xff09;是图结构数据里一类重要的问题。它的应用广泛&#xff0c;可见于生物信息学、化学信息学、社交网络…

aws xray ec2环境搭建和基础用法

参考资料 https://docs.amazonaws.cn/en_us/xray/latest/devguide/xray-daemon.html https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/ https://github.com/aws/aws-xray-sdk-node https://docs.aws.amazon.com/xray-sdk-for-python/latest/reference/ba…

联想集团:长期前景稳定,业务转型正在提高盈利能力

来源;猛兽财经 作者&#xff1a;猛兽财经 由疫情驱动的个人电脑需求正在减弱 在经历了两年的个人电脑销售强劲增长之后&#xff0c;随着全球对疫情封锁限制的放松&#xff0c;由疫情引发的远程工作和在线学习趋势带来的全球个人电脑需求正在减弱。根据IDC的数据&#xff0c;20…

文件之间的拷贝(拷贝图片实例)java.io.FileNotFoundException: G:\dad (拒绝访问。)通过绝对路径获取各种文件名

1.报错解决 :java.io.FileNotFoundException: G:\dad (拒绝访问。) 参考文献:(364条消息) java.io.FileNotFoundException:(拒接访问&#xff09;_corelone2的博客-CSDN博客_java.io.filenotfoundexception 2.code 代码参考地址:(364条消息) java中文件拷贝的几种方式_babar…

深入理解New操作符

前言 当我们对函数进行实例化时&#xff0c;需要用new操作符来实现。那么&#xff0c;对于它的底层实现原理你是否清楚呢&#xff1f;本文就跟大家分享下它的原理并用一个函数来模拟实现它&#xff0c;欢迎各位感兴趣的开发者阅读本文。 原理分析 我们通过一个具体的例子来看…

MySQL——数据库基础

文章目录什么叫做数据库&#xff1f;主流数据库基本使用服务器、数据库、表之间的关系MySQL逻辑结构MySQL架构MySQL分类存储引擎什么叫做数据库&#xff1f; 软件角度&#xff1a; 为用户或者用户程序提供更加方便的数据管理的软件&#xff0c;通过SQL语句进行&#xff01; 数…

【PostgreSQL-14版本snapshot的几点优化】

最近在分析PostgreSQL-14版本性能提升的时候&#xff0c;关注到了Snapshots的这一部分。发现在PostgreSQL-14版本&#xff0c;连续合入了好几个和Snapshots相关的patch。 并且&#xff0c;Andres Freund也通过这些改进显著减少了已确定的快照可扩展性瓶颈&#xff0c;从而改进了…

【C++】C/C++内存管理

众所周知&#xff0c;C/C没有内存&#xff08;垃圾&#xff09;回收机制&#xff0c;所以写C/C程序常常会面临内存泄漏等问题。这一节我们一起来学习C/C的内存管理机制&#xff0c;深入了解这套机制有利于我们之后写出更好的C/C程序。 在那些看不到太阳的日子里&#xff0c;别忘…