Go学习第五章——函数与包
Go学习第五章——函数与包
- 1 函数
- 1.1 基本语法
- 1.2 函数多返回值
- 1.3 函数的可见性和包级函数
- 1.4 函数调用机制底层原理
- 1.5 值类型和引用类型
- 1.6 注意事项和细节
- 1.7 逃逸机制(补,可不看)
- 2 包
- 2.1 快速入门
- 2.2 包的使用细节
- 3 函数详细讲解
- 3.1 递归调用
- 3.2 可变函数参数
- 3.3 init 函数
- 3.4 匿名函数
- 3.5 闭包
- 3.6 函数作为参数和返回值
- 3.7 defer函数
- 3.8 作为结构体的方法
- 4 变量的作用域
1 函数
函数是一段可以重复执行的代码块,通过函数可以将代码模块化,提高代码的可读性和可维护性。
要定义函数,需要指定函数的名称、参数和返回值(如果有的话)。
1.1 基本语法
基本语法
func 函数名(形参列表)(返回值类型列表){执行语句..return + 返回值列表
}
下面是一个简单的示例,展示了如何定义和调用一个简单的函数:
package mainimport "fmt"// 定义一个名为greeting的函数,它接收一个字符串参数name并没有返回值
func greeting(name string) {fmt.Printf("Hello, %s!\n", name)
}func main() {// 调用greeting函数,传入一个名字作为参数greeting("Alice")
}
运行上面的代码,输出结果为:
Hello, Alice!
1.2 函数多返回值
在 Go 语言中,函数可以返回多个值。这在某些情况下很有用,例如一个函数需要返回多个计算结果,或者需要返回一个值和一个错误状态。
下面是一个示例,展示了如何定义和使用返回多个值的函数:
package mainimport "fmt"// 定义一个名为divide的函数,它接收两个整数参数,并返回一个商和余数
func divide(a, b int) (int, int) {quotient := a / bremainder := a % breturn quotient, remainder
}func main() {// 调用divide函数,并接收两个返回值q, r := divide(10, 3)fmt.Printf("商:%d,余数:%d\n", q, r)
}
运行上面的代码,输出结果为:
商:3,余数:1
通过返回多个值,函数的调用方可以方便地获得函数计算的多个结果。
1.3 函数的可见性和包级函数
在 Go 语言中,函数和变量的可见性是由它们的命名规则决定的。一个函数或变量是否对其他代码可见,取决于它们的名称是否以大写字母开头。
如果一个函数或变量的名称以大写字母开头,则它对其他代码可见;如果名称以小写字母开头,则它只对同一个包内的代码可见。
下面是一个示例,展示了可见性的规则:
package mainimport "fmt"// 可以被其他代码访问
func PublicFunc() {fmt.Println("公有函数")
}// 只能在当前包内访问
func privateFunc() {fmt.Println("私有函数")
}func main() {PublicFunc()privateFunc() // 错误:无法访问私有函数
}
在这个示例中,我们定义了一个名为 PublicFunc 的公有函数,以及一个名为 privateFunc 的私有函数。在 main 函数中,我们可以正常调用 PublicFunc,但无法调用 privateFunc。
按照这个规则,我们可以将一些公共的、被其他代码调用的函数定义为包级函数,并将一些内部函数定义为私有函数。这有助于将代码逻辑与实现细节隔离,并提高代码的封装性。
1.4 函数调用机制底层原理
- 执行
n1 := 10,会生成一个存储这个值的区域,这里只是抽象为有这么一个main栈区,实际上不是这样命名,是使用寄存器和栈帧来实现,具体不讲解 - 因为是栈的方式,后进先出,所以这里调用函数后占用的数据,也会被优先回收掉。

