golang切片slice

slice切片

概述

golang的数组是长度不变的连续内存序列,在实际开发中局限性太高。

slice切片是由数组封装得到的数据结构,常常被称为可变数组,python中的list和javascript中的array实际上都是这种可变数组。

理解

sh 复制代码
1.数组这种数据结构是在内存中开辟固定连续的空间。
2.切片属于可变数组,也就是说长度可以变化,如果他的长度变化超过了最开始开辟的底层数组的容量,他会重新创建一个容量更大的底层数组。

定义切片

类型定义

基本语法

从切片的来源我们知道,他是个不指定长度,可以动态变化长度的可变数组,又称动态数组

定义语法

go 复制代码
var 切片名 []类型;

与数组声明的区别就是切片不指定长度。

举例

go 复制代码
package slice_knowledge

import "fmt"

func DefineSliceByType(){
	var slice1 []string 
	fmt.Printf("切片slice1的零值为%#v\n",slice1)

	if(slice1!=nil){
		fmt.Println("slice不为nil")
	}else{
		fmt.Println("slice为nil")
	}
}

运行结果

go 复制代码
切片slice1的零值为[]string(nil)
slice为nil

小结

sh 复制代码
1.使用类型定义的切片未赋值时为`nil`
注意事项

由于使用类型定义的切片未赋值时为nil,是没有长度和容量的,所以无法存储任何元素,进行如下操作会报错。

go 复制代码
package slice_knowledge

import "fmt"

func DefineSliceByType(){
	var slice1 []string 
	fmt.Printf("切片slice1的零值为%#v\n",slice1)

	if(slice1!=nil){
		fmt.Println("slice不为nil")
	}else{
		fmt.Println("slice为nil")
	}
    
    //尝试赋值
	slice1[2] = "hello"
}

报错

sh 复制代码
panic: runtime error: index out of range [2] with length 0

goroutine 1 [running]:
go_learn/slice_knowledge.DefineSliceByType()
        /home/upsilon/workplace/2024_golang/coding/go_learn/slice_knowledge/create.go:16 +0xa9
main.main()
        /home/upsilon/workplace/2024_golang/coding/go_learn/main.go:140 +0x4b
exit status 2

我们可以验证当前切片的容量和长度。

go 复制代码
package slice_knowledge

import (
	"fmt"
)

func DefineSliceByType(){
	var slice1 []string 
	fmt.Printf("切片slice1的零值为%#v\n",slice1)

	if(slice1!=nil){
		fmt.Println("slice1不为nil")
	}else{
		fmt.Println("slice1为nil")
	}

	//尝试赋值
	// slice1[2] = "hello"

	//验证当前容量和长度
	fmt.Printf("切片slice1的长度为%v,容量为%v\n",len(slice1),cap(slice1))
}

结果

go 复制代码
切片slice1的零值为[]string(nil)
slice1为nil
切片slice1的长度为0,容量为0

make定义并初始化

make方法定义切片指的是在定义时就给切片开辟出一块空间,这样可以避免上述给nil赋值的错误。

make实际上同时进行了定义和初始化的过程。

更推荐使用make方法声明切片,因为可读性更高。

基本语法

go 复制代码
var 切片名 []类型 = make([]类型,切片长度,底层容量)
//左边类型省略
var 切片名 = make([]类型,切片长度,底层容量)
//未指定容量时,golang会在底层做处理,容量>=长度
//所以可以进一步简化
var 切片名 = make([]类型,切片长度)

注意:定义时底层容量必须大于等于切片长度。

举例

go 复制代码
package slice_knowledge

import (
	"fmt"
)

func DefineSliceByMake(){
	var slice2 = make([]string,2,3)
	fmt.Printf("切片slice2的值为%#v\n",slice2)

	//验证当前容量和长度
	fmt.Printf("切片slice1的长度为%v,容量为%v\n",len(slice2),cap(slice2))
}

运行结果

sh 复制代码
切片slice2的值为[]string{"", ""}
切片slice1的长度为2,容量为3

小结

sh 复制代码
1.make定义并初始化后会用零值填充。

切片初始化

概念解读

sh 复制代码
1.声明只是注册了标识符
2.初始化是开辟内存空间,用来存储标识的值

赋值初始化

