当前位置: 首页 > news >正文

开发中造成空指针的常见用法,如何避免

1. 前言

《手册》的第 7 页和 25 页有两段关于空指针的描述:

【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。

【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:

  1. 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。

反例:public int f () { return Integer 对象}, 如果为 null,自动解箱抛 NPE。

  1. 数据库的查询结果可能为 null。

  2. 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。

  3. 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。

  4. 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。

  5. 级联调用 obj.getA ().getB ().getC (); 一连串调用,易产生 NPE。

《手册》对空指针常见的原因和基本的避免空指针异常的方式给了介绍,非常有参考价值。

那么我们思考以下几个问题:

  • 如何学习 NullPointerException(简称为 NPE)?
  • 哪些用法可能造 NPE 相关的 BUG?
  • 在业务开发中作为接口提供者和使用者如何更有效地避免空指针呢?

2. 了解空指针

2.1 源码注释

前面介绍过源码是学习的一个重要途径,我们一起看看 NullPointerException 的源码:

/*** Thrown when an application attempts to use {@code null} in a* case where an object is required. These include:* <ul>* <li>Calling the instance method of a {@code null} object.* <li>Accessing or modifying the field of a {@code null} object.* <li>Taking the length of {@code null} as if it were an array.* <li>Accessing or modifying the slots of {@code null} as if it*     were an array.* <li>Throwing {@code null} as if it were a {@code Throwable}*     value.* </ul>* <p>* Applications should throw instances of this class to indicate* other illegal uses of the {@code null} object.** {@code NullPointerException} objects may be constructed by the* virtual machine as if {@linkplain Throwable#Throwable(String,* Throwable, boolean, boolean) suppression were disabled and/or the* stack trace was not writable}.** @author  unascribed* @since   JDK1.0*/
public
class NullPointerException extends RuntimeException {private static final long serialVersionUID = 5162710183389028792L;/*** Constructs a {@code NullPointerException} with no detail message.*/public NullPointerException() {super();}/*** Constructs a {@code NullPointerException} with the specified* detail message.** @param   s   the detail message.*/public NullPointerException(String s) {super(s);}
}

源码注释给出了非常详尽地解释:

空指针发生的原因是应用需要一个对象时却传入了 null,包含以下几种情况:

  1. 调用 null 对象的实例方法。
  2. 访问或者修改 null 对象的属性。
  3. 获取值为 null 的数组的长度。
  4. 访问或者修改值为 null 的二维数组的列时。
  5. 把 null 当做 Throwable 对象抛出时。

实际编写代码时,产生空指针的原因都是这些情况或者这些情况的变种。

《手册》中的另外一处描述

“集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。”

和第 4 条非常类似。

如《手册》中的:

“级联调用 obj.getA ().getB ().getC (); 一连串调用,易产生 NPE。”

和第 1 条很类似,因为每一层都可能得到 null

当遇到《手册》中和源码注释中所描述的这些场景时,要注意预防空指针。

另外通过读源码注释我们还得到了 “意外发现”,JVM 也可能会通过 Throwable#Throwable(String, Throwable, boolean, boolean) 构造函数来构造 NullPointerException 对象。

2.2 继承体系

通过源码可以看到 NPE 继承自 RuntimeException 我们可以通过 IDEA 的 “Java Class Diagram” 来查看类的继承体系。

图片描述
可以清晰地看到 NPE 继承自 RuntimeException ,另外我们选取 NoSuchFieldExceptionNoSuchFieldErrorNoClassDefFoundError ,可以看到 Throwable 的子类型包括 ErrorException, 其中 NPE 又是 Exception 的子类。

那么为什么 ExceptionError 有什么区别? Excption 又分为哪些类型呢?

我们可以分别去 java.lang.Exceptionjava.lang.Error 的源码注释中寻找答案。

