0%

AES(Advanced Encryption Standard)是一种对称加密算法,也称为高级加密标准。它是目前应用最广泛的加密算法之一,可以在保证安全性的同时提供很高的加密和解密速度。

在 AES 加密中,加密和解密使用相同的密钥,因此称为对称加密。这意味着如果密钥泄露,那么加密数据就会面临被破解的风险,因此密钥的安全性非常重要。

AES 加密算法采用分组加密的方式,将明文数据分成固定长度的块(128 位),对每个块进行加密。其中,AES 加密算法共有三种密钥长度:128 位、192 位和 256 位,密钥长度越长,加密安全性越高,但加密和解密的速度也越慢。

AES 加密算法基于替代置换和逆替代置换技术,通过多轮加密和混淆运算来实现加密。由于 AES 加密算法是基于数学原理的安全算法,因此它可以在很大程度上抵御暴力破解和字典攻击等攻击方式。

阅读全文 »

默认情况下,输入类对象中包含 Optional 类型字段,Json 序列化和反序列化都会有一些问题:

Optional 类型序列化 Json

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Book {
String title;
Optional<String> subTitle;
}

Book book = new Book();
book.setTitle("Oliver Twist");
book.setSubTitle(Optional.of("The Parish Boy's Progress"));

String result = mapper.writeValueAsString(book);

// 输出:
{"title":"Oliver Twist","subTitle":{"present":true}}
阅读全文 »

序列化

更改字段名(别名)

使用 @JsonProperty 注解指定字段别名,序列化、反序列化同等有效:

1
2
3
4
5
6
7
8
public class MyDto {

@JsonProperty("string_value")
private String stringValue;

private int intValue;
private boolean booleanValue;
}
阅读全文 »

Jackson 是 Java 知名流行的 JSON 库(不仅仅是JSON,还可以处理 XML),也是 Spring Boot 中默认集成的库。

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>

jackson-databind 会默认引入 jackson-annotations(注解) 与 jackson-core (核心) 两个库。

阅读全文 »

§ 集合

Java 集合类库采用“持有对象”的思想,并将其分为两个不同的概念,表示为类库的基本接口:

  • 集合。一个独立元素的序列,如 ListSetQueue
  • 映射。一组成对的 “键值对” 对象。如 Map 被称为关联数组,也被称作字典。 TreeMap 保持键始终处于排序状态。

不要在新代码中使用遗留类: VectorHashtableStack

阅读全文 »

§ 对象

面向对象编程是一种编程思维方式和编码结构。

抽象

所有编程语言都提供抽象机制。从某种程度上来说,问题的复杂度直接取决于抽象的类型和质量。

“纯粹”的面向对象程序设计方法:

  • 万物皆对象。
  • 程序是一组对象,通过消息传递来告知彼此该做什么。
  • 每个对象都有自己的存储空间,可容纳其他对象。
  • 每个对象都有一种类型。
  • 同一类所有对象都能接收相同的消息。
阅读全文 »

并发

§ 同步访问共享的可变数据

关键字 synchronized 可以保证同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。

同步的概念 不仅仅是一种互斥 的方式。同步不仅可以阻止一个线程看到对象处于不一致的状态之中,它还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前的所有修改效果。

下面代码期望是程序运行大约一秒左右,然后主线程 stopRequested 设置为 true,期望后台线程循环终止。但是这个程序永远不会终止,因为后台线程“看不到”主线程对 stopRequested 的值所做的改变。

阅读全文 »

异常

§ 只针对异常情况使用异常

拙劣的代码:

1
2
3
4
5
6
7
8
try {
int i = 0;
while (true) {
range[i++].climb();
}
} catch (ArrayIndexOutOfBoundsException e) {

}

该代码使用异常来进行跳出循环。

实际上基于异常的模式比标准模式要慢的多。异常应该只用于异常的情况下,他们永远不应该用于正常的程序控制流程。

阅读全文 »

通用程序设计

§ 将局部变量的作用域最小化

要使局部变量作用域最小化,最有力的方法就是在第一次使用它的地方声明。

for 循环比 while 循环更好,因为 for 循环中,可以使用局部作用域。

§ for-each 循环优先于传统的 for 循环

对于集合的首选遍历做法使用 for-each 循环。不会有性能损失,甚至某些情况还稍有性能优势(因为对数组索引的边界只计算一次)。在简洁性和预防Bug 方面有绝对优势。

1
2
for (Element e : elements) {
}

