文章目录
函数进阶
结构体
接口
继承
type
值类型与引用类型
值传递、引用传递
打包、引用包
工具类打包文件代码
引用包代码
方法
异常捕捉处理
字符串常用函数
日期常用函数
管道(channel)
书接上篇:极速Go语言入门(超全超详细)-基础篇
整个基础篇合计32000字左右,如有遗漏可以私信我进行补充
函数进阶
以下展示函数return的使用、普通函数、匿名函数、闭包、defer延迟执行、函数作为参数传递、函数作为函数返回值、内置函数
go内置函数可参考: 中文官方文档中**builtin**(点击跳转)一栏
示例代码
package main import ( "fmt" "strconv" ) func main() { println("-------return测试---------") result_1, result_2 := returnTest(88, 99) result_3, result_4 := returnTestV2(88, 99) fmt.Printf("returnTest执行结果, result_1 = %v, result_2 = %v \n", result_1, result_2) fmt.Printf("returnTestV2执行结果, result_3 = %v, result_4 = %v \n", result_3, result_4) println("-------函数测试---------") testFuntion(1) funcResultV2_1, funcResultV2_2 := testFuntionV2(1, "123") fmt.Printf("funcResultV2_1 = %v, funcResultV2_2 = %v \n", funcResultV2_1, funcResultV2_2) testFuntionV3() testFuntionV3(1, 2, 3, 4, 5) println("-------匿名函数测试---------") //匿名函数,没有名字的函数,放在代码块中直接执行 anonymousFuncResult_1, _ := func(n1 int, n2 int) (int, float64) { return n1 * n2, float64(n1 * n2) }(2, 8) //尾部的括号里传递参数 fmt.Println("anonymousFuncResult = ", anonymousFuncResult_1) //也可以直接把匿名函数赋值给变量,但是赋值给变量之前不能给匿名函数传递参数 a := func(n1 int, n2 int) (int, int) { return n2, n1 } //尾部没有参数 n1 := 10 n2 := 29 n1, n2 = a(n1, n2) fmt.Printf("匿名函数赋值给变量之后n1 = %v, n2 = %v \n", n1, n2) println("-------全局匿名函数测试---------") //调用定义好的匿名函数全局变量 globalVariableFuncResult_1, globalVariableFuncResult_2 := globalVariableFunc(1, 3) fmt.Printf("调用全局匿名函数结果globalVariableFuncResult_1 = %v type: %T, globalVariableFuncResult_1 = %v type: %T\n", globalVariableFuncResult_1, globalVariableFuncResult_1, globalVariableFuncResult_2, globalVariableFuncResult_2) //闭包调用 f2 := closureTest() //依下述输出来看,虽然我们没有显性的声明一个全局变量,但是我们每次调用都会进行累加 fmt.Printf("闭包调用第1次返回结果:%v \n", f2(10)) fmt.Printf("闭包调用第2次返回结果:%v \n", f2(10)) fmt.Printf("闭包调用第3次返回结果:%v \n", f2(10)) f3 := closureTest() fmt.Printf("新的闭包调用第1次返回结果:%v \n", f3(10)) //新的实例运行后并不会原先闭包函数中的变量 println("-------defer测试---------") //defer:defer是go中一种延迟调用机制,defer后面的函数只有在当前函数执行完毕后才能执行,通常用于释放资源。 //参考资料:https://blog.csdn.net/m0_46251547/article/details/123762669 deferTest("字符参数") println("-------函数作为参数测试---------") testFuncArg(printString) println("-------函数作为返回值测试---------") returnFunc := testReturnFunc() fmt.Printf("函数作为返回值测试, 返回结果类型 = %T \n", returnFunc) finalResult := returnFunc(100, 200) fmt.Printf("执行函数, 结果 = %d, 类型 = %T \n", finalResult, finalResult) println("-------内置函数测试---------") //1. len : 用来求长度,比如string、array、slice、map、channel //2. new : 用来分配内存,主要用来分配值类型,比如int、float32、struct…返回的是指针 //3. make:用来分配内存,主要用来分配引用类型,比如chan、map、slice。 //值类型的用new,返回的是一个指针 p := new(int) fmt.Println("*p = ", *p, ", p = ", p) *p = 29 fmt.Println("*p = ", *p) //引用类型的用make stringStringMap := make(map[string]string, 10) //向map增加元素 stringStringMap["name"] = "Mir Li" stringStringMap["age"] = "18" } /** 如下所示,函数定义 func 函数名 (参数列表) (返回值列表) { //返回值只有一个时可以不写() //函数体,功能执行 return 返回值列表 } */ // ///return/// func returnTest(n1 int64, n2 int64) (string, int64) { //return:用于函数执行结果的值返回,可以返回一个或者多个参数 result_1 := strconv.FormatInt(n1*n2, 10) result_2 := n1 + n2 return result_1, result_2 } // 上面的写法还可以这些写 func returnTestV2(n1 int64, n2 int64) (result_1 string, result_2 int64) { result_1 = strconv.FormatInt(n1*n2, 10) result_2 = n1 + n2 return } // //普通函数// // 单个入参、单个出参 func testFuntion(number int) int { //()里的是入参, {左边的是返回值类型和参数, 可以写(result, int) return number * 10 } func testFuntionV2(number int, value string) (a int, b string) { //转化成10进制,进行字符串拼接 resultStr := strconv.FormatInt(int64(number), 10) + value return number, resultStr } // 多个入参、单个出参 func testFuntionV3(args ...int) (int, string, float64) { //(args ...int)表示可以传递多个参数,可以理解为0到多个参数 resultNum := 0 for arg := range args { fmt.Println("打印参数arg = ", arg) resultNum += arg } resultStr := "返回值2" resultFloatNum := 888.88 fmt.Printf("最终返回值1=%v, 最终返回值2=%v, 最终返回值3=%v \n", resultNum, resultStr, resultFloatNum) return resultNum, resultStr, resultFloatNum } // init函数,最大的作用是用来初始化源文件,该函数会在main函数执行前被调用 func init() { //do something fmt.Println("初始化函数,先于main函数执行") } // ///匿名函数(全局变量)/// var ( globalVariableFunc = func(number_1 int, number_2 int) (int, string) { return number_2 * number_1, strconv.FormatInt(int64(number_1), 10) } ) // ///闭包/// // 含义:闭包是由函数和与其相关的引用环境组合而成的实体[抽象、难以理解!],其实就是:匿名函数+外部引用 // 为什么使用闭包(为了避免全局变量被滥用):可以让变量常驻内存、可以让变量不污染全局 // 可参考文章:https://blog.csdn.net/qq_27654007/article/details/116667624 func closureTest() func(int) int { var n int = 10 return func(x int) int { //每一次调用都会给n进行累加赋值,n的值在内存中伴随整个闭包实例的整个生命周期 n = n + x return n } } // ///defer/// func deferTest(strValue string) string { //依据打印输出可以看出来,defer这行的逻辑是在return那一刻执行的 defer printString("defer 延迟执行测试") printString("deferTest 执行测试-1") printString("deferTest 执行测试-2") return strValue } func printString(str string) { fmt.Println(str) } // ///函数作为参数/ func testFuncArg(param func(str string)) { param("函数作为参数测试") } // ///函数作为返回值/ func testReturnFunc() func(result_1 int64, result_2 int64) int64 { return func(n1 int64, n2 int64) int64 { return n1 * n2 } }
运行结果
初始化函数,先于main函数执行 -------return测试--------- returnTest执行结果, result_1 = 8712, result_2 = 187 returnTestV2执行结果, result_3 = 8712, result_4 = 187 -------函数测试--------- funcResultV2_1 = 1, funcResultV2_2 = 1123 最终返回值1=0, 最终返回值2=返回值2, 最终返回值3=888.88 打印参数arg = 0 打印参数arg = 1 打印参数arg = 2 打印参数arg = 3 打印参数arg = 4 最终返回值1=10, 最终返回值2=返回值2, 最终返回值3=888.88 -------匿名函数测试--------- anonymousFuncResult = 16 匿名函数赋值给变量之后n1 = 29, n2 = 10 -------全局匿名函数测试--------- 调用全局匿名函数结果globalVariableFuncResult_1 = 3 type: int, globalVariableFuncResult_1 = 1 type: string 闭包调用第1次返回结果:20 闭包调用第2次返回结果:30 闭包调用第3次返回结果:40 新的闭包调用第1次返回结果:20 -------defer测试--------- deferTest 执行测试-1 deferTest 执行测试-2 defer 延迟执行测试 -------函数作为参数测试--------- 函数作为参数测试 -------函数作为返回值测试--------- 函数作为返回值测试, 返回结果类型 = func(int64, int64) int64 执行函数, 结果 = 20000, 类型 = int64 -------内置函数测试--------- *p = 0 , p = 0x140000a6030 *p = 29
结构体
之前的变量只能定义一个使用一个,有时候可能需要很多个变量,当出现这种情况的时候我们可以使用struct结构体,
struct定义结构,结构由字段(field)组成,每个field都有所属数据类型,在一个struct中,每个字段名都必须唯一。
Go中不支持面向对象,面向对象中描述事物的类的重担由struct来挑。比如面向对象中的继承,可以使用组合(composite)来实现:struct中嵌套一个(或多个)类型。
结构如下
type UserInfo struct { field1 type1 field2 type2 … } // 或者 type T struct { a, b int }
理论上,每个字段都是有具有唯一性的名字的,但如果确定某个字段不会被使用,可以将其名称定义为空标识符_
来丢弃掉:
type T struct { _ string a int }
示例代码
package main import ( "fmt" ) type UserInfo struct { //用户姓名 name string //年龄、身高 age, height int16 //相同类型可以定义在一行 //用户地址 address string } // 如果里面的字段类型都一样可以使用这种方法 type UserInfoV2 struct{ name, address string } // 组合型 type UserInfoV3 struct { user_1 UserInfo user_2 UserInfoV2 } func main() { //初始化结构体 info := UserInfo{age: 18, name: "小明", height: 180, address: "浙江省杭州市"} fmt.Printf("结构体信息:%v \n", info) //访问结构体属性,info.xxx fmt.Printf("访问name信息:%v, age:%v, 访问height信息:%v, 访问address信息:%v \n", info.name, info.age, info.height, info.address) //分配内存地址连续 fmt.Printf("name分配内存地址:%v, age分配内存地址:%v, height分配内存地址:%v, address分配内存地址:%v \n", &info.name, &info.age, &info.height, &info.address) //组合型结构体 user_1 := UserInfo{age: 33, name: "Mir Zhang", height: 180, address: "北京"} user_2 := UserInfoV2{name: "Mir Li", address: "北京"} user_3 := UserInfoV3{user_1, user_2} fmt.Printf("结构体信息:%s \n", user_3) }
运行结果
初始化函数,先于main函数执行 结构体信息:{小明 18 180 浙江省杭州市} name:小明, age:18, height:180, address:浙江省杭州市 name分配内存地址:0x14000112180, age分配内存地址:0x14000112190, height分配内存地址:0x14000112192, address分配内存地址:0x14000112198 结构体信息:{{Mir Zhang %!s(int16=33) %!s(int16=180) 北京} {Mir Li 北京}}
接口
interface类型可以定义一组方法,不需要实现。并且interface不能包含任何变量。
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
就是定义一个规范,让实现接口的实例按照这个规范来实现自己的逻辑
以以下示例来讲,定义一个动物接口,里面有发出声音的接口,实现接口的动物实例按照各自的业务逻辑来实现这个发出声音的方法
代码示例
package main import "fmt" func main() { //根据不同的实例调用对应的实例方法 doVoice(Dark{name: "小鸭子"}) doVoice(new(Cat)) } // 定义一个接口 type Animal interface { // type 接口名 interface voice() } // 定义结构体 type Dark struct { name string } type Cat struct { name string } // 实现接口方法 func (dark Dark) voice() { // 接口是引用类型,所以这里传递的是变量的引用 fmt.Printf("%v 嘎嘎叫 \n", dark.name) } func (cat Cat) voice() { // 接口是引用类型,所以这里传递的是变量的引用 fmt.Printf("喵喵叫 \n") } func doVoice(animal Animal) { animal.voice() }
运行结果
小鸭子 嘎嘎叫 喵喵叫
继承
在java中继承相当于拥有父类的一些属性和方法
go里面继承可以通过嵌套匿名结构体来实现
代码示例
package main import ( "fmt" ) func main() { father := Father{name: "父亲", phone: "13100001111"} fmt.Printf("父亲属性: %d \n", father) son := new(Son) son.hobby = "打篮球" son.name = "儿子" son.phone = "13033331111" fmt.Printf("儿子属性: %d \n", son) } type Father struct { name, phone string } type Son struct { Father //匿名结构体 hobby string }
运行结果
父亲属性: {%!d(string=父亲) %!d(string=13100001111)} 儿子属性: &{{%!d(string=儿子) %!d(string=13033331111)} %!d(string=打篮球)}
type
type用于类型定义(type definition)与类型别名(type alias),可以理解就是定义一个类型或者给一个类型起别名
示例代码
package main import ( "fmt" "unsafe" ) // 定义类型 type UserMsg struct { name, phone string } // 类型起别名 type myIntType int64 //结构体起别名 type MyUserMsg UserMsg func main() { //使用起了别名的类型 var number_test myIntType = 999 fmt.Printf("number_test = %v, 类型:%T, 大小:%v字节 \n", number_test, number_test, unsafe.Sizeof(number_test)) myUserMsg := MyUserMsg{name: "123", phone: "13111110000"} fmt.Printf("myUserMsg = %v, 类型:%T, 大小:%v字节 \n", myUserMsg, myUserMsg, unsafe.Sizeof(myUserMsg)) }
运行结果
number_test = 999, 类型:main.myIntType, 大小:8字节 myUserMsg = {123 13111110000}, 类型:main.MyUserMsg, 大小:32字节
值类型与引用类型
值类型:基本数据类型、数组、结构体。变量直接存储值,通常存储于栈中,函数传参时使用值传递
引用类型:指针、切片、映射、管道、接口等。变量存储的是值的地址,通常存储于堆中,会发生GC,函数传参时使用引用传递。
值传递、引用传递
值传递:使用按值传递来传递参数,也就是传递参数的副本。在函数中对副本的值进行更改操作时,不会影响到原来的变量。
引用传递:传递的是一个地址的拷贝,通过它可以修改这个值所指向的地址上的值。
在函数调用时,引用类型(slice、map、interface、channel)都默认使用引用传递,另外使用指针也可以进行引用传递。
package main import ( "fmt" "unsafe" ) func main() { //值传递测试 var number int64 = 999 testV1(number) //从这里看出调用函数赋值后并不会改变原值 fmt.Println("main()当前number=", number) //应用传递测试 var i int = 10 fmt.Printf("i当前的值 = %v \n", i) //1、ptr是一个指针变量 2、ptr存的是i变量的地址 3、类型是*int var ptr *int = &i //赋予新值 *ptr = 20 fmt.Printf("指针存储的值:%v, 类型:%T, 占内存字节数:%d, 指针存储地址指向的值:%d, 指针的地址:%v \n", ptr, ptr, unsafe.Sizeof(ptr), *ptr, &ptr) fmt.Printf("i修改后的值 = %v \n", i) } func testV1(number int64) { number = 1000 fmt.Println("testV1()当前number=", number) }
运行结果
testV1()当前number= 1000 main()当前number= 999 i当前的值 = 10 指针存储的值:0x14000114018, 类型:*int, 占内存字节数:8, 指针存储地址指向的值:20, 指针的地址:0x14000100020 i修改后的值 = 20
打包、引用包
有时候我们除了引用go提供的官方包,也可能需要我们自己打包后在其他模块引用封装好的包
如我当前项目文件名为:MyTest
在项目文件路径下创建了packageUtils文件夹,在文件夹下创建了UtilsPKG.go文件
图示如下:
工具类打包文件代码
package packageUtils func NumberUtils(number_1 int64, number_2 int64, operation string) (resultNumber float64) { switch operation { case "*": resultNumber = float64(number_2 * number_1) break case "/": resultNumber = float64(number_2 / number_1) break case "*+": resultNumber = float64(number_2 + number_1) break case "-": resultNumber = float64(number_2 - number_1) break } return }
引用包代码
在其他路径下随意创建一个文件
package main import ( "MyTest/packageUtils" //这块就是导入了我们上面打好的包 "fmt" ) func main() { resultNum := packageUtils.NumberUtils(100, 200, "*") fmt.Printf("执行包调用函数结果:%v", resultNum) }
运行结果
包调用函数返回值: 200
方法
方法虽不同于函数,是两个概念性的东西,但是方法和函数有些相似,如果之前没有接触过go方法和函数相关的知识,一定会犯迷糊,下面我们看看方法的用法,go中方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct。
go方法的声明(定义)
方法必必须要有一个接收者(t type),这个接收者是一个类型,这样方法就和这个类型绑定在一起,称为这个类型的方法。
func (t type) methodName (参数列表) (返回值列表){ 方法体 return 返回值 }
代码示例
package main import ( "fmt" "strconv" "unsafe" ) func main() { var number_test MyInt = 888 result_num := number_test.testMethod(999) println("testMethod invoke result:", result_num) personInfo := Person{Age: 20, Name: "Mr F"} result := personInfo.testMethodV2() fmt.Printf("testMethodV2 invoke result:%v, type:%T", result, result) } type MyInt int64 func (receiver MyInt) testMethod(number_1 int64) string { fmt.Println("this is a data type receiver,", receiver) return "-------" + strconv.FormatInt(number_1, 10) + "-------" } type Person struct { Name string Age int } func (receiver Person) testMethodV2() Person { fmt.Println("this is a struct receiver,", receiver) return receiver }
运行结果
this is a data type receiver, 888 testMethod invoke result: -------999------- this is a struct receiver, {Mr F 20} testMethodV2 invoke result:{Mr F 20}, type:main.Person
异常捕捉处理
go语言中没有像java中一样的try catch finally处理模块,
代码示例
package main import ( "fmt" "strconv" "strings" "unsafe" ) func main() { //测试异常捕捉处理 testExceptionCapture(); fmt.Println("异常捕捉后继续处理流程") //测试异常panic testPanic(); //上面函数执行报错后如果没有异常捕捉处理程序直接结束运行 fmt.Println("异常捕捉处理测试") } func testPanic() { n1 := 1 n2 := 0 n3 := n1 / n2 //发送异常之后,下面的输出语句不会输出,程序直接结束 fmt.Println("res:", n3) } func testExceptionCapture() { defer func() { if err := recover(); err != nil { fmt.Println("捕获到的异常信息: ", err) //异常之后做一些事情 fmt.Println("发送钉钉告警或者邮件告警等") } }() n1 := 1 n2 := 0 n3 := n1 / n2 //发送异常之后,下面的输出语句不会输出 fmt.Println("res:", n3) }
运行结果
捕获到的异常信息: runtime error: integer divide by zero 发送钉钉告警或者邮件告警等 异常捕捉后继续处理流程 panic: runtime error: integer divide by zero goroutine 1 [running]: main.testPanic() /Users/dasouche/go/src/MyTest/TwoTest.go:27 +0x1c main.main() /Users/dasouche/go/src/MyTest/TwoTest.go:18 +0x64
字符串常用函数
参考中文文档strings包相关:https://studygolang.com/static/pkgdoc/pkg/strings.htm
代码示例
package main import ( "fmt" "strconv" "strings" "time" "unsafe" ) func main() { //统计字符串的长度,按字节 len(str) var str string = "hello word" fmt.Printf("str长度:%v \n", len(str)) //字符串遍历,同时处理有中文的问题 r := []rune(str) r := []rune(str) for i := range r { fmt.Printf("遍历字符串:%v \n", i) } //字符串转整数: n , err := strconv.Atoi(“12”) number_1, _ := strconv.Atoi("12") number_2, err_2 := strconv.Atoi("test") fmt.Printf("字符串转整数,number_1:%v \n", number_1) fmt.Printf("字符串转整数,number_2:%v, 错误信息:%v \n", number_2, err_2) //整数转字符串: str = strconv.Itoa(12345)、strconv.FormatInt(999, 10) str = strconv.Itoa(12345) fmt.Printf("整数转字符串,str:%v \n", str) str = strconv.FormatInt(999, 10) fmt.Printf("整数转字符串,str:%v \n", str) //字符串转[]byte: var bytes= []byte(“hello go”) var bytes = []byte("hello word") fmt.Printf("字符串转byte数组,str:%v \n", bytes) //[]byte转字符串: str = string([]byte{97,98,99}) str = string([]byte{97, 98, 99}) fmt.Printf("byte转字符串,str:%v \n", str) //10进制转2,8,16进制: str = strconv.FormatInt(123,2) // 2->8,16S str = strconv.FormatInt(123, 2) fmt.Printf("数字转2进制字符串,str:%v \n", str) str = strconv.FormatInt(123, 8) fmt.Printf("数字转8进制字符串,str:%v \n", str) str = strconv.FormatInt(123, 10) fmt.Printf("数字转10进制字符串,str:%v \n", str) str = strconv.FormatInt(123, 16) fmt.Printf("数字转16进制字符串,str:%v \n", str) //查找子串是否在指定的字符串中: strings.Contains(“seafood”, “foo”) //true isContains := strings.Contains("food foo eat", "foo") fmt.Printf("是否包含字符串foo,isContains:%v \n", isContains) //统计一个字符串有几个指定的子串:strings.Count(“ceheese”, “e”) //4 countNum := strings.Count("hello word", "l") fmt.Printf("字符串含有%d个l:%v \n", countNum) //不区分大小写的字符串比较(== 是区分字母大小写的): fmt.PrintIn(strings.EqualFold(“abc”, “Abc”) // true isEqual := strings.EqualFold("abc", "Abc") fmt.Printf("字符串不区分大小写比较是否相等:%v \n", isEqual) fmt.Printf("字符串区分大小写比较是否相等:%v \n", "abc" == "Abc") //返回子串在字符串第一次出现的index值,如果没有返回-1 : strings.Index(“NLT_abc”,”abc”) //4 firstIndex := strings.Index("hello word hello word", "ll") fmt.Printf("字符子串在字符串中第一次出现的地方:%v \n", firstIndex) //返回子串在字符串最后一次出现的index,如没有返回-1 : strings.LastIndex(“go golang” , “go”) lastIndex := strings.LastIndex("hello word hello word", "ll") fmt.Printf("字符子串在字符串中最后一次出现的地方:%v \n", lastIndex) //将指定的子串替换成 另外一个子串: strings.Replace(“go go hello” , “go”, “go语言”, n) n 可以指定你希望替换几个,如果n = -1表示全部替换 str = strings.Replace("hello word hello word", "ll", "ll-", 2) fmt.Printf("替换后的字符串:%v \n", str) //按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组: splitData := strings.Split("Mr Li,20,北京朝阳区", ",") fmt.Printf("分割后的数据:%v \n", splitData) //将字符串的字母进行大小写的转换: strings.ToLower(“Go”) // go strings.ToUpper(“Go”) //GO str = strings.ToLower("HELLO Word") fmt.Printf("转换成小写后的数据:%v \n", str) str = strings.ToUpper("hello Word") fmt.Printf("转换成大写后的数据:%v \n", str) //将字符串左右两边的空格去掉 : strings.TrimSpace(“ tn a lone gopher ntrn “) str = strings.TrimSpace(" hello, word ") fmt.Printf("去掉空格后的数据:%v \n", str) //将字符串左右两边指定的字符去掉: strings.Trim(“! hello! “, “ !”) // [“hello”]//将左右两边!和””去掉 str = strings.Trim("! hello, word !", "!") fmt.Printf("去掉!后的数据:%v \n", str) //将字符串左边指定的字符去掉: strings.TrimLeft(“! hello! “,” !”) // [“hello”]//将左边!和”“去掉 str = strings.TrimLeft("! hello, word !", "!") fmt.Printf("去掉左边!后的数据:%v \n", str) //将字符串右边指定的字符去掉: strings.TrimRight(“! hello! “,” !”) // [“hello”]//将右边!和””去掉 str = strings.TrimRight("! hello, word !", "!") fmt.Printf("去掉右边!后的数据:%v \n", str) //判断字符串是否以指定的字符串开头: strings.HasPrefix(“ftp://192.168.10.1" ,”ftp”) // true hasPrefix := strings.HasPrefix("! hello, word !", "!") fmt.Printf("是否以!开头:%v \n", hasPrefix) //判断字符串是否以指定的字符串结束: strings.HasSuffix(“‘NLT_abc.jpg”,”abc”) //false hasSuffix := strings.HasSuffix("! hello, word !", "!") fmt.Printf("是否以!结束:%v \n", hasSuffix) //判断字符串是否包含一个字符串 hasContains := strings.Contains("hello word", "hello") fmt.Printf("字符串hello word是否包含hello:%v \n", hasContains) }
运行结果
str长度:10 遍历字符串:0 遍历字符串:1 遍历字符串:2 遍历字符串:3 遍历字符串:4 遍历字符串:5 遍历字符串:6 遍历字符串:7 遍历字符串:8 遍历字符串:9 字符串转整数,number_1:12 字符串转整数,number_2:0, 错误信息:strconv.Atoi: parsing "test": invalid syntax 整数转字符串,str:12345 整数转字符串,str:999 字符串转byte数组,str:[104 101 108 108 111 32 119 111 114 100] byte转字符串,str:abc 数字转2进制字符串,str:1111011 数字转8进制字符串,str:173 数字转10进制字符串,str:123 数字转16进制字符串,str:7b 是否包含字符串foo,isContains:true 字符串含有2个l:%!v(MISSING) 字符串不区分大小写比较是否相等:true 字符串区分大小写比较是否相等:false 字符子串在字符串中第一次出现的地方:2 字符子串在字符串中最后一次出现的地方:13 替换后的字符串:hell-o word hell-o word 分割后的数据:[Mr Li 20 北京朝阳区] 转换成小写后的数据:hello word 转换成大写后的数据:HELLO WORD 去掉空格后的数据:hello, word 去掉!后的数据: hello, word 去掉左边!后的数据: hello, word ! 去掉右边!后的数据:! hello, word 是否以!开头:true 是否以!结束:true 字符串hello word是否包含hello:true
日期常用函数
参考中文文档time包相关:https://studygolang.com/static/pkgdoc/pkg/time.htm
代码示例
package main import ( "fmt" "strconv" "strings" "time" "unsafe" ) func main() { println("-----常用日期函数测试----") //返回当前系统时间 currentTime := time.Now() fmt.Printf("当前系统时间=%v \n", currentTime) //返回当前系统时间年、月、日、日、时、分、秒、毫秒 date, month, day := currentTime.Date() fmt.Printf("当前系统年=%v,月=%v,日=%v \n", date, month, day) currentTimeYear := currentTime.Year() currentTimeDay := currentTime.Day() currentTimeMonth := currentTime.Month() currentTimeHour := currentTime.Hour() currentTimeMinute := currentTime.Minute() currentTimeSecond := currentTime.Second() //时间戳 currentTimeUnixMilli := currentTime.UnixMilli() //纳秒:常用于生成随机数字(如生成订单号、随机序列等) currentTimeUnixNano := currentTime.UnixNano() //方法【Unix】将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒) currentTimeUnix := currentTime.Unix() fmt.Printf("当前系统年=%v,月=%v,日=%v,时=%v,分=%v,秒=%v,当前系统毫秒数=%v,当前系统纳秒数=%v,Unix时间=%v \n", currentTimeYear, currentTimeDay, currentTimeMonth, currentTimeHour, currentTimeMinute, currentTimeSecond, currentTimeUnixMilli, currentTimeUnixNano, currentTimeUnix) //Duration 类型用于表示两个时刻 ( Time ) 之间经过的时间,以 纳秒 ( ns ) 为单位。 点击进去可以看到是time里面的自定义类型:type Duration int64 start_time := time.Now() // 空循环 uint32 的最大值次数 const UINT32_MAX uint32 = ^uint32(0) var i uint32 for i = 0; i < UINT32_MAX; i += 1 { } end_time := time.Now() spand_time := time.Duration(end_time.Sub(start_time)) fmt.Printf("空循环 uint32 的最大值次数耗时时间(s):%v \n", spand_time.Seconds()) println("------时间格式化-----") var now = time.Now() // 以下的数字都是固定的值,不能更换,据说2006/01/02 15:04:05是创始人思考创建go的时间 fmt.Println(now.Format("2006")) // 2022 fmt.Println(now.Format("01")) // 04 fmt.Println(now.Format("02")) // 30 fmt.Println(now.Format("15")) // 10 fmt.Println(now.Format("04")) // 52 fmt.Println(now.Format("05")) // 16 // 数字之外的其它字符可以更换 fmt.Println(now.Format("2006/01/02 15:04:05")) // 2022/04/30 10:52:16 fmt.Println(now.Format("2006-01-02 15:04:05")) // 2022-04-30 10:52:16 println("------sleep练习-----") sleep_start_time := time.Now() var num = 1 for { fmt.Printf("%v ", num) //休眠30ms time.Sleep(time.Millisecond * 30) if num == 5 { println() break } num++ } sleep_end_time := time.Now() sleep_spand_time := sleep_end_time.Sub(sleep_start_time) fmt.Printf("sleep 测试耗费时间(ms):%v \n", sleep_spand_time.Milliseconds()) println("------时间戳 <-互转-> 日期字符串-----") // 时间戳转换年月日时分秒(一个参数是秒,另一个参数是纳秒) //Unix返回与给定Unix时间相对应的本地时间, //秒和纳秒。 var time_1 = time.Unix(1595289901, 0) var timeStr = time_1.Format("2006-01-02 15:04:05") fmt.Println("时间戳转时间字符串结果:%v \n", timeStr) // 日期字符串转换成时间戳 var timeStr2 = "2022-11-25 14:44:52" var tmp = "2006-01-02 15:04:05" //转换模版 timeObj5, _ := time.ParseInLocation(tmp, timeStr2, time.Local) fmt.Println("日期字符串转换成ms时间戳结果:%v \n", timeObj5) }
管道(channel)
管道(channel)为引用类型,必须先初始化才能使用;本质是一个队列,有类型,而且线程安全.
管道的定义: var 变量名 chan 数据类型
代码示例
package main import ( "fmt" "math/rand" "time" ) func main() { println("------管道测试-------") //简单用法 simpleChannel := make(chan int, 2) simpleChannel <- 100 simpleChannel <- 200 //取出管道数据丢弃 <-simpleChannel channelInfo := <-simpleChannel fmt.Printf("读取simpleChannel管道数据: %v \n", channelInfo) intsChannel := make(chan int, 50) //协程相关在后续进阶篇中介绍,相当于开启一个新的线程执行逻辑,不阻塞main()线程的逻辑执行 //开启协程,用于写数据 go func() { for true { writeNum := rand.Intn(100) intsChannel <- writeNum fmt.Printf("写入管道数据: %v \n", writeNum) time.Sleep(time.Millisecond * 500) } }() //开启协程,用于读数据 go func() { for true { //读取管道数据 readInfo := <-intsChannel fmt.Printf("读取管道数据: %v \n", readInfo) time.Sleep(time.Millisecond * 500) } }() //防止数据还没有在协程里打印,main函数退出,main()执行结束后其他相关的协程也会结束 time.Sleep(time.Second * 3) //程序结束 }
运行结果
------管道测试------- 读取simpleChannel管道数据: 200 写入管道数据: 81 读取管道数据: 81 写入管道数据: 87 读取管道数据: 87 写入管道数据: 47 读取管道数据: 47 写入管道数据: 59 读取管道数据: 59 写入管道数据: 81 读取管道数据: 81 写入管道数据: 18 读取管道数据: 18
学海无涯,吾生也有涯,而知也无涯,以有涯随无涯. 加油👍
后续还会加入进阶篇,有兴趣的点个关注吧