通过 Exception 的源码注释我们了解到, Exception 分为两类一种是非受检异常(uncheked exceptions)即 java.lang.RuntimeException 以及其子类;而受检异常(checked exceptions)的抛出需要再普通函数或构造方法上通过 throws 声明。

通过 java.lang.Error 的源码注释我们了解到,Error 代表严重的问题,不应该被程序 try-catch。编译时异常检测时, Error 也被视为不可检异常(uncheked exceptions)。

大家可以在 IDEA 中分别查看 ExceptionError 的子类,了解自己开发中常遇到的异常都属于哪个分类。

我们还可以通过《JLS》2 第 11 章 Exceptions 对异常进行学习。

其中在异常的类型这里,讲到:

不可检异常( unchecked exception)包括运行时异常和 error 类。

可检异常( checked exception )不属于不可检异常的所有异常都是可检异常。除 RuntimeException 和其子类,以及 Error 类以及其子类外的其他 Throwable 的子类。

图片描述
还有更多关于异常的详细描述,,包括异常的原因、异步异常、异常的编译时检查等,大家可以自己进一步学习。

3. 空指针引发的血案

3.1 最常见的错误姿势

 @Testpublic void test() {Assertions.assertThrows(NullPointerException.class, () -> {List<UserDTO> users = new ArrayList<>();users.add(new UserDTO(1L, 3));users.add(new UserDTO(2L, null));users.add(new UserDTO(3L, 3));send(users);});}// 第 1 处private void send(List<UserDTO> users) {for (UserDTO userDto : users) {doSend(userDto);}}private static final Integer SOME_TYPE = 2;private void doSend(UserDTO userDTO) {String target = "default";// 第 2 处if (!userDTO.getType().equals(SOME_TYPE)) {target = getTarget(userDTO.getType());}System.out.println(String.format("userNo:%s, 发送到%s成功", userDTO, target));}private String getTarget(Integer type) {return type + "号基地";}

在第 1 处,如果集合为 null 则会抛空指针;

在第 2 处,如果 type 属性为 null 则会抛空指针异常,导致后续都发送失败。

大家看这个例子觉得很简单,看到输入的参数有 null 本能地就会考虑空指针问题,但是自己写代码时你并不知道上游是否会有 null

3. 2 无结果仍返回对象

实际开发中有些同学会有一些非常 “个性” 的写法。

为了避免空指针或避免检查到 null 参数抛异常,直接返回一个空参构造函数创建的对象。

类似下面的做法:

/*** 根据订单编号查询订单** @param orderNo 订单编号* @return 订单*/
public Order getByOrderNo(String orderNo) {if (StringUtils.isEmpty(orderNo)) {return new Order();}// 查询orderreturn doGetByOrderNo(orderNo);
}

由于常见的单个数据的查询接口,参数检查不符时会抛异常或者返回 null。 极少有上述的写法,因此调用方的惯例是判断结果不为 null 就使用其中的属性。

这个哥们这么写之后,上层判断返回值不为 null , 上层就放心大胆得调用实例函数,导致线上报空指针,就造成了线上 BUG。

3.3 新增 @NonNull 属性反序列化的 BUG

假如有一个订单更新的 RPC 接口,该接口有一个 OrderUpdateParam 参数,之前有两个属性一个是 id 一个是 name 。在某个需求时,新增了一个 extra 属性,且该字段一定不能为 null

采用 lombok 的 @NonNull 注解来避免空指针:

import lombok.Data;
import lombok.NonNull;import java.io.Serializable;@Data
public class OrderUpdateParam implements Serializable {private static final long serialVersionUID = 3240762365557530541L;private Long id;private String name;// 其它属性// 新增的属性@NonNullprivate String extra;
}

上线后导致没有使用最新 jar 包的服务对该接口的 RPC 调用报错。

我们来分析一下原因,在 IDEA 的 target - classes 目录下找到 DEMO 编译后的 class 文件,IDEA 会自动帮我们反编译:

public class OrderUpdateParam implements Serializable {private static final long serialVersionUID = 3240762365557530541L;private Long id;private String name;@NonNullprivate String extra;public OrderUpdateParam(@NonNull final String extra) {if (extra == null) {throw new NullPointerException("extra is marked non-null but is null");} else {this.extra = extra;}}@NonNullpublic String getExtra() {return this.extra;}public void setExtra(@NonNull final String extra) {if (extra == null) {throw new NullPointerException("extra is marked non-null but is null");} else {this.extra = extra;}}// 其他代码}

我们还可以使用反编译工具:JD-GUI 对编译后的 class 文件进行反编译,查看源码。

由于调用方调用的是不含 extra 属性的 jar 包,并且序列化编号是一致的,反序列化时会抛出 NPE。

Caused by: java.lang.NullPointerException: extra​        at com.xxx.OrderUpdateParam.<init>(OrderUpdateParam.java:21)

RPC 参数新增 lombok 的 @NonNull 注解时,要考虑调用方是否及时更新 jar 包,避免出现空指针。

3.4 自动拆箱导致空指针

前面章节讲到了对象转换,如果我们下面的 GoodCreateDTO 是我们自己服务的对象, 而 GoodCreateParam 是我们调用服务的参数对象。

@Data
public class GoodCreateDTO {private String title;private Long price;private Long count;
}@Data
public class GoodCreateParam implements Serializable {private static final long serialVersionUID = -560222124628416274L;private String title;private long price;private long count;
}

其中 GoodCreateDTOcount 属性在我们系统中是非必传参数,本系统可能为 null

如果我们没有拉取源码的习惯,直接通过前面的转换工具类去转换。

我们潜意识会认为外部接口的对象类型也都是包装类型,这时候很容易因为转换出现 NPE 而导致线上 BUG。

public class GoodCreateConverter {public static GoodCreateParam convertToParam(GoodCreateDTO goodCreateDTO) {if (goodCreateDTO == null) {return null;}GoodCreateParam goodCreateParam = new GoodCreateParam();goodCreateParam.setTitle(goodCreateDTO.getTitle());goodCreateParam.setPrice(goodCreateDTO.getPrice());goodCreateParam.setCount(goodCreateDTO.getCount());return goodCreateParam;}
}

当转换器执行到 goodCreateParam.setCount(goodCreateDTO.getCount()); 会自动拆箱会报空指针。

GoodCreateDTOcount 属性为 null 时,自动拆箱将报空指针。

再看一个花样踩坑的例子

我们作为使用方调用如下的二方服务接口:

public Boolean someRemoteCall();

然后自以为对方肯定会返回 TRUEFALSE,然后直接拿来作为判断条件或者转为基本类型,如果返回的是 null,则会报空指针异常:

if (someRemoteCall()) {// 业务代码}

大家看示例的时候可能认为这种情况很简单,自己开发的时候肯定会注意,但是往往事实并非如此。

希望大家可以掌握常见的可能发生空指针场景,在开发是注意预防。

3.5 分批调用合并结果时空指针

大家再看下面这个经典的例子。

因为某些批量查询的二方接口在数据较大时容易超时,因此可以分为小批次调用。

下面封装一个将 List 数据拆分成每 size 个一批数据,去调用 function RPC 接口,然后将结果合并。

  public static <T, V> List<V> partitionCallList(List<T> dataList, int size, Function<List<T>, List<V>> function) {if (CollectionUtils.isEmpty(dataList)) {return new ArrayList<>(0);}Preconditions.checkArgument(size > 0, "size 必须大于0");return Lists.partition(dataList, size).stream().map(function).reduce(new ArrayList<>(),(resultList1, resultList2) -> {resultList1.addAll(resultList2);return resultList1;});}

看着挺对,没啥问题,其实则不然。

设想一下,如果某一个批次请求无数据,不是返回空集合而是 null,会怎样?

很不幸,又一个空指针异常向你飞来 …

此时要根据具体业务场景来判断如何处理这里可能产生的空指针异常

如果在某个场景中,返回值为 null 是一定不允许的行为,可以在 function 函数中对结果进行检查,如果结果为 null,可抛异常。

如果是允许的,在调用 map 后,可以过滤 null :

// 省略前面代码
.map(function)
.filter(Objects::nonNull)
// 省略后续代码

4. 预防空指针的一些方法

NPE 造成的线上 BUG 还有很多种形式,如何预防空指针很重要。

下面将介绍几种预防 NPE 的一些常见方法:

图片描述

4.1 接口提供者角度

4.1.1 返回空集合

如果参数不符合要求直接返回空集合,底层的函数也使用一致的方式:

public List<Order> getByOrderName(String name) {if (StringUtils.isNotEmpty(name)) {return doGetByOrderName(name);}return Collections.emptyList();
}

4.1.2 使用 Optional

Optional 是 Java 8 引入的特性,返回一个 Optional 则明确告诉使用者结果可能为空:

public Optional<Order> getByOrderId(Long orderId) {return Optional.ofNullable(doGetByOrderId(orderId));
}

如果大家感兴趣可以进入 Optional 的源码,结合前面介绍的 codota 工具进行深入学习,也可以结合《Java 8 实战》的相关章节进行学习。

4.1.3 使用空对象设计模式

该设计模式为了解决 NPE 产生原因的第 1 条 “调用 null 对象的实例方法”。

在编写业务代码时为了避免 NPE 经常需要先判空再执行实例方法:

public void doSomeOperation(Operation operation) {int a = 5;int b = 6;if (operation != null) {operation.execute(a, b);}
}

《设计模式之禅》(第二版)554 页在拓展篇讲述了 “空对象模式”。

可以构造一个 NullXXX 类拓展自某个接口, 这样这个接口需要为 null 时,直接返回该对象即可:

public class NullOperation implements Operation {@Overridepublic void execute(int a, int b) {// do nothing}
}

这样上面的判空操作就不再有必要, 因为我们在需要出现 null 的地方都统一返回 NullOperation,而且对应的对象方法都是有的:

public void doSomeOperation(Operation operation) {int a = 5;int b = 6;operation.execute(a, b);
}

4.2 接口使用者角度

讲完了接口的编写者该怎么做,我们讲讲接口的使用者该如何避免 NPE

4.2.1 null 检查

正如《代码简洁之道》第 7.8 节 “别传 null 值” 中所要表达的意义:

可以进行参数检查,对不满足的条件抛出异常。

直接在使用前对不能为 null 的和不满足业务要求的条件进行检查,是一种最简单最常见的做法。

通过防御性参数检测,可以极大降低出错的概率,提高程序的健壮性:

    @Overridepublic void updateOrder(OrderUpdateParam orderUpdateParam) {checkUpdateParam(orderUpdateParam);doUpdate(orderUpdateParam);}private void checkUpdateParam(OrderUpdateParam orderUpdateParam) {if (orderUpdateParam == null) {throw new IllegalArgumentException("参数不能为空");}Long id = orderUpdateParam.getId();String name = orderUpdateParam.getName();if (id == null) {throw new IllegalArgumentException("id不能为空");}if (name == null) {throw new IllegalArgumentException("name不能为空");}}

JDK 和各种开源框架中可以找到很多这种模式,java.util.concurrent.ThreadPoolExecutor#execute 就是采用这种模式。

public void execute(Runnable command) {if (command == null)throw new NullPointerException();// 其他代码}

以及 org.springframework.context.support.AbstractApplicationContext#assertBeanFactoryActive

protected void assertBeanFactoryActive() {if (!this.active.get()) {if (this.closed.get()) {throw new IllegalStateException(getDisplayName() + " has been closed already");}else {throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");}}
}

4.2.2 使用 Objects

可以使用 Java 7 引入的 Objects 类,来简化判空抛出空指针的代码。

使用方法如下:

private void checkUpdateParam2(OrderUpdateParam orderUpdateParam) {Objects.requireNonNull(orderUpdateParam);Objects.requireNonNull(orderUpdateParam.getId());Objects.requireNonNull(orderUpdateParam.getName());
}

原理很简单,我们看下源码;

public static <T> T requireNonNull(T obj) {if (obj == null)throw new NullPointerException();return obj;
}

4.2.3 使用 commons 包

我们可以使用 commons-lang3 或者 commons-collections4 等常用的工具类辅助我们判空。

4.2.3.1 使用字符串工具类:org.apache.commons.lang3.StringUtils

public void doSomething(String param) {if (StringUtils.isNotEmpty(param)) {// 使用param参数}
}

4.2.3.2 使用校验工具类:org.apache.commons.lang3.Validate

public static void doSomething(Object param) {Validate.notNull(param,"param must not null");
}
public static void doSomething2(List<String> parms) {Validate.notEmpty(parms);
}

该校验工具类支持多种类型的校验,支持自定义提示文本等。

前面已经介绍了读源码是最好的学习方式之一,这里我们看下底层的源码:

public static <T extends Collection<?>> T notEmpty(final T collection, final String message, final Object... values) {if (collection == null) {throw new NullPointerException(String.format(message, values));}if (collection.isEmpty()) {throw new IllegalArgumentException(String.format(message, values));}return collection;
}

该如果集合对象为 null 则会抛空 NullPointerException 如果集合为空则抛出 IllegalArgumentException

通过源码我们还可以了解到更多的校验函数。

4.2.4 使用集合工具类:org.apache.commons.collections4.CollectionUtils

public void doSomething(List<String> params) {if (CollectionUtils.isNotEmpty(params)) {// 使用params}
}

4.2.5 使用 guava 包

可以使用 guava 包的 com.google.common.base.Preconditions 前置条件检测类。

同样看源码,源码给出了一个范例。原始代码如下:

public static double sqrt(double value) {if (value < 0) {throw new IllegalArgumentException("input is negative: " + value);}// calculate square root
}

使用 Preconditions 后,代码可以简化为:

 public static double sqrt(double value) {checkArgument(value >= 0, "input is negative: %s", value);// calculate square root}

Spring 源码里很多地方可以找到类似的用法,下面是其中一个例子:

org.springframework.context.annotation.AnnotationConfigApplicationContext#register

public void register(Class<?>... annotatedClasses) {Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");this.reader.register(annotatedClasses);
}

org.springframework.util.Assert#notEmpty(java.lang.Object[], java.lang.String)

public static void notEmpty(Object[] array, String message) {if (ObjectUtils.isEmpty(array)) {throw new IllegalArgumentException(message);}
}

虽然使用的具体工具类不一样,核心的思想都是一致的。

4.2.6 自动化 API

4.2.6.1 使用 lombok 的 @Nonnull 注解

 public void doSomething5(@NonNull String param) {// 使用paramproccess(param);}

查看编译后的代码:

 public void doSomething5(@NonNull String param) {if (param == null) {throw new NullPointerException("param is marked non-null but is null");} else {this.proccess(param);}}

4.2.6.2 使用 IntelliJ IDEA 提供的 @NotNull 和 @Nullable 注解

maven 依赖如下:

<!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
<dependency><groupId>org.jetbrains</groupId><artifactId>annotations</artifactId><version>17.0.0</version>
</dependency>

@NotNull 在参数上的用法和上面的例子非常相似。

public static void doSomething(@NotNull String param) {// 使用paramproccess(param);
}

我们可以去该注解的源码 org.jetbrains.annotations.NotNull#exception 里查看更多细节,大家也可以使用 IDEA 插件或者前面介绍的 JD-GUI 来查看编译后的 class 文件,去了解 @NotNull 注解的作用。

5. 总结

本节主要讲述空指针的含义,空指针常见的中枪姿势,以及如何避免空指针异常。下一节将为你揭秘 当 switch 遇到空指针,又会发生什么奇妙的事情。

相关文章:

开发中造成空指针的常见用法,如何避免

1. 前言 《手册》的第 7 页和 25 页有两段关于空指针的描述&#xff1a; 【强制】Object 的 equals 方法容易抛空指针异常&#xff0c;应使用常量或确定有值的对象来调用 equals。 【推荐】防止 NPE&#xff0c;是程序员的基本修养&#xff0c;注意 NPE 产生的场景: 返回类型为…...

MySQL事务和索引

目录 事务的概念 事务的四大特性&#xff08;ACID&#xff09; 原子性 隔离性 持久性 一致性 什么是脏读、幻读和不可重复读&#xff1f; 脏读 幻读 不可重复读 事务的隔离级别 读未提交 读已提交 可重复读 串行化 索引 索引优点 索引缺点 索引分类 索引设…...

Kali工具集简介

Kali Linux提供了数种经过定制的专门为渗透测试设计的工具。工具都会按下图中下拉选单所示的方式按组分类聚合。了解工具是做渗透测试第一个认知。 口Information Gathering(信息收集) 这些都是侦察工具,用来收集目标网络和设备的数据。在这类工具中,从找出设备的工具到查看使…...

离散数学 | 图论 | 欧拉图 | 哈密顿图 | 割点 | 桥(欧拉图和哈密顿图有没有割点和桥?)

本文主要解决以下几个问题&#xff1a; 1.欧拉图能不能有割点&#xff0c;能不能有桥&#xff1f; 2.哈密顿图能不能有割点&#xff0c;能不能有桥&#xff1f; 首先我们要明白几个定义 割点的定义就是在一个图G中&#xff0c;它本来是连通的&#xff0c;去掉一个点v以后这个…...

Android生命周期:理解与应用

摘要&#xff1a;Android生命周期是开发Android应用程序时至关重要的概念。本文将介绍Android生命周期的概念、生命周期方法的执行顺序以及如何在应用程序中正确地管理生命周期。我们还将讨论生命周期对于应用程序的重要性&#xff0c;并提供一些实际应用中的最佳实践和注意事项…...

00后真的是内卷王中王,真的想离职了....

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…...

linux Fd以及重定向讲解

感谢你的阅读&#xff0c;是对我最大的鼓励&#xff01;&#xff01;&#xff01;&#xff01; 目录 fd理解 文件操作重定向 让我们回顾C语言文件操作 首选我们要知道2个知识点&#xff1a; 额外知识点 如何理解一切皆文件呢&#xff1f; 当父进程fork创建子进程是否创建…...

Moonbeam近日提案公投一览

正在跟进Moonbeam治理的小伙伴&#xff0c;一起来快速浏览一下近期生态中正在发生的事情吧&#xff01;其中包含多个去中心化应用的Grant加速计划提案、HRMP开拓提案以及优化质押相关平台的内容。许多提案都与网络的运作息息相关&#xff0c;一起了解和参与Moonbeam的发展吧&am…...

凝聚青年力量,打造数字化人才队伍

当代青年人勇于探索、敢于创新、勤于变革&#xff0c;积极承担社会责任。这与ABeam倡导的「Build Beyond As One.™」的品牌理念不谋而合。ABeam的青年员工是未来社会的中坚力量&#xff0c;也正用他们的青春能量助力ABeam在中国的发展。 01 新兴青年力量 对ABeam而言&#…...

蓝牙资讯|智能家居标准Matter 1.1 发布,智能家居产品兼容更丰富

据“CSA 连接标准联盟”官方微信号&#xff0c;Matter 1.1 版本已发布&#xff0c;“1.1 版本带来的更新使设备制造商和开发者上手更容易、产品获取认证更方便&#xff0c;也让产品能更快地交付给用户。该版本还为电池供电设备提供了更大支持&#xff0c;而这类设备涉及多种类型…...

Cube Map 系列之:手把手教你 实现天空盒(Sky Box)

什么是天空盒 An skybox is a box with textures on it to look like the sky in all directions or rather to look like what is very far away including the horizon.天空盒是一个使用纹理贴图构建的盒子&#xff0c;人在其中朝任何一个方向看去&#xff0c;其纹理彷佛天空…...

腾讯VS百度:在AI上下大赌注

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 腾讯控股(00700)最近已经把基础模型和生成式人工智能应用方面的行业突破视为其业务的新增长机会了&#xff0c;并且正在大力投资人工智能&#xff0c;从而增强其现有产品的竞争力和拓展新的机会&#xff0c;比如腾讯已经把…...

字节原来这么容易进,是面试官放水,还是公司实在是太缺人?

本人211非科班&#xff0c;之前在字节和腾讯实习过&#xff0c;这次其实没抱着什么特别大的希望投递&#xff0c;没想到字节可以再给我一次机会&#xff0c;还是挺开心的。 本来以为有个机会就不错啦&#xff01;没想到能成功上岸&#xff0c;在这里要特别感谢帮我内推的同学&…...

生死疲劳|因为此书莫言获得诺贝尔奖

&#x1f4da;书名&#xff1a;《生死疲劳》 ✏️作者&#xff1a;莫言 历经六世的生死轮回&#xff0c; 三代人无尽的生死疲劳&#xff1b; 触碰极致的痛苦与快乐&#xff0c; 感受不灭的热情与希望。 &#x1f525;虽然本书长达39万字&#xff0c;但阅读过程却是无比的酣畅…...

Linux系统编程总结

day2 vim的三种工作模式 命令模式 vi hello.c zz 保存退出 2.编辑模式 i a o s &#xff08;有大写&#xff09;可以写东西 3.末行模式&#xff1a; 文本和末行模式不能直接切换 要切换回命令模式 再到末行模式&#xff0c;w:保存 q:退出 按两次esc回到命令模式 vim的基本…...

javascript基础二:Javscript字符串的常用方法有哪些?

在日常开发中&#xff0c;我们对字符串也是操作蛮多&#xff0c;这里我们来整理下字符串的一下最常用的方法 一、操作方法 字符串常用的操作方法归纳为增、删、改、查 增 这里增的意思并不是说直接增添内容&#xff0c;而是创建字符串的一个副本&#xff0c;再进行操作 除了…...

面了个 Java 实习生,小伙很优秀!

大家好&#xff0c;我是鱼皮&#xff0c;前几天给自己的公司面试了一位 Java 暑期实习生&#xff0c;候选人目前是大三。 整个过程我都录屏了&#xff0c;并且在征得候选人的同意后&#xff0c;把面试过程分享出来。一方面是希望对其他在学编程找工作的小伙伴有一些启发和参考…...

Java -并发(多线程)-Interview面试题收集

1、多线程并发 1&#xff09;多线程中 synchronized 锁升级的原理是什么&#xff1f; synchronized 锁升级原理&#xff1a;在锁对象的对象头里面有一个 threadid 字段&#xff0c;在第一次访问的时候 threadid 为空&#xff0c;jvm 让其持有偏向锁&#xff0c;并将 threadid…...

HashMap的merge()方法

最近遇到一个需求&#xff0c;需要统计各个会员的正在履行合同的合同租金总计&#xff0c;以此作为制定会员等级的标准。但是之前这个方法其实是有的&#xff0c;只是写的乱七八糟&#xff0c;具体的代码就不太方便放上来&#xff0c;就说说大致的代码思路吧。 原代码思路是先查…...

用 mysql_secure_installation 工具来进行密码重置操作(有效)

mysql_secure_installation 工具用于在 MariaDB 中进行一些安全设置&#xff0c;包括重置 root 用户的密码。您可以按照以下步骤使用该工具来重置 root 用户的密码&#xff1a; 1. 以管理员身份登录到您的系统。 2. 执行以下命令以运行 mysql_secure_installation 工具&#…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...