当看到冒号(:) 时,请将其读作「in」,因此,上面的循环读作「对于元素 elements 中的每个元素 e」

三种常见的情况无法使用 for-each 循环:

  • 过滤。遍历集合,并删除指定的元素。
  • 转换。遍历列表或数组,取代它部分或者全部元素值
  • 并行迭代。需要并行地遍历多个集合。
阅读全文 »

方法

§ 检查参数的有效性

公有方法,要用 Javadoc 的 @throws 说明违反参数限制时的异常,如 IllegalArgumentExceptionNullPointerException 等。

非公有方法,通常应该使用断言来检查它们的参数。断言如果失败,会抛出 AssertionError

1
2
assert a != null;
assert offset >= 0 && offset <= a.length;
阅读全文 »

Lambdas 与 Stream

§ Lambda 表达式优于匿名类

1
2
3
4
5
Collections.sort(words, new Comparator<String>() { 
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});

Java 8 增加了函数式接口,lambda 表达式和方法引用,以便更容易地创建函数对象。Stream API 一同被添加进来,为处理数据元素序列提供类库支持。

从 Java 8 开始,lambda 是迄今为止表示小函数对象的最佳方式。 除非必须创建非函数式接口类型的实例,否则不要使用匿名类作为函数对象。

1
2
3
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));

Collections.sort(words, comparingInt(String::length));
阅读全文 »

泛型

§ 不要使用原始类型

原始类型是指 List 而不是 List<E>,在 Java 1.5 之后应该没有使用原始类型的理由了。这样会失掉泛型在安全性和表述性方面的优势。

使用参数化的类型允许插入任意对象,如 List<Object> 还是可以的。

List 和 参数化类型 List<Object> 区别是,前者逃避了 泛型检查,后者明确告知编译器,能够执有任意类型的对象。

如:List<String> 可以传递给 List 的参数,但是不能将它传给类型 List<Object> 的参数。泛型有子类型化的规则,List<String> 是原始类型 List 的一个子类型,而不是参数化类型 List<Object> 的子类型。

应该通过 无限制通配符类型Set<?> 取代原始类型。

泛型信息可以在运行时被擦除,所以当使用 instanceof 操作符时,应当使用无限制通配符类型:

1
2
3
if (o instanceof Set) {
Set<?> m = (Set<?>) o;
}

这是使用泛型类型的 instanceof 运算符的首选方法。

阅读全文 »

类和接口

§ 使类和成员的可访问性最小化

设计良好的模块会隐藏所有实现细节,把它的API与它的实现清晰的隔离开来,模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块内部工作情况。这个概念被称为 信息隐藏或封装,是软件设计的基本原则。

Java 中对于成员(字段、方法、嵌套类、嵌套接口),有四种可能的访问级别:

  • 私有的。private
  • 包级私有的。default 或未指定访问修饰符
  • 受保护的。protected,声明该成员的子类可以访问这个成员(会受到一些限制)
  • 公有的。public,任何地方都可以访问该成员。

常量是类抽象的一个组成部分,可以通过 public static final 暴露常量,这些字段应当是包含 基本类型的值,或 不可变对象的引用。包含可变对象虽然引用不能修改,但引用的对象可以被修改,这可能会带来灾难性的结果。

注意,非零长度的数组总是可变的,所以类具有公共静态 final 数组是错误的。

阅读全文 »

所有对象都通用的方法

§ 重写 equals 时请遵守通用约定

什么时候应该重写 Object.equals?

如果类具有自己特有的 “逻辑相等” 概念,父类还没有重写过 equals 以实现期望的行为,这时我们需要重写 equals 方法,这通常属于 “值类” 的情形。

值类是仅仅一个表示值的类,例如 Integer。或者自己定义的 DTO 类。

重写 equals 方法的通用约定:

  • 自反性。对于任何非 null 的引用 x,x.equals(x) 必须返回 true
  • 对称性。对于任何非 null 的引用 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 必须返回 true
  • 传递性。对于任何非 null 的引用 x、y、z,如果 x.equals(y) 返回 true,并且 y.equals(z) 也返回 true,那么 x.equals(z) 也必须返回true
  • 一致性。对于任何非 null 的引用 x 和 y,多次调用 x.equals(y) 会一致返回 true 或 false
  • 对于任何非 null 的引用 x,x.equals(null) 必须返回 false。

同时还有最重要的提醒:

  • 重写 equals 时总要重写 hashCode
  • 不要试图让 equals 方法过于智能
  • 不要将 equals 声明中的 Object 对象替换为其他类型。
阅读全文 »