语法

在声明的同时赋值来进行初始化。

go 复制代码
var 切片名 = []type{元素1,元素2,...}

举例

go 复制代码
package slice_knowledge
import (
	"fmt"
)
func SetValByVal(){
	var slice = []int64{1,2,3,4}
	fmt.Printf("切片slice的值为%#v\n",slice)
	fmt.Printf("切片slice1的长度为%v,容量为%v\n",len(slice),cap(slice))
}

结果

sh 复制代码
切片slice的值为[]int64{1, 2, 3, 4}
切片slice1的长度为4,容量为4

容量和长度

sh 复制代码
赋值初始化的切片长度为赋值长度,容量也为赋值的长度。

借助make初始化

make语法在声明的同时进行初始化,指明了切片的容量与长度。

sh 复制代码
1.切片的长度
值的实际个数,超过长度就属于越界访问。
没有赋值的元素使用类型零值。

2.切片容量
切片是引用类型,当容量没发生改变时,切片始终引用的是同一底层数组。

举例

go 复制代码
package slice_knowledge
import (
	"fmt"
)
func SetValByMake(){
	var slice = make([]int64,5)
	
	fmt.Printf("切片的值为%#v\n",slice)
}

结果

sh 复制代码
切片的值为[]int64{0, 0, 0, 0, 0}

底层图示

借助数组初始化

基本语法

切片的由来就是他是通过数组的截取得到的数据类型。

语法

go 复制代码
//前包后不包
var 切片名 = 数组[开始下标:结束下标]

解释

sh 复制代码
1.这样初始化可以不用指明切片类型,因为可以由数组类型推导
2.得到的切片是由数组前闭后开取得的,即从{开始下标,...,结束下标-1}
3.所得切片长度为结束下标-开始下标,容量为数组长度-开始下标。

举例

go 复制代码
package slice_knowledge
import (
	"fmt"
)
func SetValByArray(){
	var array = []int64{1,2,3,4,5,6}
	var slice = array[1:3:5]
	fmt.Printf("切片的值为%#v\n",slice)
	fmt.Printf("切片slice1的长度为%v,容量为%v\n",len(slice),cap(slice))
}

结果

go 复制代码
切片的值为[]int64{2, 3}
切片slice1的长度为2,容量为5

实际上还有第三个位置,例如数组[开始下标:结束下标:max]

切片的容量等于max-1,注意切片的截取长度

底层图示

各种截取操作
截取语法 获取切片 切片长度 切片容量
数组[:] 从数组索引为0到len(数组长度)-1处获得切片 数组长度 数组容量
数组[low:] 从数组索引为lowlen(数组长度)-1处获得切片 数组长度-low 数组容量-low
数组[:high] 从数组索引为0到high处获得切片 high 数组容量
数组[low:high] 从数组索引为lowhigh处获得切片 high-low 数组容量-low
数组[low:high:max] 从数组索引为lowhigh处获得切片 high-low max-low

举例

sh 复制代码
//所有截取的方法
func AllSetValByArray(){
	var array = []int64{1,2,3,4,5,6}
	//数组[:]
	var sliceA = array[:]
	fmt.Println("截取方法为数组[:]")
	fmt.Printf("切片的值为%#v\n",sliceA)
	fmt.Printf("切片sliceA的长度为%v,容量为%v\n",len(sliceA),cap(sliceA))

	fmt.Println("*****************************")

	var sliceB = array[2:]
	fmt.Println("截取方法为数组[2:]")
	fmt.Printf("切片的值为%#v\n",sliceB)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceB),cap(sliceB))
	fmt.Println("*****************************")

	var sliceC = array[:5]
	fmt.Println("截取方法为数组[:5]")
	fmt.Printf("切片的值为%#v\n",sliceC)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceC),cap(sliceC))
	fmt.Println("*****************************")

	var sliceD = array[1:3]
	fmt.Println("截取方法为数组[1:3]")
	fmt.Printf("切片的值为%#v\n",sliceD)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceD),cap(sliceD))
	fmt.Println("*****************************")

	var sliceE = array[1:3:3]
	fmt.Println("截取方法为数组[1:3:3]")
	fmt.Printf("切片的值为%#v\n",sliceE)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceE),cap(sliceE))
	fmt.Println("*****************************")
}

