SpringBoot

Redis

入门

image-20240918212230787

因为是kv存储在内存里,所以,查询速度会比mysql快很多

image-20240918212538330

Java回炉重造

增强循环

在 Java 中,增强的 for 循环(即 for (String str : split))只是对数组中每个元素的副本进行操作,不会改变原始数组 split 的内容。如果你需要修改数组中的元素,你应该使用传统的 for 循环来进行操作,例如:

1
2
3
for (int i = 0; i < split.length; i++) {
split[i] = Integer.toBinaryString(Integer.parseInt(split[i]));
}

这样可以直接修改 split 数组中的每个元素。

内存分配

image-20240909104205648

image-20240909104305627

关于数组的内存

image-20240909104510727

关于方法的内存

image-20240909104941704

值传递

image-20240909105041091

这个是方法的值传递,形参在方法中的改变不会影响数组的值。

引用类型传递

image-20240909105257963

引用类型直接到堆中改变的值,所以是可以影响到本来数组的值的。

字符串

注意看注释,其实还蛮有用的(对于计算机网络来说

new String(b, 0, len) 是创建一个新的字符串对象的语法,其中 b 是一个字节数组,0 是起始索引,len 是有效字节的个数。

内存

image-20240909111301569

image-20240909111315439

所以再创建新的字符串的时候,最好用直接赋值的方式创建。

StringBuilder

image-20240909112238899

使用场景

image-20240909113331903

Math

image-20240909130348347

那个max记得,以后可能有很大用处

BigIntager

略,反正可以放很大的值

正则表达式

值得注意的是,一个[]只能匹配一个字符(除非后面加了量词)也就是如果要验证“ab”,需要如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ZhengZe {
public static void main(String[] args) {
String str = CreateString();
//正则表达式
System.out.println(str.matches("[abc][abc]"));
System.out.println(str.matches("\\w{4,}"));
System.out.println(str.matches("[abc]{2,}\\W+"));
}

public static String CreateString() {
//还记得键盘录入怎么写吗?
Scanner sc = new Scanner(System.in);
System.out.println("请输入录入:");
String str = sc.nextLine();
return str;
}
}

image-20240909134458101

substring包头不包尾

数组

整型数组并将其转换为列表

在Java中,你可以使用多种方式创建整型数组并将其转换为列表(List)。以下是一些常见的方法:

在Java 8中,.boxed()Stream API 中的一个方法,用于将基本数据类型的流(如 IntStreamLongStreamDoubleStream)转换为对应的包装类型的流(如 Stream<Integer>Stream<Long>Stream<Double>)。

为什么需要 .boxed()

Java的集合框架(如 ListSet 等)只能存储对象,不能直接存储基本数据类型(如 intlongdouble)。因此,当你想将基本数据类型的流转换为集合时,需要先将基本数据类型装箱(box)为对应的包装类型。

示例

假设你有一个 int[] 数组,你想将其转换为一个 List<Integer>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class BoxedExample {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};

// 将数组转换为流
IntStream intStream = Arrays.stream(array);

// 使用 .boxed() 将 IntStream 转换为 Stream<Integer>
Stream<Integer> integerStream = intStream.boxed();

// 将 Stream<Integer> 收集为 List<Integer>
List<Integer> list = integerStream.collect(Collectors.toList());

// 打印列表
System.out.println(list); // 输出: [1, 2, 3, 4, 5]
}
}

在上面的代码中,Arrays.stream(array) 创建了一个 IntStream,然后调用 .boxed() 方法将其转换为 Stream<Integer>,最后使用 collect(Collectors.toList()) 将流收集为一个 List<Integer>

简化写法

通常情况下,你可以将上述步骤简化为一行代码:

1
2
3
List<Integer> list = Arrays.stream(array)
.boxed()
.collect(Collectors.toList());

这样,.boxed() 方法会自动将 IntStream 转换为 Stream<Integer>,使得后续的收集操作可以顺利进行。

总之,.boxed() 方法用于将基本数据类型的流转换为对应的包装类型的流,以便在需要对象的地方使用。

方法一:使用Arrays.asList()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Arrays;
import java.util.List;

public class ArrayToListExample {
public static void main(String[] args) {
// 创建整型数组
int[] array = {1, 2, 3, 4, 5};

// 将数组转换为列表
List<Integer> list = Arrays.stream(array)
.boxed()
.collect(Collectors.toList());

// 打印列表
System.out.println(list); // 输出: [1, 2, 3, 4, 5]
}
}