函数调用是计算机程序中的一个重要概念,它用于在程序执行过程中跳转到函数代码的起始位置,并在函数执行完毕后返回到原来的位置。函数调用的底层实现涉及到栈的分配、参数传递和返回值处理等过程。
-
栈空间的分配:每个线程都会有自己的栈空间,用于存储函数的局部变量、函数参数和返回值。函数调用时,会给调用栈分配一块空间来存储函数执行过程中所需的数据。栈的分配是一个
后进先出(LIFO)的过程,即新的函数调用会在栈的顶部分配空间。 -
参数传递:函数调用需要将参数传递给被调用的函数。参数的传递方式一般分为两种:
值传递和引用传递。
-
在值传递中,参数的值会被复制到被调用函数的栈帧中,由于是复制操作,被调函数的修改不会影响到调用函数。
-
在引用传递中,函数参数是一个指针,传递的是变量在内存中的地址,被调用函数可以通过指针来访问和修改原始数据。
-
函数调用过程:当一个函数需要调用另一个函数时,会先将当前函数的执行状态压入栈中,包括返回地址、参数、局部变量等信息。然后跳转到被调用函数的起始位置,执行被调用函数的代码。在被调用函数执行结束后,会将返回值返回给调用函数,在栈中恢复调用函数的执行状态,包括返回地址和栈帧等信息,然后继续执行。
-
返回值处理:函数执行完毕后,需要将返回值返回给调用函数。返回值的处理方式和参数传递类似,可以使用值传递或者引用传递。在实际的底层实现中,一般通过寄存器或者栈帧来传递和存储返回值。
总结起来,函数调用的底层实现主要涉及栈空间的分配与释放、参数传递和返回值处理等过程。这些过程是通过寄存器和栈帧来实现的,不同的编程语言和编译器可能会有一些细节上的差异,但基本思想是相通的。
1.5 值类型和引用类型
前面使用的值传输,所以会发现函数并没有修改值,只是修改函数本身的栈空间里的变量值,当然还有相对应,可以直接通过引用类型传递,修改对应的值。
使用传输地址,也就是引用传递的方式,让函数直接修改地址值。
import "fmt"func add1(num int) {num = num + 1fmt.Println("在add1函数里,num=", num)
}func add2(num *int) {*num = *num + 1fmt.Println("在add2函数里,num=", *num)
}func main() {var num int = 2add1(num)fmt.Println("在main函数里,第一次,num=", num)add2(&num)fmt.Println("在main函数里,第二次,num=", num)
}
输出结果:
在add1函数里,num= 3
在main函数里,第一次,num= 2
在add2函数里,num= 3
在main函数里,第二次,num= 3
1.6 注意事项和细节
- golang不支持重载(通过其他方式实现)
- 在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
- 函数既然是一种数据类型,因此在Go中,函数可以作为
形参,并且调用。 - 为了简化数据类型定义,Go支持自定义数据类型
- 基本语法:
type 自定义数据类型名 数据类型// 理解:相当于一个别名 - 案例:type myInt int // 这是myInt就等价int来使用了
- 案例:type mySum func(int, int) int // 这是mySum就等价一个函数类型func (int, int) int
import "fmt"type myFunType func(int, int) intfunc myFun(funvar myFunType, num1 int, num2 int) int {return funvar(num1, num2)
}func main() {type myInt intvar num1 myInt = 40// num2 := int(num1) // 报错,因为myInt本质是,不是一个数据类型fmt.Println("num1=", num1)res := myFun(func(i1 int, i2 int) int { // 这里用到了匿名函数,可以去看看后面所说的定义return i1 + i2}, 500, 600)fmt.Println("res=", res)
}
输出:
num1= 40
res= 1100
- 支持对函数返回值命名
cal1和cal2的函数是一样的,只是cal2里对返回值命名,这样就可以不用考虑顺序,因为如果是cal1的方式,return的值需要考虑跟返回值类型的顺序一样。
import ("fmt""strconv"
)func cal1(n1 int, n2 int) (int, string) {sum := n1 + n2sub := strconv.FormatInt(int64(n1-n2), 10)return sum, sub
}func cal2(n1 int, n2 int) (sum int, sub string) {sub = strconv.FormatInt(int64(n1-n2), 10)sum = n1 + n2return
}func main() {sum1, sub1 := cal1(2, 2)sum2, sub2 := cal2(2, 2)fmt.Printf("cal1的返回值:%v, %v \n", sum1, sub1)fmt.Printf("cal2的返回值:%v, %v \n", sum2, sub2)
}
输出结果:
cal1的返回值:4, 0
cal2的返回值:4, 0
- 使用_标识符,忽略返回值
import "fmt"func cal(n1 int, n2 int) (sum int, sub int) {sum = n1 + n2sub = n1 - n2return
}func main() {res1, _ := cal(10, 20)fmt.Printf("res1 = %d", res1)
}
输出:res1 = 30
1.7 逃逸机制(补,可不看)
逃逸分析是编译器的一种静态分析技术,用于分析程序中的变量是否会逃逸到堆上分配内存。逃逸指的是当一个变量在函数内部分配内存,并且在函数外部被引用时,该变量就会逃逸到堆上。
逃逸分析的作用是优化内存分配和回收,将一部分变量从堆上分配转移到栈上分配,减少堆的压力和垃圾回收的负担。逃逸分析可以减少内存分配的次数,避免频繁的系统调用和锁竞争,提高程序的性能和并发能力。
逃逸分析的实现原理可以分为以下几个步骤:
-
内联优化:逃逸分析通常是在函数级别上进行的,首先编译器会尝试内联函数。内联优化是将函数的代码插入到调用它的函数中,减少函数调用的开销。内联优化会扩展函数的作用域,使函数内部的变量和参数可以直接访问。这样,一些局部变量就可以在栈上分配,而不是在堆上分配。
-
变量分析:逃逸分析会对函数的变量进行分析,判断变量是否逃逸。如果一个变量逃逸,编译器会将其分配在堆上;如果一个变量不逃逸,则可以将其分配在栈上。变量的逃逸分析包括以下情况的判断:
- 变量是否在函数返回后继续存在;
- 变量是否被存储到全局变量中,以供其他函数使用;
- 变量是否被闭包函数引用。
-
逃逸分析结果的使用:逃逸分析的结果会被编译器用于指导内存分配器进行内存分配。根据逃逸分析的结果,编译器可以决定将变量分配在栈上还是堆上。对于不逃逸的变量,编译器可以直接在栈上分配内存,避免了堆分配和垃圾回收的开销。
总结起来,逃逸分析是一种优化技术,它使用静态分析的方法判断变量是否会逃逸到堆上分配内存。逃逸分析的作用是减少堆的压力和垃圾回收的负担,提高程序的性能和并发能力。逃逸分析的实现原理包括内联优化、变量分析和逃逸分析结果的使用。
2 包
go的每一个文件都是属于一个包,也就是说go是以包的形式来管理文件和项目目录结构。
2.1 快速入门
包的三大作用:
- 区分相同名字的很熟、变量等标识符
- 当程序文件很多时,可以很好的管理项目
- 控制函数、变量等访问范围,即作用域
首先创建不同文件夹下面的包,再编写好对应的函数,然后import导入,最后调用!