结果

sh 复制代码
截取方法为数组[:]
切片的值为[]int64{1, 2, 3, 4, 5, 6}
切片sliceA的长度为6,容量为6
*****************************
截取方法为数组[2:]
切片的值为[]int64{3, 4, 5, 6}
切片sliceB的长度为4,容量为4
*****************************
截取方法为数组[:5]
切片的值为[]int64{1, 2, 3, 4, 5}
切片sliceB的长度为5,容量为6
*****************************
截取方法为数组[1:3]
切片的值为[]int64{2, 3}
切片sliceB的长度为2,容量为5
*****************************
截取方法为数组[1:3:3]
切片的值为[]int64{2, 3}
切片sliceB的长度为2,容量为2
*****************************
注意事项

1.结束下标不得超过开始下标

go 复制代码
//报错:invalid slice indices: 2 < 3
var sliceF = array[3:2]
//相等是可以的相当于切片长度为0

2.结束下标和开始下标都必须小于数组长度

go 复制代码
var array = []int64{1,2,3,4,5,6}
/*
	报错:	panic: runtime error: slice bounds out of range [:8] with capacity 6
*/
var sliceG = array[5:8]

3.max的值要大于等于结束下标

go 复制代码
//报错:invalid slice indices: 3 < 4
var sliceH = array[1:4:3]
// 切片容量 = max-开始下标
// 切片长度 = 结束下标 - 开始下标
/*
	由于 容量>=长度
	max-开始下标>= 结束下标 - 开始下标
	max >= 结束下标
*/

4.max的值要小于等于数组的容量

go 复制代码
var array = []int64{1,2,3,4,5,6}
/*
   panic: runtime error: slice bounds out of range [::9] with capacity 6
*/
var sliceI = array[1:4:9]

切片的长度和容量

概念讲解

sh 复制代码
1.切片的长度
当前切片可以存储的元素个数,相当于js声明了一个数组。
访问的下标大于切片长度就会报越界错误。

2.切片的容量
切片的底层大小,切片是动态数组,可以通过append操作来延长切片长度。
当切片的长度大于当前容量时就会触发"扩容"操作。
"扩容"操作后,底层的引用就会改变。

比喻

sh 复制代码
1.声明切片就好比你买了一座房子(底层数组),"容量"就是房子的大小,"长度"就是你在房子里放的东西个数。
2.我们操作切片,因为都在这座房子里操作,所以相互影响。
3.用append扩充长度,如果房子还装得下,那继续在这个房子中,我们的一切操作都会影响到这个房子。
4.如果房子装不下了,我们就要"扩容",搬到新的更大的房子里,搬家后我们在新房子的操作就与旧房子无关了。

长度和容量方法

go 复制代码
var sliceX = make([]string,10,15)
//获得切片容量
cap(sliceX)
//获得切片长度
len(sliceX)

公式

go 复制代码
/*
	房子自然要比能放的东西大
*/
切片长度<=切片容量

切片是引用类型

切片截取

我们不仅可以从数组截取得到切片,也可以从切片截取得到新切片。

截取的语法

截取语法 获取切片 切片长度 切片容量
切片[:] 从切片索引为0到len(切片长度)-1处获得切片 切片长度 切片容量
切片[low:] 从切片索引为lowlen(切片长度)-1处获得切片 切片长度-low 切片容量-low
切片[:high] 从切片索引为0到high处获得切片 high 切片容量
切片[low:high] 从切片索引为lowhigh处获得切片 high-low 切片容量-low
切片[low:high:max] 从切片索引为lowhigh处获得切片 high-low max-low

举例

