常州做网站价位网店网站源码
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:10
当前位置: 首页 > news >正文
常州做网站价位,网店网站源码,wdcp网站备份问题,东莞短视频推广方法1.Go概述
程序是一段计算机指令的有序组合。程序算法数据结构。任何程序都可以将模块通过三种基本的控制结构#xff08;顺序、分支、循环#xff09;进行组合来实现。
Go#xff08;也称为Golang#xff09;是一种由Google开发的开源编程语言。设计目标是使编程更简单、…1.Go概述
程序是一段计算机指令的有序组合。程序算法数据结构。任何程序都可以将模块通过三种基本的控制结构顺序、分支、循环进行组合来实现。
Go也称为Golang是一种由Google开发的开源编程语言。设计目标是使编程更简单、高效和可靠。Go旨在提供高性能、简洁且易于理解的语法。它结合了传统编译型语言的速度和性能以及动态类型语言的易用性和便捷性。 Go语言特性
1.静态编译
2.少即是多语法简洁简单可读性强
3.原生支持并发编程
4.Duck模型的非侵入式接口
5.强调组合组合优于继承
6.支持多种操作系统和体系结构的交叉编译,这里的 targetOS 是目标操作系统如 windows、linux、darwin 等targetArchitecture 是目标体系结构如 amd64、arm、386 等。
GOOStargetOS GOARCHtargetArchitecture go build
GOOSwindows GOARCHamd64 go build7.大量使用接口和内置函数来提高代码的复用度
8.支持和C 语言相互调用的机制CGOGo 中使用 C 包来调用 C 函数以及使用特殊的类型来处理在 Go 和 C 之间传递的数据。以下是一个简单的示例展示了如何在 Go 中调用 C 函数
#include stdio.hvoid helloFromC() {printf(Hello from C!\n);
}package main/*
#cgo CFLAGS: -g -Wall
void helloFromC();
/
import Cfunc main() {C.helloFromC()
}9.精确依赖并通过增量编译、并行编译以及缓存编译结果来缩短编译时间
Go语言基本命令
2.Go基本语法
Go语言中共有25个关键字是静态强类型语言。
强类型编译器会确认每个变量应有的类型错误使用将引发错误
静态仅支持编译时自动推断类型 Go语言中的条件语句if判断后面不需要()同时if可带一个初始化子语句用跟条件分开。同时Go也不支持三元运算符
/
if SimpleStmt;Expression {statement……
}
*/
if i:10;i8 {//条件语句
}switch语句说明
注意math.Floor(num)函数用于返回小于num的最小整数
同时switch是惰性求值只有在需要求值时才去计算表达式从而降低消耗提升性能。
for循环
1.GO 的循环语句只有for ,没有while/do while 2.for 语句后面同样不用加( ) 3.for语句的三个部分,省略任何一个时分号不能省略 4.只留条件判断时可以不用分号 相当于while语句 5.全部省略,变为无限循环
//while:
for experssion {}
//无限循环
for {if state {break}
}goto 可以跟标签更紧密合作可以代替break 跳出多重循环
手动实现排序
手写实现的冒泡排序Go代码如下所示
这里需要注意切片作为参数传递是引用传递
func bubbleSort(nums []int){n:len(nums)// 这里i是定义排序好的数量for i:0;in-1;i {// 每次排序都是从第一个元素开始冒泡for j:0;jn-1-i;j {if nums[j]nums[j1] {nums[j1],nums[j]nums[j],nums[j1]}}}
}手写实现插入排序Go代码如下所示
func insertSort(nums []int){n:len(nums)// 从无序组第二个元素开始依次插入有序组中for i:1;in;i{key:nums[i]j:i-1for j0 keynums[j]{nums[j1]nums[j]j–}nums[j1]key}
}手动实现快速排序具体Go代码如下
func quickSort(nums []int){n:len(nums)if n2 {return }// 定义基准线pivot:nums[0]low,high:0,n-1for lowhigh {if nums[low]pivot{low}else{nums[low],nums[high]nums[high],nums[low]high–}}// 交换基准元素位置nums[0],nums[high]nums[high],nums[0]// 递归排序左右子数组quickSort(nums[:high])quickSort(nums[high1:])
}3.基础数据类型及运算
Go语言中基础数据类型有整型、浮点型、复数、布尔型、字符型、字符串型以及错误类型
可以用 reflect.TypeOf 函数来查看类型名称
1.基础类型
1.整型
整型按照是否有符号可以分为有符号位和无符号位
整型按照位数可以分为int int8 int 16 int32 int32 int64
这里需要注意不同的整型之间是不能直接比较不能直接运算
2.浮点数
浮点数主要包括float32和float64
标准库math包中的函数都使用float64
3.复数
复数由两个浮点数表示一个实部、一个虚部
有两种复数类型即complex64两个float32组成和complex128两个float64组成
有三个内置复数处理函数
complex(float,float) 创建复数
real() 获取实部
image() 获取虚部
package mainimport (fmt
)func main() {// 创建复数var comp1 complex64 complex(2, 3) // 实部为2虚部为3comp2 : complex(4.5, 7.1) // 使用默认类型complex128// 输出复数fmt.Println(Complex 1:, comp1)fmt.Println(Complex 2:, comp2)// 访问实部和虚部fmt.Println(Real part of Complex 1:, real(comp1)) // 输出实部fmt.Println(Imaginary part of Complex 1:, imag(comp1)) // 输出虚部
}4.布尔类型
布尔值主要包括true和false类型长度为1byte
布尔类型无法被其他类型赋值也不支持类型转换
这里布尔类型不支持用0和1表示真假
if 和for 语句的条件部分必须是布尔类型的值或表达式
2.运算符
运算符主要包括算术运算符、关系运算符、逻辑运算符、赋值运算符以及位运算符
1.算术运算符
算术运算符主要包括加减乘除、取模、自增、自减
注意Go语言自增只支持变量,不支持变量自减也是
2.关系运算符
关系运算符主要包括 !注意由于布尔类型不支持转换整型因此不等式连写例如xyz这种语法是错误的
3.逻辑运算符
逻辑运算符主要包括取反、且、或||
4.赋值运算符 5.位运算符
位运算符包括 左移 相当于乘以2右移 相当于除以2位与
| 位或
^ 异或位运算只对整数其作用是底层运算效率较高
4.集合数据类型
Go语言中主要有三种类型的集合分别是数组Array、切片Slice以及Map
1.数组
数组是同类元素的集合数组变量声明后其元素类型、数组长度均不可变
数组声明
// 只声明未赋值H
var arr1 [5]int
// 直接赋值
arr2:[3]int{1,2,3}
// 数组长度由初始化数量确定
arr3:[…]int{1,2,3} //…不可省略
// 对含有下标的元素赋初值 其余元素保持零值
arr4:[4]{0:99,3:100}数组复制
数组变量之间进行复制会拷贝整个数组值拷贝 a : […]string{USA, China, India, Germany}b : a b[0] Singaporefmt.Println(a is , a)fmt.Println(b is , b) //a is [USA China India Germany] //b is [Singapore China India Germany]数组传参
与数组复制类型只是实参拷贝一份给形参函数调用结束则销毁二者相互独立传递大数组时效率较低
func changeLocal(num [5]int) {num[0] 55fmt.Println(inside function , num)
}func main() {num : […]int{5, 6, 7, 8, 8}fmt.Println(before passing to function , num)changeLocal(num) //num is passed by valuefmt.Println(after passing to function , num)
}
//before passing to function [5 6 7 8 8]
//inside function [55 6 7 8 8]
//after passing to function [5 6 7 8 8] 数组遍历
数组遍历可以采用for循环遍历或range遍历
// for循环a : […]float64{67.7, 89.8, 21, 78}for i : 0; i len(a); i { fmt.Printf(%d th element of a is %.2f\n, i, a[i])}
// range遍历a : […]float64{67.7, 89.8, 21, 78}for i, v : range a { // 第一个参数为序号第二个为变量fmt.Printf(%d the element of a is %.2f\n, i, v)}//0 the element of a is 67.70 //1 the element of a is 89.80 //2 the element of a is 21.00 //3 the element of a is 78.00
此外range遍历的两个参数如果只用一个会报错可以用_占位符来表示只用一个参数
多维数组 a : [3][2]string{{lion, tiger},{cat, dog},{pigeon, peacock}, //此处,不可忽略否则报错}for _, v1 : range a {for _, v2 : range v1 {fmt.Printf(%s , v2)}fmt.Printf(\n)}2.切片
由于数组的定长性和值拷贝限制其使用因此提供切片使用即提供长度可变的数组引用
切片声明时不能给定底层数组大小否则变成了数组声明同时可以使用内置函数make来声明和初始化
切片是引用类型不支持运算
创建切片
这里需要注意如果创建切片时指定底层数组一旦切片改变底层数组元素也会发生改变因为切片是对原数组的引用。因此多个切片可以共享同一个底层数组。
// 指定底层数组创建a : [5]int{76, 77, 78, 79, 80}//底层数组s1 : a[0:4] // from a[0] to a[3]s2 : a[:4] // from a[0] to a[3]s3 : a[2:5] // from a[2] to a[4]s4 : a[2:] // from a[2] to a[4]fmt.Printf(%v\n%v\n%v\n%v, s1, s2, s3, s4)//[76 77 78 79]//[76 77 78 79]//[78 79 80]//[78 79 80]
// 同时创建数组和切片
//指定数组大小只创建数组
c : [3]int{6, 7, 8}
//不指定数组大小返回切片引用底层数组匿名
d : []int{6, 7, 8}
//用…推断数组大小只创建数组
e : […]int{6, 7, 8} 内置函数 len() 返回切片当前长度 内置函数cap()返回切片底层数组容量
切片动态增加
内置函数 append() 动态扩展切片在底层数组容量范围内会直接覆盖底层数组元素
package mainimport fmtfunc main() {arr : [7]int{9, 8, 7, 6, 5, 4, 3}sli : arr[1:3]sli append(sli, 20) // 增加一个20切片容量扩展一倍fmt.Printf(%v\n, arr) //[9 8 7 20 5 4 3]fmt.Printf(%v\n, sli) //[8 7 20]
}切片动态增加时当超过底层数组容量大小时会重新创建底层数组并转移数据 切片增长在元素小于1000时成倍增长超过1000增长速率大概为1.25 cars : []string{Ferrari, Honda, Ford}fmt.Println(cars:, cars, length, len(cars), capacity, cap(cars)) //cars: [Ferrari Honda Ford] length 3 capacity 3fmt.Printf(%x\n, cars[0])//c000080330cars append(cars, Toyota)fmt.Println(cars:, cars, length, len(cars), capacity, cap(cars))//cars: [Ferrari Honda Ford Toyota] length 4 capacity 6 //why 6fmt.Printf(%x\n, cars[0]) //c0000a4000 //Why切片合并
内置函数 append() 还支持切片的合并用…运算符把对应切片所有元素都取出 veggies : []string{potatoes, tomatoes, brinjal}fruits : []string{oranges, apples}food : append(veggies, fruits…) //… 不可忽略fmt.Println(food:, food)//food: [potatoes tomatoes brinjal oranges apples]
切片传参
在函数传参时复制的是结构体拷贝实现引用传递
func subtactOne(numbers []int) {for i : range numbers {numbers[i] - 2}
}
func main() {nos : []int{8, 7, 6}fmt.Println(slice before function call, nos)//slice before function call [8 7 6]subtactOne(nos)fmt.Println(slice after function call, nos)//slice after function call [6 5 4]
}多维切片
多维切片比多维数组灵活每行元素个数不必相同 pls : [][]string{{C, C, C#},{JavaScript},{Go, Rust},}for _, v1 : range pls {for _, v2 : range v1 {fmt.Printf(%s , v2)}fmt.Printf(\n)}//C C C# //JavaScript //Go Rust
3.Map
Map用于存储一系列无序的键值对是引用类型不支持运算nil除外
Map不是线程安全的不支持并发写
1.初始化
Map零值不可用只声明不初始化为nil值不分配底层存储空间不能添加元素 用字面量或make函数进行初始化后可以添加元素 var m1 map[string]intfmt.Println(m1 nil)//true//m1[a] 1 //errorm2 : map[string]int{}fmt.Println(m2 nil)//falsem2[a] 1 //okm3 : make(map[string]int)fmt.Println(m3 nil)//falsem3[a] 1 //ok2.赋值
可在Map初始化后进行元素赋值也可在Map初始化时直接元素赋值 personSalary : make(map[string]int)personSalary[steve] 12000personSalary[jamie] 15000personSalary[mike] 9000//初始化时直接赋值personSalary : map[string]int{steve: 12000,jamie: 15000,}
3.元素查找
Map元素通过下标访问其实可以返回两个值底层实际为函数Comma-ok 法 1.对应的value 2.对应的key是否存在的布尔值 personSalary : map[string]int{steve: 12000,jamie: 15000,}value, ok : personSalary[joe]if ok true {fmt.Println(Salary of joe is, value)} else {fmt.Println(joe not found)}4.Map元素遍历
Map元素可以使用range 遍历但不保证顺序 personSalary : map[string]int{steve: 12000,jamie: 15000,}personSalary[mike] 9000for key, value : range personSalary {fmt.Printf(personSalary[%s] %d\n, key, value)}//personSalary[mike] 9000//personSalary[steve] 12000// personSalary[jamie] 150005.Map元素删除
使用内置函数delete()删除Map元素 1.key存在对应元素被删除 2.key不存在什么都不发生 personSalary : map[string]int{steve: 12000,jamie: 15000,}personSalary[mike] 9000fmt.Println(map before deletion, personSalary)delete(personSalary, steve)fmt.Println(map after deletion, personSalary)//map before deletion map[jamie:15000 mike:9000 steve:12000]//map after deletion map[jamie:15000 mike:9000]5.Go函数
函数效率高则程序效率高建议多用标准库函数
1.函数定义
//语法格式
func funcName(paramList)(resultList) {coding ……
}
//paramList input1 type1,input2 type2 ……
//resultList output1 type1,output2 type2 ……
//多个相邻相同类型参数可以使用简写
func add(a, b int) int {return a b
}Go函数中可以有多个返回值同时返回值可以有变量名并在函数体内可见。
注意不支持函数重载因为重载只是偶尔有用但在实践中会引起无解和导致脆弱性
2.参数不定参数
不定参数形参数目可变、不确定 不定参数声明语法格式 param … type
不定参数的形参在函数内是切片
func sum(nums …int) int {total : 0for _, num : range nums {total num}return total
}上述不定参数是不定数量但类型是相同的如果要实现不定数量同时不定类型则需要通过接口类型interface{}作为参数实现
func printAll(vals …interface{}) {for _, val : range vals {fmt.Println(val)}
}3.匿名函数
匿名函数相当于函数字面量可以使用函数的地方就可以使用匿名函数
//匿名函数直接调用
func(a,b int )int{return a-b
}(5,4)
//匿名函数赋值给函数变量
var sum func(a,b int )int{return ab
}
//函数作为返回值
func getFun(op string) func(a,b int )int {return func(a,b int )int{return ab}
}4.闭包
闭包函数引用环境常见于在函数内部定义匿名函数并且该匿名函数访问定义它的外部函数的作用域
package main
import fmt
func main() {// 外部函数外的变量outsideVar : 10// 内部函数形成闭包closureFunc : func() {fmt.Println(outsideVar) // 闭包函数内部访问外部变量}closureFunc() // 调用闭包函数
}函数柯里化
函数柯里化就是把接收多个参数的函数变换成接收单一参数的函数
函数柯里化是一种将多参数函数转换为一系列单参数函数的过程。这种转换的结果是原始函数可以通过一系列较少参数的函数来调用。
5.延迟调用defer
Go函数支持defer进行延迟调用
defer类似OOP语言异常处理中的final子句常用来保证系统的资源的回收和释放。 defer Println(last)Println(main body)Println(first)//main body//first//last使用defer函数时会把当时的实参值传递给形参即使后序实参发生变化也不影响函数结果 a : 5defer fmt.Println(“defer 注册函数时的a值, a)a 10fmt.Println(“普通函数的a值, a)//普通函数的a值 10//defer 注册函数时的a值 5此外使用多个defer时这些defer调用以先进后出FILO顺序在函数返回前被执行 name : Naveenfmt.Printf(Original String: %s\n, string(name))fmt.Printf(Reversed String: )for _, v : range []rune(name) {defer fmt.Printf(%c, v)}//Original String: Naveen//Reversed String: neevaN6.递归函数
形式上一个正在执行的函数调用了自身(直接递归).
递归不能无限制调用因为栈空间有限 递归中必须有完成终极任务的语句 递归调用参数逐渐逼近结束条件 递归的目的是简化设计使程序易读但通常效率较低
6.结构体和方法
1.结构体
1.结构体定义
结构体把有内在联系的不同类型的数据统一成一个整体使它们相互关联
结构体是变量的集合从外部看是一个实体
type Employee struct{firstName stringlastName stringage intsalary int
}2.带标签的结构体
结构体中的字段除了名字和类型外还可以有一个可选的标签tag
标签是一个附属于字段的字符串用于描述字段信息 标签还可以按key1:“value1” key2:“value2”键值对进行修饰来提供编码、解码、ORM等转化辅助
可以使用反射获取结构体标签中的每一个键值对
package mainimport (fmtreflect
)type Person struct {Name string json:name validate:requiredAge int json:age validate:min18
}func main() {p : Person{Name: Alice, Age: 25}// 获取结构体字段的标签信息t : reflect.TypeOf(p)for i : 0; i t.NumField(); i {field : t.Field(i)fmt.Printf(Field: %s, Tag: %s\n, field.Name, field.Tag)}
}3.结构变量初始化
1.可以使用字段名初始化这样不需要按顺序未指定的字段为零值 emp1 : Employee{firstName: Sam,age: 25,salary: 500,lastName: “Anderson”, //逗号不能忽略}2.用字面量初始化按字段类型声明顺序并全部设置顺序不对或遗漏字段报错
emp2 : Employee{Thomas, Paul, 29, 800}4.访问修改字段值
1.采用 结构变量.字段
emp : Employee{Thomas, Paul, 29, 800}
fmt.Println(emp.age)2.采用*结构变量指针.字段
emp : Employee{Sam, Anderson, 55, 6000}
fmt.Println(First Name:, (*emp).firstName)3.采用 结构变量指针.字段不支持-
emp : Employee{Sam, Anderson, 55, 6000}
fmt.Println(First Name:, emp.firstName)4.匿名字段
结构体字段也可以省略字段名字段名默认为对应数据类型名称数据类型不能重复 type Person struct {stringint}p : Person{Naveen, 50}p.int 602.方法
方法是对具体类型行为的封装本质上是绑定到该类型的函数。
OO语言的方法通常有个隐藏的this或self指针来指向对象Go把这个隐藏指针暴露出来称为接受者。
func (t Type) funcName(paramList)(resultList)
func (t *Type) funcName(paramList)(resultList)1.方法实例
type Employee struct {name stringsalary intcurrency string
}
//定义方法
func (e Employee) displaySalary() {fmt.Printf(Salary of %s is %s%d, e.name, e.currency, e.salary)
}
func main() {emp1 : Employee{name: Sam Adolf,salary: 5000,currency: \(,}emp1.displaySalary()
}其实方法可以使用等价的函数实现具体如下所示
type Employee struct { name stringsalary intcurrency string
}
func displaySalary(e Employee) { fmt.Printf(Salary of %s is %s%d, e.name, e.currency, e.salary)
}
func main() { emp1 : Employee{name: Sam Adolf,salary: 5000,currency: \),}displaySalary(emp1)
}既然函数可以做那为什么还需要方法呢
在 Go 语言中方法和等价的函数都能完成类似的工作。虽然它们可以完成相同的任务但方法和函数之间存在一些差异和适用场景其中方法更适合于特定类型的操作和面向对象的编程。
GO 的函数不能重载导致不同类型不能用同名函数而不同类型的方法可以同名 GO 不支持class ,使用结构代替类结构字段用来封装对象属性方法用来封装对象的行为
此外方法并非结构体专有所有自定义类型都可以定义方法
type myInt int //自定义类型func (a *myInt) add(b myInt) myInt {return *a b
}num1 : myInt(5)
num2 : myInt(10)
sum : num1.add(num2)7.接口
Go语言中的接口是Duck模型的非侵入式接口与传统的接口不同非侵入式接口其具体类型实现接口不需要显式声明只要其方法集是接口的超集编译时会进行对应校验
GO 接口只有方法签名没有数据字段没有函数体代码
类型的方法集是多个接口的超集则实现多个接口
1.接口类定义
// 命令接口类型
type interfaceName interface{//接口类型命名通常以er为后缀methodName(paramList)(resultList)otherInterfaceName
}
// 匿名接口类型
interface{methodName(paramList)(resultList)otherInterfaceName
}而如果匿名接口中方法集为空即是interface{}是一种空接口所有的类型都实现了空接口都可以赋值或传递给空接口。
2.接口初始化
只声明未赋值的接口变量为nil 接口变量初始化需要把接口绑定到具体类型实例 未初始化的接口变量不能调用其方法 方法的接收者才能给接口变量赋值 接口变量的值包括底层类型的值和具体类型
package mainimport (fmt
)// 接口定义
type Speaker interface {Speak() string
}// 实现接口的结构体
type Dog struct{}// Dog 结构体实现 Speak 方法
func (d Dog) Speak() string {return Woof!
}// 创建接口的实例
func NewSpeaker() Speaker {return Dog{} // 返回一个 Dog 类型它满足了 Speaker 接口
}func main() {// 初始化接口并调用方法speaker : NewSpeaker()fmt.Println(speaker.Speak())
}此外一个接口可以包含一个或者多个接口即嵌套接口
type ReadWrite interface {Read(b Buffer) boolWrite(b Buffer) bool
}
type File interface{ReadWriteclose() bool
}3.接口类型断言
接口类型断言用来判断实现某个接口的变量是否为某个类型
若是则返回该类型的值和true
不是则返回该类型的零值和false
// interfaceName.(typeName)var a interface{} 56v, ok : a.(int)fmt.Println(v, ok)//56 truevar b interface{} truev, ok b.(int)fmt.Println(v, ok)//0 false4.接口类型查询
接口类型查询是使用swtich语句确定接口变量底层类型
.(type)只能用于switch表达式是因为变量底层类型判断只能用接口类型断言go 只能判断变量内存格式是否匹配某种类型并按某种类型来解析值
func findType(i interface{}) {switch i.(type) {//.(type)只能用于switch表达式case string:fmt.Printf(string and value is %s\n, i.(string))case int:fmt.Printf(int and value is %d\n, i.(int))default:fmt.Printf(Unknown type\n)}
}
findType(77)
findType(89.98)
// int and value is 77
// Unknown typ5.Stringer接口
Stringer 接口是 Go 语言中的一个接口它只包含一个方法String()用于返回该类型的字符串表示形式。这个接口通常被用来自定义类型的字符串输出格式。
在 Go 语言中如果某个类型实现了 Stringer 接口那么你可以使用 fmt 包中的打印方法如 Println 或 Sprintf来自定义该类型的输出方式。
以下是 Stringer 接口的定义
type Stringer interface {String() string
}接口的 String() 方法返回一个字符串。实现了 Stringer 接口的类型可以定义自己的 String() 方法以便自定义该类型的字符串输出。
以下是一个简单的示例演示了如何使用 Stringer 接口
package main
import (fmt
)
type Person struct {Name stringAge int
}
// 实现 Stringer 接口
func (p Person) String() string {return fmt.Sprintf(%s is %d years old, p.Name, p.Age)
}
func main() {person : Person{Alice, 30}fmt.Println(person) // 输出: Alice is 30 years old
}在这个例子中Person 类型实现了 Stringer 接口重写了 String() 方法这样当我们使用 fmt.Println 打印 Person 类型的变量时会调用 String() 方法并输出该类型的自定义字符串格式。
总结实现了Stringer接口的类型变量使用fmt.Println方法打印该对象时可以按照指定一定的格式输出与Java中重写toString方法类似。
6.Sorter接口
标准库的sort包定义排序要实现三个方法
//Len() 反映元素个数的方法
//Less(i, j) 比较第 i 和 j 个元素
//Swap(i, j) 交换第 i 和 j 个元素
// 具体Sorter接口定义如下
type Sorter interface {Len() intLess(i, j int) boolSwap(i, j int)
}请基于Sorter接口实现冒泡排序
package mainimport (fmt
)type Sorter interface {Len() intLess(i, j int) boolSwap(i, j int)
}type IntArray []intfunc (arr IntArray) Len() int {return len(arr)
}func (arr IntArray) Less(i, j int) bool {return arr[i] arr[j]
}func (arr IntArray) Swap(i, j int) {arr[i], arr[j] arr[j], arr[i]
}func BubbleSort(data Sorter) {n : data.Len()for i : 0; i n-1; i {for j : 0; j n-i-1; j {if data.Less(j1, j) {data.Swap(j, j1)}}}
}func main() {array : IntArray{64, 34, 25, 12, 22, 11, 90}fmt.Println(Unsorted array:, array)BubbleSort(array)fmt.Println(Sorted array:, array)
}7.接口特性
接口特性简称动静结合
接口静态特性
支持在编译阶段的类型检查当一个接口类型变量被赋值时编译器会检查右值的类型是否实现了该接口方法集合中的所有方法。
接口动态特性
即使用空接口变量可以使用不同类型的变量赋值
在运行时存储在接口类型变量中的值的真实类型。比如var i interface{} 13中接口变量i的动态类型为int。 在运行时可以被赋值为不同的动态类型变量从而支持运行时多态。
8.反射
变量的最基本信息是类型和值反射可以在程序运行时检查变量的类型和值
通过反射可以获取结构体变量的各字段信息甚至包括结构字段的tag信息
package main
import (fmtreflect
)
type Person struct {Id int //首字母大写表示公开字段Name stringSex string
}
func (this Person) Call() {fmt.Println(我正在打电话)
}
func getTypeAndValue(object interface{}) {//动态获取对象object的类型信息objectType : reflect.TypeOf(object)objectValue : reflect.ValueOf(object)fmt.Println(type , objectType.Name())fmt.Println(type , objectType, value , objectValue)// objectType.NumField() 获取字段的总数for i : 0; i objectType.NumField(); i {field : objectType.Field(i)value : objectValue.Field(i)fmt.Printf(type %d %v\n, i, field.Type)fmt.Printf(name %d %v\n, i, field.Name)fmt.Printf(value %d %v\n, i, value.Interface())}for i : 0; i objectValue.NumMethod(); i {method : objectValue.Method(i)method.Call(nil)}
}
func main() {person : Person{1, nancy, mail}getTypeAndValue(person)
}8.错误处理
Go语言里没有异常机制只有错误处理错误通过函数的多返回值来处理
Go语言的错误主要有编译时错误、运行时错误以及逻辑错误
Go语言的错误处理方式
1.可处理通过函数返回错误进行处理
2.不可处理通过panic抛出错误退出程序
1.error接口
通过error 接口 实现错误处理的标准模式打印错误时自动调用Error()函数
type error interface{Error() string
}可能出错的函数最后一个返回值为错误类型检查该返回值是否为nil是则处理错误否则正常调用 f, err : os.Open(/test.txt)if err ! nil {fmt.Println(err)return}fmt.Println(f.Name(), opened successfully)2.if快乐路径原则
“快乐路径”原则是编程中的一种设计理念其指导思想是使函数的主要路径尽可能保持“快乐”也就是函数的主要工作或逻辑能够尽快完成而不被意外情况干扰。
一个例子就是通过合理的错误检查和返回将错误处理逻辑放在函数的开头而将主要逻辑和处理放在函数的主要部分。这样可以尽早退出函数并返回错误但同时保持主要逻辑在函数主体内部让主逻辑尽可能快乐。
func PerformTask(param int) (result int, err error) {// 错误检查放在前面if param 0 {return 0, errors.New(param cannot be negative)}// 主逻辑放在主体内部// 这里是函数的主要逻辑称为快乐路径result param * 2return result, nil
}3.自定义错误
error是Go语言内置的接口类型只有一个方法Error()用于返回错误信息的字符串表示。
标准库中errors包提供了创建简单错误信息的函数。
package mainimport (errorsfmt
)func divide(a, b int) (int, error) {if b 0 {return 0, errors.New(division by zero)}return a / b, nil
}func main() {result, err : divide(6, 2)if err ! nil {fmt.Println(Error:, err)} else {fmt.Println(Result:, result)}result, err divide(3, 0)if err ! nil {fmt.Println(Error:, err)} else {fmt.Println(Result:, result)}
}此外还可以使用fmt包的Errorf 函数创建自定义错误
package mainimport (fmt
)
func someFunction() error {return fmt.Errorf(This is a more detailed error: %s, specific error message)
}func main() {err : someFunction()if err ! nil {fmt.Println(Error:, err)}
}当多处错误处理存在代码重复时可以使用goto集中处理错误
err : firstCheckError()
if err ! nil {goto onExit
}
err secondCheckError()
if err ! nil {goto onExit
}
// 正常处理代码
onExit:
fmt.Println(err)
exitProcess()4.panic
panic 是 Go 语言中的内建函数之一用于在发生不可恢复的错误时引发程序中止。panic 会停止当前函数的执行并向调用者传播一个引发恐慌的信号随后程序将被终止。
通常情况下panic 用于处理严重错误如数组越界、空指针引用等。当它被调用时程序将停止执行当前函数开始执行延迟defer函数然后程序会崩溃并显示 panic 产生的错误信息。 在遇到 panic 时程序的正常流程会被打破不会再继续执行当前任务。
在开发过程中尽量避免使用 panic而应该在可以预测和处理的情况下使用错误返回error returns或其他适当的处理方式因为 panic 不可恢复容易引起程序不稳定。
package main
import fmt
func someFunc() {// 模拟一个无法处理的错误err : someErrorOccurred()if err ! nil {panic(An unexpected error occurred: err.Error())}
}
func main() {fmt.Println(Starting the program.)someFunc()fmt.Println(End of the program.)
}5.recover
在 Go 语言中recover 函数用于恢复程序的执行从恐慌状态panic中恢复。recover 只有在延迟函数defer的内部调用时才会生效。
通常情况下recover 与 defer 配合使用以便在程序进入恐慌状态时恢复程序执行。
下面是一个使用 recover 来捕获并处理恐慌状态的示例
package mainimport (fmt
)func recoverDemo() {if r : recover(); r ! nil {fmt.Println(Recovered:, r)}
}func someFunc() {defer recoverDemo()// 模拟一个恐慌状态panic(Something went wrong!)
}func main() {fmt.Println(Starting the program.)someFunc()fmt.Println(End of the program.)
}在这个示例中recoverDemo 函数作为一个延迟函数defer在 someFunc 中执行。当 someFunc 函数引发了恐慌状态recoverDemo 中的 recover 函数捕获到这个恐慌然后打印出了错误信息。
请注意recover 函数只在延迟函数中有效。在非延迟函数中调用 recover 是无效的且只有在恐慌状态发生时才能捕获错误信息。
9.并发
1.进程、线程、协程
进程是程序在内存中运行时操作系统对其进行资源分配和调度的独立单位 线程是进程的一个执行实体是进程内部进行的一条执行路径是 CPU 调度和分派的基本单位它是比进程更小的能独立运行的基本单位 每个进程至少包括一个线程每个进程的初始线程被称为主线程主线程终止进程终止 协程是轻量级的线程一个线程可以拥有多个协程 进程和线程是操作系统级的协程是编译器级的。协程不被操作系统内核管理而完全由程序控制因此没有线程切换的开销。和多线程比数量越多协程的性能优势就越明显。协程的最大优势在于其轻量级可以轻松创建上万个而不会导致系统资源衰竭
2.goroutine
在Go语言中Goroutine是并发执行的基本单元。它们是Go运行时环境中的轻量级线程由Go调度器分配到逻辑处理器上执行。Goroutine的运行并不依赖于物理处理器或操作系统线程。每个逻辑处理器P负责运行Goroutine多个P可以运行在一个物理处理器CPU上。
3.协程通信
不要通过共享内存来通信而是通过通信来共享内存
协程间的通信常见是两种方式
1.共享数据很多语言采用共享内存来实现程序数据同步确保程序以合乎逻辑的方式执行。在程序执行过程中进程或线程可能对共享数据加锁以禁止其他进程或线程修改它。总体编程复杂性高
2.消息机制每个并发单元是独立个体多个并发单元的数据不共享通过消息通信来同步数据。
4.channel通道
通道是一种特殊的类型同时只能有一个 goroutine 访问通道进行发送和获取数据。
通道写入和读取使用 - 运算符 写入 通道-变量 读取 变量-通道
5.缓冲通道
通道包括无缓冲通道和有缓冲通道 无缓冲通道 make(chan datatype) 有缓冲通道 make(chan datatypecapacity)
无缓冲通道只能存储一条消息有缓冲通道可以根据make函数的capacity参数存储n条消息按FIFO读出
func receiver(c chan string) {for msg : range c {fmt.Println(msg)}
}
func main() {messages : make(chan string, 2)messages - hellomessages - worldgo receiver(messages) time.Sleep(time.Second * 1)
}//hello world 此外还能使用内置函数返回缓冲通道状态
len()获取通道当前缓存数 cap()获取通道缓存容量 ch : make(chan string, 3)ch - naveench - paulfmt.Println(capacity is, cap(ch))fmt.Println(length is, len(ch))fmt.Println(read value, -ch)fmt.Println(new length is, len(ch))//capacity is 3//length is 2//read value naveen//new length is 1无缓冲通道写入等待读取读取等待写入在双方准备好之前是阻塞的
有缓冲通道通道已满时的写入会等待通道已空的读取会等待
6.关闭通道
关闭通道使用内置函数close(),实际上是关闭写入即发送者告诉接收者不会再有数据发往通道
接收者能够在通道接收数据的同时获取通道是否已关闭的参数 。
func producer(chnl chan int) { for i : 0; i 10; i {chnl - i}close(chnl)
}
func main() { ch : make(chan int)go producer(ch)for {v, ok : -chif ok false {break}fmt.Println(Received , v, ok)}
}此外for range能够自动判断通道是否关闭具体代码如下所示
func producer(chnl chan int) { for i : 0; i 10; i {chnl - i}close(chnl)
}
func main() { ch : make(chan int)go producer(ch)for v : range ch {fmt.Println(Received ,v)}
}7.WaitGroup
sync.WaitGroup 用于等待一组 Go 协程执行完成后再执行主程序的方法。它提供了一个简单的机制以便主程序知道其他所有协程何时执行完成。
在使用 sync.WaitGroup 时主要有三个函数
Add(int)增加要等待的协程数量。Done()标志已完成的协程。Wait()等待所有的协程都完成。
通常Add 函数用于计数要等待的协程数量然后在协程的函数中使用 Done 标志已经执行完毕最后使用 Wait 阻塞主程序直到所有协程都执行完毕。 以下是一个示例演示了 sync.WaitGroup 的用法
package mainimport (fmtsynctime
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done() // 标志协程完成fmt.Printf(Worker %d starting\n, id)time.Sleep(time.Second) // 模拟工作fmt.Printf(Worker %d done\n, id)
}func main() {var wg sync.WaitGroupfor i : 1; i 5; i {wg.Add(1) // 增加等待的协程数量go worker(i, wg)}wg.Wait() // 等待协程执行完成fmt.Println(All workers have finished)
}在这个示例中我们启动了五个协程每个协程模拟一些工作通过 time.Sleep 模拟。Add 用于增加要等待的协程数量Done 标志协程已执行完成而 Wait 阻塞了主程序直到所有的协程都执行完毕。
8.猜数字例题
这里分享一道使用协程的例题具体例题如下感兴趣的小伙伴可以尝试一下
9.计时器Timer
协程间的通信需设置超时等辅助机制
一次性计时器定时器只计时一次结束便停止
package mainimport (fmttime
)func main() {timer1 : time.NewTimer(2 * time.Second)-timer1.C // 阻塞等待定时器信号fmt.Println(Timer 1 expired)timer2 : time.NewTimer(1 * time.Second)go func() {-timer2.Cfmt.Println(Timer 2 expired)}()stop2 : timer2.Stop() // 停止定时器2if stop2 {fmt.Println(Timer 2 stopped)}
}10.定时器Ticker
周期性定时器定时器周期性进行计时除非主动停止否则将永久运行
在Go语言中time.Ticker 是用于重复间隔性触发操作的工具。与 time.Timer 不同time.Ticker 会在一定的时间间隔内重复向通道发送时间事件。
主要方法如下所示
func NewTicker(d Duration) *Ticker 指定一个时间创建一个Ticker Ticker一经创建便开始计时不需要额外的启动命令
func (t *Ticker) Stop() 停止计时但管道不会被关闭示例代码如下所示
package mainimport (fmttime
)func main() {ticker : time.NewTicker(1 * time.Second)defer ticker.Stop()for {select {case -ticker.C:fmt.Println(Ticker ticked)}}
}11.select
多路复用是在一个信道上传输多路信号或数据流比如网线
select 借用网络多路复用的概念用于监听多个通道同时响应多个通道
多个通道都没有可写或可读的状态select 会阻塞 有一个通道是可写或可读的 select 会执行该通道语句 有多个通道是可写或可读的 select 会随机选择其中一个执行
select 语句是 Go 语言用于处理通道操作的关键工具。它可以同时监听多个通道操作一旦某个通道可操作有消息可以接收或发送就执行相应的 case 语句。select 语句有点类似于 switch 语句但是专门用于通道的操作。
下面是一个示例演示了 select 语句的用法
package mainimport (fmttime
)func main() {ch1 : make(chan string)ch2 : make(chan string)go func() {time.Sleep(2 * time.Second)ch1 - one}()go func() {time.Sleep(1 * time.Second)ch2 - two}()for i : 0; i 2; i {select {case msg1 : -ch1:fmt.Println(Received, msg1)case msg2 : -ch2:fmt.Println(Received, msg2)}}
}在这个示例中通过两个协程向两个不同的通道 ch1 和 ch2 发送消息。select 语句会监听这两个通道的状态一旦有数据可接收就执行相应的 case 语句最终输出接收到的消息。
12.Mutex
多个线程同时竞争使用某个变量可能会导致结果失控
mutex互斥锁用来保证某个变量在任一时刻只能有一个线程访问mutex 用Lock()和Unlock()来创建资源的临界区这一区间内的代码是线程安全的任何一个时间点都只能有一个goroutine执行这段区间的代码
mutex 也可以用通道来代替通道底层基于mutex即mutex性能更高通常不涉及线程交互数据的用mutex其他性能要求不敏感用通道
package mainimport (fmtsync
)var count 0
var mutex sync.Mutexfunc increment() {mutex.Lock() // 通过 Lock() 方法锁住共享资源countmutex.Unlock() // 通过 Unlock() 方法解锁共享资源
}func main() {var wg sync.WaitGroupfor i : 0; i 1000; i {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println(Count:, count)
}13.RWMutex
Mutex 在大量并发时同一时刻只有一个协程持有锁其他阻塞等待性能下降 RWMutex在Mutex的基础之上增加了读、写的信号量并使用了类似引用计数的读锁数量可使多个协程持有读锁适合应用在具有一定并发量且读多写少的场合。
注意
RWMutex中可以申请多个读锁有读锁时申请写锁将会被阻塞
只要有写锁后序申请读锁和写锁都会被阻塞
主要方法如下
func (rw *RWMutex) Lock() //申请写锁
func (rw *RWMutex) Unlock() //释放写锁
func (rw *RWMutex) RLock() //申请读锁
func (rw *RWMutex) RUnlock()//释放读锁package main
import (fmtsync
)var sharedData int
var rwMutex sync.RWMutexfunc readData() {rwMutex.RLock() // 读取共享资源时使用 RLock() 方法defer rwMutex.RUnlock()fmt.Println(Read Data:, sharedData)
}func writeData(value int) {rwMutex.Lock() // 写入共享资源时使用 Lock() 方法defer rwMutex.Unlock()sharedData valuefmt.Println(Write Data:, value)
}func main() {// 读取数据for i : 0; i 5; i {go readData()}// 写入数据for i : 0; i 5; i {go writeData(i)}// 等待所有协程执行完毕fmt.Scanln()
}
- 上一篇: 常州做网站公司电子商务模拟实训报告企业网站建设
- 下一篇: 常州做网站要多少钱网络运营课程培训视频
相关文章
-
常州做网站公司电子商务模拟实训报告企业网站建设
常州做网站公司电子商务模拟实训报告企业网站建设
- 技术栈
- 2026年03月21日
-
常州做网站的 武进企业视频网站模板
常州做网站的 武进企业视频网站模板
- 技术栈
- 2026年03月21日
-
常州做网站的 武进关于企业网站建设数据现状分析
常州做网站的 武进关于企业网站建设数据现状分析
- 技术栈
- 2026年03月21日
-
常州做网站要多少钱网络运营课程培训视频
常州做网站要多少钱网络运营课程培训视频
- 技术栈
- 2026年03月21日
-
常州做网站一般多少钱网站建设好之后怎么上传东西
常州做网站一般多少钱网站建设好之后怎么上传东西
- 技术栈
- 2026年03月21日
-
厂字型布局网站例子快抖霸屏乐云seo
厂字型布局网站例子快抖霸屏乐云seo
- 技术栈
- 2026年03月21日