2.2 包的使用细节
-
在给一个文件夹打包时,该包对应一个文件夹,比如这里的是utils文件夹对应的包名就是utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
-
当包名过长时,可以给包名取别名,取完之后,之前的就不能用了
在import时,在前面写的代码,就是别名。例如:这里把utils改成util。
package mainimport (util "GoStudy_Day1/Day03/model/utils""fmt"
)func main() {var num int = 2nums := util.Cal(num)fmt.Printf("%v 的平方等于:%v \n", num, nums)
}
- 编译一个可执行程序文件,就需要将这个包声明为
main,然后实际开发的时候,都是编译成exe文件再运行。
编译可以指定名字和目录,比如:放在bin目录下:go build -o bin/my.exe go_code/project/main
3 函数详细讲解
3.1 递归调用
在函数里,又调用了本身,也就是自己
下面是一个案例和底层栈空间的调用情况

注意细节:
- 执行一个函数时,就会串接一个新的受保护的独立空间(新函数栈,如图所示)
- 函数的局部变量是独立的,不会相互影响
- 递归必须向退出递归的条件逼近,否则就是无限递归,很容易栈溢出崩溃,实际开发不怎么用
- 当一个函数执行完毕或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时函数执行完毕就销毁。
3.2 可变函数参数
可变参数函数是指可以接收不定数量参数的函数,这些参数被看作是一个切片。
Go 语言中的可变参数函数使用 ... 表示。
下面是一个示例,展示了如何定义和调用可变参数函数:
package mainimport "fmt"// 定义一个名为sum的函数,它接收任意数量的整数参数,并返回它们的总和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {// 调用sum函数,传入多个整数参数fmt.Println("总和:", sum(1, 2, 3, 4, 5))fmt.Println("总和:", sum(10, 20, 30))
}
运行上面的代码,输出结果为:
总和: 15
总和: 60
在这个示例中,我们定义了一个名为 sum 的函数,它可以接收任意数量的整数参数,并返回它们的总和。在 main 函数中,我们调用了 sum 函数两次,并传递了不同数量的参数。
通过使用可变参数,我们可以简化函数的调用,使之更加灵活。
3.3 init 函数
基本介绍:
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。
案例说明:
import "fmt"func init() {fmt.Println("main init...")
}func main() {fmt.Println("main...")
}
输出结果:
main init...
main...
细节讨论:
- 如果一个文件同时包含
全局变量定义,init函数和main函数,则执行的流程是变量定义->init函数->main函数 - init函数最主要的作用,就是完成一些初始化的工作,比如下面的案例:
utils包:
package utilsvar Name string
var Age intfunc init() {Name = "Tom"Age = 100
}
main包
import ("GoStudy_Day1/Day03/model/utils""fmt"
)func main() {fmt.Printf("Name = %v, Age = %v", utils.Name, utils.Age)
}
输出结果:Name = Tom, Age = 100
从过程可以看出,有点像Java定义成员变量,并且给一个默认值的感觉
3.4 匿名函数
匿名函数是一种特殊的函数,它没有函数名,可以直接在其他函数中定义和使用。匿名函数在需要临时定义一段代码,并且这段代码不需要复用时很有用。
下面是示例,展示了如何定义和调用匿名函数:
实例一:
package mainimport "fmt"func main() {// 在main函数内定义一个匿名函数,并立即调用它func() {fmt.Println("这是一个匿名函数!")}()// 将匿名函数赋值给变量,然后进行调用greeting := func(name string) {fmt.Printf("Hello, %s!\n", name)}greeting("Alice")
}
运行上面的代码,输出结果为:
这是一个匿名函数!
Hello, Alice!
实例二:将匿名函数赋给a变量
func main() {a := func(n1 int, n2 int) int {return n1 - n2}res2 := a(10, 10)fmt.Println("res2=", res2)
}
输出结果:res2= 0
实例三:匿名函数赋值给全局变量,那么就成为一个全局匿名函数
这里不写了,跟上面雷同~~~
匿名函数可以用于需要临时定义一段代码的场景,例如在并发编程中,可以将匿名函数传递给协程进行并发执行。
3.5 闭包
闭包是指一个函数捕获并保存了其自身外部作用域的变量的引用。
简单来说,闭包就是一个函数以及它所引用的变量的组合体。
下面是一个示例,展示了如何使用闭包:
package mainimport "fmt"// 定义一个名为counter的函数,返回一个匿名函数
func counter() func() int {count := 0return func() int {count++return count}
}func main() {// 创建一个计数器实例c := counter()// 使用闭包进行计数fmt.Println(c()) // 输出:1fmt.Println(c()) // 输出:2fmt.Println(c()) // 输出:3
}
运行上面的代码,输出结果为:
1
2
3
在这个示例中,我们定义了一个 counter 函数,它返回一个匿名函数。在匿名函数内部,我们定义了一个变量 count,然后在每次调用匿名函数时更新这个变量,并返回它的值。
最佳实践
假设传入一个文件名,设置一个变量作为文件名的后缀,如果这个文件名没有指定的后缀,则自动给这个文件名添加后缀,如果有的话,直接输出。
这里需要使用HasSuffix函数,表示查找该该string有没有指定的后缀
func makeSuffix(suffix string) func(string) string {return func(name string) string {// 如果 name 没有指定后缀,则加上,否则就返回原来的名字if !strings.HasSuffix(name, suffix) {return name + suffix}return name}
}func main() {f := makeSuffix(".jpg")fmt.Println("文件名处理后=", f("winter"))fmt.Println("文件名处理后=", f("winter.jpg"))
}
输出结果:
文件名处理后= winter.jpg
文件名处理后= winter.jpg
虽然也可以用普通函数实现,但是过程太过复杂,所以不用。
通过闭包,我们可以创建一个状态被隐藏的函数,这个函数可以持续地访问和修改它所引用的变量。
ps:闭包还能通过这方式实现协程之间的数据传递,不过需要避免变量共享问题。(以后再讲~~)
3.6 函数作为参数和返回值
在 Go 语言中,函数可以作为参数传递给其他函数,也可以作为函数的返回值。这种能力使得代码更加灵活,可以根据需要将函数与其他函数进行组合。
下面是一个示例,展示了如何将函数作为参数和返回值:
package mainimport "fmt"// 定义一个名为apply的函数,它接收一个函数作为参数,并将参数函数应用到数字5上
func apply(f func(int, int) int) {result := f(5, 10)fmt.Println("应用结果:", result)
}// 定义一个名为add的函数,它接收两个整数并返回它们的和
func add(a, b int) int {return a + b
}func main() {// 将add函数作为参数传递给apply函数apply(add)// 将匿名函数作为参数传递给apply函数apply(func(a, b int) int {return a * b})
}
运行上面的代码,输出结果为:
应用结果: 15
应用结果: 50
在这个示例中,我们定义了一个 apply 函数,它接收一个函数作为参数,并将这个函数应用到数字5和10上。在 main 函数中,我们分别将 add 函数和一个匿名函数作为参数传递给 apply 函数,用于实现加法和乘法操作。
通过将函数作为参数和返回值,我们可以更灵活地组合和使用函数,实现更多复杂的功能。
3.7 defer函数
为什么需要defer
在函数中,程序员进程需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。
在 Go 语言中,我们可以使用 defer 关键字延迟执行一些代码,无论外部函数执行的怎样,这些代码都会在函数返回之前被执行。
下面是一个示例,展示了 defer 的使用场景:
import "fmt"func sum(n1 int, n2 int) int {defer fmt.Println("ok1 n1=", n1)defer fmt.Println("ok2 n2=", n2)res := n1 + n2fmt.Println("ok3 res=", res)return res
}func main() {res := sum(10, 20)fmt.Println("res=", res)
}
运行上面的代码,输出结果为:
ok3 res= 30
ok2 n2= 20
ok1 n1= 10
res= 30
当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,当函数执行完毕之后,再从defer栈中,一次从栈顶取出语句执行,所以从上面的案例就可以看出,先执行的是ok2语句。
3.8 作为结构体的方法
在 Go 语言中,方法是一种与特定类型关联的函数。它们可以通过定义在类型上的方法来实现某些特定操作。
下面是一个示例,展示了如何定义和使用方法:
package mainimport ("fmt""math"
)// 定义一个名为Circle的结构体类型
type Circle struct {radius float64
}// 在Circle类型上定义一个名为area的方法,它返回这个圆的面积
func (c Circle) area() float64 {return math.Pi * c.radius * c.radius
}func main() {// 创建一个Circle实例c := Circle{radius: 5}// 调用Circle类型的方法fmt.Println("圆的面积:", c.area())
}
运行上面的代码,输出结果为:
圆的面积: 78.53981633974483
在这个示例中,我们定义了一个名为 Circle 的结构体类型,它包含一个半径属性。然后,在 Circle 类型上定义了一个名为 area 的方法,它用于计算圆的面积。
在 main 函数中,我们创建了一个 Circle 实例,并调用了 Circle 类型的 area 方法来计算面积。
通过方法,我们可以将某些操作与特定类型绑定,使得代码更加清晰和面向对象。
4 变量的作用域
-
函数内部声明/定义的遍历叫局部变量,作用域仅限于函数内部。
func test() {// age 和 name 的作用域就只在test函数内部age := 10Name := "Tom~" }func main() { } -
函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效。
//函数外部声明/定义的变量叫全局变量, //作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效 var age int = 50 var Name string = "jack~"//函数 func test() {//age 和 Name的作用域就只在test函数内部age := 10Name := "tom~"fmt.Println("age=", age) // 10fmt.Println("Name=", Name) // tom~ }func main() {fmt.Println("age=", age) // 50fmt.Println("Name=", Name) // jack~test() } -
如果变量是在一个代码块,比如 for / if中,那么这个变量的作用域就在该代码块。
package main
import ("fmt"
)
func main() {//如果变量是在一个代码块,比如 for / if中,那么这个变量的的作用域就在该代码块for i := 0; i <= 10; i++ {fmt.Println("i=", i)}var i int //局部变量for i = 0; i <= 10; i++ {fmt.Println("i=", i)}fmt.Println("i=", i)
}
Over~~~~结束啦!!!!冲冲冲!!!
相关文章:
Go学习第五章——函数与包
Go学习第五章——函数与包 1 函数1.1 基本语法1.2 函数多返回值1.3 函数的可见性和包级函数1.4 函数调用机制底层原理1.5 值类型和引用类型1.6 注意事项和细节1.7 逃逸机制(补,可不看) 2 包2.1 快速入门2.2 包的使用细节 3 函数详细讲解3.1 递…...
【Python 常用脚本及命令系列 5 -- 如何使用 BeautifulSoup 解析CSDN网页表格中的数据】
文章目录 Python BeautifulSoup 介绍CSDN 网页表格解析开发问题总结 Python BeautifulSoup 介绍 BeautifulSoup是一个Python库,用于解析HTML和XML文档。它常常用于网络爬虫来提取网页中的信息。 以下是BeautifulSoup的一些主要特性: 解析HTMLÿ…...
OpenFeign实现分析、源码解析
什么是openfeign? 是springcloud全家桶的组件之一,其核心作用是为Rest API提供高效简洁的rpc调用方式。 为什么只定义接口而没有实现类? 源码解读(省略) 总结: 源码分析:如何发送http请求? …...
2023 10月最新Vmd 下载安装教程,WindowsLinux
文章目录 下载Vmdwindows版本安装LINUX版本安装 下载Vmd 谷歌搜索VMD 点击左下角download VMD 可选择对应版本 注:点击后会出现输入用户名和密码,由于我已注册,界面不见了,所以直接描述一下。 输入用户名和密码然后会出现让登记…...
Photoshop(PS)安装教程(图文教程超详细)
目录 一.简介 二.安装步骤 软件:PS版本:2023语言:简体中文大小:3.20G系统要求:Win10(1903)及以上版本,64位操作系统硬件要求:CPU2.0GHz 内存8G(或更高,不支…...
C++模版进阶
一、非类型模版参数 之前学习的模版,参数一般是某种类型,但其实非类型的参数也可以定义在模版里面,但也有一定的限制,只可以定义整形家族的参数,而且具有常量性 注意: 1. 浮点数、类对象以及字符串是不允…...
CloudCompare
CloudCompare 源码编译Windows 功能格式转换 源码编译 Windows 源码编译出来的默认基本不带几个插件,包括保存为 .las 的功能 可以直接从 https://www.danielgm.net/cc/ 下载编译好的版本,插件比较多。也有免安装版本 cmake -B build -S . -G "Vi…...
【算法小课堂】深入理解前缀和算法
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。 我们通过一个例子来理解前缀和算法的优势: 一维前缀和: ww…...
元对象系统功能
元对象系统功能 建立工程 布局页面 布局页面 修改原件名称 建立元对象 函数作为接口 增加一些固定的属性 #------------------------------------------------- # # Project created by QtCreator 2023-10-24T21:54:44 # #----------------------------…...
【2024秋招】小米中间件后端开发一面2023-9-13-base武汉
1 自我介绍 2 快手实习 2.1 讲讲你写的curd启动器,做了哪些工作呢 答: 2.2 网上也有一些开源的curd代码生成器,你为什么需要自研呢(重要) 答: (1)这个必须得自研,因…...
SpringMVC Day 01:入门案例
前言 在我们的日常工作和学习中,Web 开发是一个无法回避的重要环节。而在 Java Web 开发领域,SpringMVC 无疑是一个重量级选手。它以其灵活性、强大功能和清晰的 MVC 结构,赢得了大量开发者的青睐。但是,对于初学者来说ÿ…...
docker、docker-compose安装教程,很详细
docker、docker-compose安装教程,很详细 一、卸载旧版1、查看有没有安装过旧版2、停止docker3、删除安装过docker的相关包4、删除docker相关的镜像和容器 二、docker安装1、设置阿里云镜像2、查看所有docker3、安装最新版本4、安装指定版本 三、使用前准备1、启动do…...
源代码转换:Tangible Software Solutions 23.10 Crack
Tangible Software Solutions The Most Accurate and Reliable Source Code Converters Convert between C#, Java, C, Python, & VB, while saving countless hours of painstaking work and valuable time.源代码转换 Key Benefits Saves valuable time Accurate and com…...
SAD notes
ESKF 总结 prediction 更新误差先验 F F F通过3.42来算 得到 这里有点绕的一点是: 误差状态的 F F F牵涉到名义状态, 而名义状态又需要在时间上推进更新 其中, F中的名义状态的推进通过公式3.41得到, (名义状态不考虑误差, 这一点从3.41d, 3.41e可以看出, 误差状态只考虑…...
[SQL开发笔记]BETWEEN操作符:选取介于两个值之间的数据范围内的值
一、功能描述: BETWEEN操作符:选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 二、BETWEEN操作符语法详解: BETWEEN操作符语法: SELECT column1, column2,…FROM table_nameWHERE column BETWEEN val…...
Babylonjs学习笔记(三)——创建天空盒
书接上回,这里讨论创建天空盒!!! // 天空盒const envTex CubeTexture.CreateFromPrefilteredData(./env/environmentSpecular.env,scene)scene.environmentTexture envTex;scene.createDefaultSkybox(envTex,true)scene.environ…...
【计算机网络】文件传输协议FTP和SFTP
1. 介绍 SFTP(SSH文件传输协议)和FTP(文件传输协议)都是用于在计算机之间传输文件的网络协议。FTP和SFTP都位于OSI模型中的应用层。这两种协议用于文件传输和管理,是应用层协议,因此它们工作在OSI模型的最…...
Python 编程语言的介绍
Python 是一种高级、动态类型的解释型语言。由 Guido van Rossum 于1989年底发明,并在1991年首次发布。Python 的设计哲学强调代码的可读性和简洁的语法,特别是使用缩进来表示代码块,这使得开发者能够用更少的代码表达想法。 基础概念: 语法…...
centos服务器搭建安装Gitlab教程使用教程
1、更新服务器: sudo yum update -y && sudo yum upgrade -y 2、下载Gitlab的RPM包 https://packages.gitlab.com/gitlab/gitlab-cece表示开源el表示centos 选64位el8对应CentOS8 本教程以centos8为例,在服务器中,下载centos8的…...
linux复习笔记02(小滴课堂)
linux下输入输出错误重定向: 输入重定向:< 一个大于号是进行了覆盖。 两个大于号是追加。 输出重定向可以用于以后日志打印。 错误重定向: 错误重定向是不把信息打印到屏幕上而是打印到指定文件中去: 输出重定向其实是用的1…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