go 复制代码
func AllSetValBySlice(){
	var slice = make([]int64,6,10)
	//数组[:]
	var sliceA = slice[:]
	fmt.Println("截取方法为数组[:]")
	fmt.Printf("切片的值为%#v\n",sliceA)
	fmt.Printf("切片sliceA的长度为%v,容量为%v\n",len(sliceA),cap(sliceA))

	fmt.Println("*****************************")

	var sliceB = slice[2:]
	fmt.Println("截取方法为数组[2:]")
	fmt.Printf("切片的值为%#v\n",sliceB)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceB),cap(sliceB))
	fmt.Println("*****************************")

	var sliceC = slice[:5]
	fmt.Println("截取方法为数组[:5]")
	fmt.Printf("切片的值为%#v\n",sliceC)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceC),cap(sliceC))
	fmt.Println("*****************************")

	var sliceD = slice[1:3]
	fmt.Println("截取方法为数组[1:3]")
	fmt.Printf("切片的值为%#v\n",sliceD)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceD),cap(sliceD))
	fmt.Println("*****************************")

	var sliceE = slice[1:3:3]
	fmt.Println("截取方法为数组[1:3:3]")
	fmt.Printf("切片的值为%#v\n",sliceE)
	fmt.Printf("切片sliceB的长度为%v,容量为%v\n",len(sliceE),cap(sliceE))
	fmt.Println("*****************************")
}

结果

sh 复制代码
截取方法为数组[:]
切片的值为[]int64{0, 0, 0, 0, 0, 0}
切片sliceA的长度为6,容量为10
*****************************
截取方法为数组[2:]
切片的值为[]int64{0, 0, 0, 0}
切片sliceB的长度为4,容量为8
*****************************
截取方法为数组[:5]
切片的值为[]int64{0, 0, 0, 0, 0}
切片sliceB的长度为5,容量为10
*****************************
截取方法为数组[1:3]
切片的值为[]int64{0, 0}
切片sliceB的长度为2,容量为9
*****************************
截取方法为数组[1:3:3]
切片的值为[]int64{0, 0}
切片sliceB的长度为2,容量为2
*****************************

同底切片互相影响

何谓同底层切片

sh 复制代码
1.从同一个数组截取的切片
2.从同一切片截取到的切片及其衍生切片
3.赋值得到的切片(因为切片是引用类型)

验证

go 复制代码
package slice_knowledge
import (
	"fmt"
)
func VerifySlice(){
	var array = [5]int64{1,2,3,4,5}
	fmt.Printf("sliceA增加前,array的值为%#v\n",array)

	var sliceA = array[:2]
	fmt.Printf("sliceA增加前,sliceA的值为%#v\n",sliceA)
	
	var sliceB = array[2:4]
	fmt.Printf("sliceA增加前,sliceB的值为%#v\n",sliceB)

	sliceA = append(sliceA, 8)
	fmt.Printf("sliceA增加后,array的值为%#v\n",array)
	fmt.Printf("sliceA增加后,sliceA的值为%#v\n",sliceA)
	fmt.Printf("sliceA增加后,sliceB的值为%#v\n",sliceB)
}

结果

sh 复制代码
sliceA增加前,array的值为[5]int64{1, 2, 3, 4, 5}
sliceA增加前,sliceA的值为[]int64{1, 2}
sliceA增加前,sliceB的值为[]int64{3, 4}
sliceA增加后,array的值为[5]int64{1, 2, 8, 4, 5}
sliceA增加后,sliceA的值为[]int64{1, 2, 8}
sliceA增加后,sliceB的值为[]int64{8, 4}

切片方法

Append增加切片元素

源码

go 复制代码
// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//
//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
//
// As a special case, it is legal to append a string to a byte slice, like this:
//
//	slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

基本语法

go 复制代码
//在切片末尾添加1个元素
append(原始切片,元素1)

//在切片末尾依次添加多个元素
append(原始切片,元素1,元素2,...)

//在切片末尾添加其他切片的所有元素
append(原始切片,其他切片...)

返回值

sh 复制代码
1.一个切片
如果触发扩容(原始切片长度+元素个数>原始切片容量),则返回的切片与原始切片无关
如果没有触发扩容,则返回的切片与原始切片共享底层

举例