方法二:使用ArrayList构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.ArrayList;
import java.util.List;

public class ArrayToListExample {
public static void main(String[] args) {
// 创建整型数组
int[] array = {1, 2, 3, 4, 5};

// 将数组转换为列表
List<Integer> list = new ArrayList<>();
for (int value : array) {
list.add(value);
}

// 打印列表
System.out.println(list); // 输出: [1, 2, 3, 4, 5]
}
}

方法三:使用IntStream和Collectors.toList()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ArrayToListExample {
public static void main(String[] args) {
// 创建整型数组
int[] array = {1, 2, 3, 4, 5};

// 将数组转换为列表
List<Integer> list = IntStream.of(array)
.boxed()
.collect(Collectors.toList());

// 打印列表
System.out.println(list); // 输出: [1, 2, 3, 4, 5]
}
}

方法四:使用Collections.addAll()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ArrayToListExample {
public static void main(String[] args) {
// 创建整型数组
int[] array = {1, 2, 3, 4, 5};

// 创建列表并添加数组元素
List<Integer> list = new ArrayList<>();
Collections.addAll(list, array);

// 打印列表
System.out.println(list); // 输出: [1, 2, 3, 4, 5]
}
}

注意事项

  • 在方法一和方法三中,我们使用了Java 8引入的Stream API来处理数组和列表之间的转换。
  • 在方法二和方法四中,我们使用了传统的循环和添加元素的方式来构建列表。
  • 在所有方法中,我们都使用了Integer对象而不是基本数据类型int,因为Java的集合框架不支持基本数据类型,只支持对象(所有用了流中的.boxed包装类)。

选择哪种方法取决于你的具体需求和偏好。

image-20240909141051511

流stream

在Java中,List 是一种常见的集合类型,用于存储一组有序的元素。Java 8引入了流(Stream)API,使得对集合的操作更加简洁和功能强大。流支持链式编程,允许你以声明式的方式对集合进行各种操作。

流的基本概念

流是一个来自数据源的元素队列,并支持聚合操作。流的操作分为两种类型:

  1. 中间操作:返回一个新的流,允许链式调用其他操作。
  2. 终端操作:触发流的处理并产生结果或副作用。

链式编程示例

假设我们有一个 List<Integer>,我们想对其进行一系列操作,例如过滤出偶数、将它们平方,然后计算总和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;
import java.util.List;

public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sumOfSquaresOfEvens = numbers.stream() // 创建流
.filter(n -> n % 2 == 0) // 过滤出偶数
.map(n -> n * n) // 将每个偶数平方
.reduce(0, Integer::sum); // 计算总和

System.out.println("Sum of squares of evens: " + sumOfSquaresOfEvens); // 输出: Sum of squares of evens: 220
}
}

具体操作解释

  1. 创建流

    1
    numbers.stream()

    这行代码从 List<Integer> 创建了一个流。

  2. 过滤出偶数

    1
    .filter(n -> n % 2 == 0)

    这行代码使用 filter 方法过滤出流中的偶数。filter 是一个中间操作,返回一个新的流。

  3. 将每个偶数平方

    1
    .map(n -> n * n)

    这行代码使用 map 方法将流中的每个元素平方。map 也是一个中间操作,返回一个新的流。

  4. 计算总和

    1
    .reduce(0, Integer::sum)

    这行代码使用 reduce 方法计算流中所有元素的总和。reduce 是一个终端操作,触发流的处理并产生结果。

其他常用流操作

  • **forEach**:对流中的每个元素执行某个操作。

    1
    numbers.stream().forEach(System.out::println);
  • **collect**:将流中的元素收集到一个集合中。

    1
    List<Integer> evens = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
  • **sorted**:对流中的元素进行排序。

    1
    List<Integer> sortedNumbers = numbers.stream().sorted().collect(Collectors.toList());
  • **distinct**:去除流中的重复元素。

    1
    List<Integer> distinctNumbers = numbers.stream().distinct().collect(Collectors.toList());

总结

流API和链式编程使得对集合的操作更加简洁和功能强大。通过组合不同的中间操作和终端操作,你可以以声明式的方式对集合进行复杂的处理。这种编程方式不仅提高了代码的可读性,还减少了样板代码的编写。

lambda表达式

image-20240909141858817