go 复制代码
package slice_knowledge
import (
	"fmt"
)
func AppendMethod(){
	var sliceA = make([]int64,5,8)
	fmt.Printf("append操作前,sliceA的值为%#v\n",sliceA)

	var array =[]int64{9,99,999,9999}
	var sliceB = array[:]
	fmt.Printf("append操作前,sliceB的值为%#v\n",sliceB)

	//append添加一个元素
	sliceC := append(sliceA,8)
	fmt.Printf("sliceC的值为%#v\n",sliceC)
	//append方法不会改变原始切片的长度
	fmt.Printf("append操作后,sliceA的值为%#v\n",sliceA)
	//没有触发扩容时原始切片和结果切片共用底层
	sliceC[1] = 55
	fmt.Printf("sliceC修改后的值为%#v\n",sliceC)
	fmt.Printf("sliceC修改后,sliceA的值为%#v\n",sliceA)

	//添加多个元素(仍然未触发扩容时)
	sliceD := append(sliceA,5,4)
	fmt.Printf("sliceD的值为%#v\n",sliceD)
	//原因是sliceA、sliceC、sliceD共用底层,所以sliceD的操作影响到了sliceC
	fmt.Printf("sliceC的值为%#v\n",sliceC)
	fmt.Printf("append操作后,sliceA的值为%#v\n",sliceA)

	//拓展运算符合
	sliceE :=append(sliceA,sliceB...)
	fmt.Printf("sliceE的值为%#v\n",sliceD)
	fmt.Printf("sliceE修改前,sliceA的值为%#v\n",sliceA)
	//最终长度>sliceA的容量,扩容了,sliceE与sliceA无关了
	sliceE[0] = 7878
	fmt.Printf("sliceE修改后,sliceA的值为%#v\n",sliceA)
}

结果

sh 复制代码
append操作前,sliceA的值为[]int64{0, 0, 0, 0, 0}
append操作前,sliceB的值为[]int64{9, 99, 999, 9999}
sliceC的值为[]int64{0, 0, 0, 0, 0, 8}
append操作后,sliceA的值为[]int64{0, 0, 0, 0, 0}
sliceC修改后的值为[]int64{0, 55, 0, 0, 0, 8}
sliceC修改后,sliceA的值为[]int64{0, 55, 0, 0, 0}
sliceD的值为[]int64{0, 55, 0, 0, 0, 5, 4}
sliceC的值为[]int64{0, 55, 0, 0, 0, 5}
append操作后,sliceA的值为[]int64{0, 55, 0, 0, 0}
sliceE的值为[]int64{0, 55, 0, 0, 0, 5, 4}
sliceE修改前,sliceA的值为[]int64{0, 55, 0, 0, 0}
sliceE修改后,sliceA的值为[]int64{0, 55, 0, 0, 0}
案例解读
go 复制代码
func AppendMethod(){
	var sliceA = make([]int64,5,8)
	fmt.Printf("append操作前,sliceA的值为%#v\n",sliceA)

	sliceC := append(sliceA,88)

	//添加多个元素(仍然未触发扩容时)
	sliceD := append(sliceA,5,4)
	fmt.Printf("sliceD的值为%#v\n",sliceD)
	//原因是sliceA、sliceC、sliceD共用底层,所以sliceD的操作影响到了sliceC
	fmt.Printf("sliceC的值为%#v\n",sliceC)
	fmt.Printf("append操作后,sliceA的值为%#v\n",sliceA)
}

解释

sh 复制代码
1.sliceA的容量是8
2.sliceC是sliceA增加一个元素得到的,(长度=6)<容量8,所以共享底层。
可以拆分为两步:
先设立一个新变量sliceC,长度为len(sliceA)+1
然后赋值 sliceC[6] = 88
底层 slice[6] = 88

3.sliceD是sliceA增加两个元素得到的,(长度=5+2)<容量8,所以共享底层
可以拆分为两步:
先设立一个新变量sliceD,长度为len(sliceA)+2
然后赋值sliceD[6]=5,sliceD[7]=4
底层slice[6]=5,slice[7]=4

4.由于sliceA、sliceC、sliceD共享底层,所以sliceC也受到了影响

可以理解为所有共享同底层的切片都是底层切片的投影。

copy方法

基本语法

源码

go 复制代码
// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int

解释

函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准。两个 slice 可指向同一底层数组,允许元素区间重叠。

语法

go 复制代码
copy(切片A,切片B)

返回值

sh 复制代码
更改元素的个数

举例