单列集合(除了树

image-20240909113834277

image-20240909145119047

image-20240909145142290

集合中的引用类

image-20240909145456540

关于引用类,有很多像equals这样的接口是要在引用类中重写的。

image-20240909145914214

Collection

image-20240909154008051

二叉树:普通。

二叉搜索树:比父节点小的在左,大的在右。

平衡二叉树:二叉搜索树。任意节点的左右子树高度都不超过1。

红黑树

image-20240909154904479

image-20240909155032989

红黑树:二叉搜索树,规则如上。(好难orz

set

image-20240909160101260

hashset

哈希值

image-20240909160301447

底层原理

image-20240909160630682

数组+链表+红黑树

双列集合

MAP

image-20240909161630544

遍历方式有三种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Hsmap {
public static void main(String[] args) {
HashMap<String,String> hmap = new HashMap<>();
hmap.put("key1", "value1");
hmap.put("key2", "value2");
hmap.put("key3", "value3");
hmap.put("key4", "value4");

//第一种遍历方式
//将key变为单列
Set<String> keys = hmap.keySet();
for(String key : keys){
System.out.println(key+"="+hmap.get(key));
}
//第二种
//entry:表示键值对对象
//set:set单列集合
//entryset相当于把键值对对象(value;key;)放到set当中
Set<Map.Entry<String, String>> entries = hmap.entrySet();
for(Map.Entry<String, String> entry : entries){
System.out.println(entry.getKey()+"="+entry.getValue());
}
//第三种使用foreach
hmap.forEach((k,v) -> System.out.println(k+"="+v));
}
}

hashmap

image-20240909163443666

LinkedHashMap

image-20240909163812019

因为有双项链表所以才有序

TreeMap

可定义key的排序规则,自动升序。

可变参数

1
void (int a,int... args)

可变参数一定要写在最后。

初识线程

image-20231026165509655

线程是抢占式调度

线程优先级只是一个概率大小的问题

守护线程,是其他线程结束后,才会陆陆续续的结束的线程(不是立马停止

礼让线程,类名.方法,让出当前CPU尽可能让线程执行均匀(了解即可

image-20231026171453249

很重要的一张图

线程安全性

线程执行时,具有随机性,在当前线程执行时,cpu的执行权很可能被其他线程给抢走。(特别是对于共享代码块)

image-20231026172550534

锁机制就是一种常用的同步机制,它可以用来保护共享资源,确保在某个线程访问共享资源时,其他线程无法同时修改该资源,从而保证数据的一致性。通过加锁,我们可以控制多线程对共享资源的访问顺序和互斥性,避免竞争条件的发生。

加锁并不意味着多线程失去了意义。相反,合理地使用锁可以解决线程间的竞争问题,保证数据的正确性,并发执行的优势依然存在。锁的作用是提供了一种同步机制,使得多线程能够协调地访问共享资源,确保线程间的互斥和顺序性,从而避免数据的混乱和冲突。

synchronized ()

检测到线程进来自动的关锁,线程执行完又自动的开锁。

同步代码块

就是把一段共享代码块锁起来,这样可以避免共享数据时的抢占安全性问题。

如果把整个方法里的代码全部锁起来就不用在代码块外面包裹synchronized (){}了直接在方法外面锁。

同步方法

image-20231026175916165

Lock

手动开锁和关锁。image-20231026181718576

关于死锁:切记不要让两个锁嵌套起来。

生产者和消费者(等待唤醒机制)

image-20231026183031447

线程四步走

image-20231026183332535

阻塞队列

image-20231026225840628

image-20231026225856072

线程池

image-20231027013422240

自定义线程池

需要的7个参数

核心线程都在忙,排核心线程的队已经排满,这个时候才创建临时线程。

image-20231027215115033

image-20231027215627052

网络编程

image-20231027220049242

image-20231027220315718

会出现ip不够用

所以有了ipv6

image-20231027220445349

image-20231027220527693

节省ipv4的方式

image-20231027220702656

在每个地方的局域网,都可能碰上不一样的路由给我的电脑分配不一样的ip地址,向一个ip发送消息是限制地方的。如果向该ip地址发送一些消息,如下:

image-20231027221015437

但是localhost:(127.0.0.1)是一个特殊地址,是一个本地回环地址,即为本机ip。此时发送消息时不经过路由器的。所以向这个ip发送东西不限制地方。如下:image-20231027221122028

image-20231027221347007

image-20231027222753174

image-20231027222929128

image-20231027223027151

UDP通信协议

image-20231027234540817

TCP通信协议