go 复制代码
func CopyMethod(){
	var sliceA = []int64{1,2,3}
	var sliceB = []int64{11,22,33,44,55,66,77,88}
	fmt.Printf("copy前sliceA的值为%#v\n",sliceA)
	fmt.Printf("copy前sliceB的值为%#v\n",sliceB)

	copy(sliceA,sliceB)
	fmt.Printf("copy后sliceA的值为%#v\n",sliceA)
	fmt.Printf("copy后sliceB的值为%#v\n",sliceB)

	var sliceC = []int64{1,2,3}
	var sliceD = []int64{11,22,33,44,55,66,77,88}
	fmt.Printf("copy前sliceC的值为%#v\n",sliceC)
	fmt.Printf("copy前sliceD的值为%#v\n",sliceD)

	x:=copy(sliceD,sliceC)
	fmt.Printf("copy后sliceC的值为%#v\n",sliceC)
	fmt.Printf("copy后sliceD的值为%#v\n",sliceD)
	fmt.Printf("更改元素的个数为%d\n",x)
}

结果

sh 复制代码
copy前sliceA的值为[]int64{1, 2, 3}
copy前sliceB的值为[]int64{11, 22, 33, 44, 55, 66, 77, 88}
copy后sliceA的值为[]int64{11, 22, 33}
copy后sliceB的值为[]int64{11, 22, 33, 44, 55, 66, 77, 88}
copy前sliceC的值为[]int64{1, 2, 3}
copy前sliceD的值为[]int64{11, 22, 33, 44, 55, 66, 77, 88}
copy后sliceC的值为[]int64{1, 2, 3}
copy后sliceD的值为[]int64{1, 2, 3, 44, 55, 66, 77, 88}
更改元素的个数为3

解释

sh 复制代码
1.copy(A,B)
将B的值赋给A,A与B的长度不改变只改变值,如果有对应位置的值就赋值,否则保留之前的值

2.如果A的长度4>B的长度3
A[0] = B[0]
A[1]=B[1]
A[2]=B[2]
A[3]=A[3]
...

3.如果A的长度3<B的长度4
A[0]=B[0]
A[1]=B[1]
A[2]=B[2]
对于共用底层的切片
go 复制代码
func CopyMethod(){
	var array = []int64{1,2,3,4,5,6}
	sliceE := array[:]
	sliceF := array[2:4]
	fmt.Printf("copy前sliceE的值为%#v\n",sliceE)
	fmt.Printf("copy前sliceF的值为%#v\n",sliceF)
	copy(sliceF,sliceE)
	fmt.Printf("copy后sliceE的值为%#v\n",sliceE)
	fmt.Printf("copy后sliceF的值为%#v\n",sliceF)
}

结果

sh 复制代码
copy前sliceE的值为[]int64{1, 2, 3, 4, 5, 6}
copy前sliceF的值为[]int64{3, 4}
copy后sliceE的值为[]int64{1, 2, 1, 2, 5, 6}
copy后sliceF的值为[]int64{1, 2}

解释

go 复制代码
copy(sliceF,sliceE)
将sliceE赋值给sliceF,
sliceE[0] = sliceF[0] => 1
由于共底,底层 silce[2] = 1
(因为sliceE[0]是底层slice[2],则sliceF变成了[1,2,1,4,5,6])

sliceE[1]=sliceF[1]=>2
由于共底,底层 silce[3] = 2
(因为sliceE[1]是底层slice[3],则sliceF变成了[1,2,1,2,5,6])
相关推荐
橘猫云计算机设计1 小时前
基于Springboot的自习室预约系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·毕业设计
秋书一叶1 小时前
SpringBoot项目打包为window安装包
java·spring boot·后端
pwzs2 小时前
Spring MVC 执行流程全解析:从请求到响应的七步走
java·后端·spring·spring mvc
小兵张健2 小时前
互联网必备职场知识(4)—— 共情沟通能力
后端·产品经理·运营
AskHarries3 小时前
使用 acme.sh 自动更新 SSL 证书的指南
后端
Chandler243 小时前
Go:反射
开发语言·后端·golang
pwzs3 小时前
深入浅出 MVCC:MySQL 并发背后的多版本世界
数据库·后端·mysql
盒子69103 小时前
go for 闭环问题【踩坑记录】
开发语言·后端·golang
刘大猫264 小时前
Arthas monitor(方法执行监控)
人工智能·后端·监控
追逐时光者4 小时前
MongoDB从入门到实战之MongoDB简介
后端·mongodb