ga1400-golang基础

lling1234 · · 282 次点击 · 开始浏览    置顶
# 1.环境搭建 ## 1.1 Go 语言环境安装 如果打不开可以使用这个地址:<https://golang.google.cn/dl/>。 VMware中Ubuntu18.04安装 VMware Tools Ubuntu18.04.3虚拟机安装步骤(图文教程,非常详细!!!) https://blog.csdn.net/qq_42372031/article/details/100588245?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-10.base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-10.base linux安装go []: https://blog.csdn.net/weixin_42031162/article/details/107190041?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162572775816780262561696%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&amp;request_id=162572775816780262561696&amp;biz_id=0&amp;utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-107190041.first_rank_v2_pc_rank_v29_1&amp;utm_term=ubuntu%E5%AE%89%E8%A3%85go&amp;spm=1018.2226.3001.4187 sudo tar -C /usr/local -xzf go1.16.5.linux-amd64.tar windows安装目录 D:\ling\Go 工作目录 D:\ling\Go_WorkSpace ```go package main import "fmt" func main(){ fmt.Println("hello,ling") } ``` 开发工具使用vscode ## 1.2语言结构 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。 # 2.基础语法 每个语句不需要以分号;结尾,go编译器自动完成。 若果将多个语句写在同一行,必须使用;区分,实际开发不鼓励,定义变量、更改变量值、输出语句等后面加上;结尾也不报错。 ```go package main import ( "fmt" ) func main(){ //%d 表示整型数字,%s表示字符串 var stockcode=123 var enddate="2021-7-7 15:17:52" var url="Code=%d&endDate=%s" var target_url=fmt.Sprintf(url,stockcode,enddate) fmt.Println(target_url) } //输出结果为: //Code=123&endDate=2021-7-7 15:17:52 ``` 单行注释// 多行注释/* */ 无效的标识符: - 1ab(以数字开头) - case(个语音的关键字) - a+b(运算符是不允许的) 字符串用 + 拼接 fmt.Println("Google" + "Runoob") Go 语言中变量的声明必须使用空格隔开 var age int; ## 2.1 格式化字符串 Sprintf(字符串拼接) ```go // %d 表示整型数字,%s 表示字符串 var stockcode=123 var enddate="2020-12-31" var url="Code=%d&endDate=%s" var target_url=fmt.Sprintf(url,stockcode,enddate) fmt.Println(target_url) ``` # 3.数据类型 ## 3.1 数字类型 | 序号 | 类型和描述 | | ---- | ------------------------------------------------------------ | | 1 | uint8 无符号8位整型(0到255) | | 2 | unit16 无符号16位整型(0到65535) | | 3 | unit32 无符号32位整型(0 到 4294967295) | | 4 | unit64 无符号64位整型(0 到 18446744073709551615) | | 5 | int8 有符号8位整型(-128到127) | | 6 | int16 有符号16位整型(-32768 到 32767) | | 7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) | | 8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) | ## 3.2 浮点型 | 序号 | 类型和描述 | | ---- | ----------------------------- | | 1 | float32 IEEE-754 32位浮点型数 | | 2 | float64 IEEE-754 64位浮点型数 | | 3 | complex64 32位实数和虚数 | | 4 | complex128 64位实数和虚数 | 实数包括: - 整数 : 像 0、1、2、3、-1、-2 等等。 - 有理数: 像 3/4、0.125、0.333……、1.1 等等。 - 无理数:想 **π** , √2 等等。 虚数的定义:虚数的平方是负数。如: ![img](https://gitee.com/ling66611/picgo-image/raw/master/master/20201204164934668.png) 但是,正数的平方是正数、负数的平方也是正数,也就是一个数的平方永远是正数或零。 复数是实数和虚数的组合: ![img](https://gitee.com/ling66611/picgo-image/raw/master/master/20201204164708410.gif) 如: 1 + i、 39 + 3i、 0.8 − 2.2i、 −2 + π i、 √2 + i/2 注意:复数是两个数加起来的,一个是实数部分,一个是虚数部分。 但这两部分都可以是 0 ,所以所有实数和虚数都是复数。 ## 3.3其他数字类型 | 序号 | 类型和描述 | | ---- | ------------------------------------ | | 1 | byte 类似uint8 | | 2 | rune 类似int32 | | 3 | uint 32或64位 | | 4 | int 与uint一样大 | | 5 | uintptr 无符号整型,用于存放一个指针 | 但是全局变量是允许声明但不使用的 如果你想要交换两个变量的值,则可以简单地使用 **a, b = b, a**,两个变量的类型必须是相同。 空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。 # 4.变量 - 以下几种类型为 **nil**: ```go var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口 ``` ## 4.1声明全局变量 全局变量允许声明但不使用的。 ```go // 这种因式分解关键字的写法一般用于声明全局变量 var ( vname1 v_type1 vname2 v_type2 ) ``` ## 4.2值类型和引用类型 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值: ![4.4.2_fig4.1](https://www.runoob.com/wp-content/uploads/2015/06/4.4.2_fig4.1.jpgrawtrue) 当使用等号 `=` 将一个变量的值赋值给另一个变量时,如:`j = i`,实际上是在**内存中将 i 的值进行了拷贝**: ![4.4.2_fig4.2](https://www.runoob.com/wp-content/uploads/2015/06/4.4.2_fig4.2.jpgrawtrue) 定义的变量、导入的包如果没有使用会报错。 空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。 # 5.常量 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。 - 显式类型定义: `const b string = "abc"` - 隐式类型定义: `const b = "abc"` 常量还可以用作枚举: ```go const ( Unknown = 0 Female = 1 Male = 2 ) ``` ## 5.1 iota ota,特殊常量,可以认为是一个可以被编译器修改的常量。 iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。 第一个 iota 等于 0,每当 iota **在新的一行被使用时,它的值都会自动加 1**;所以 a=0, b=1, c=2 可以简写为如下形式: ```go const ( a = iota b c ) ``` # 6.运算符 ## 6.1 位运算符 ![1625800862350](https://gitee.com/ling66611/picgo-image/raw/master/master/1625800862350.png) ## 6.2 其他运算符 | 运算符 | 描述 | 实例 | | ------ | ---------------- | ------------------------ | | & | 返回变量储存地址 | &a;将给出变量的实际地址 | | * | 指针变量 | *a;是一个指针变量 | ## Go语言的格式化输出中%d%T%v%b等的含义 ![1625651429541](https://gitee.com/ling66611/picgo-image/raw/master/master/1625651429541.png) ![在这里插入图片描述](https://gitee.com/ling66611/picgo-image/raw/master/master/20181108105759217.png) ![在这里插入图片描述](https://gitee.com/ling66611/picgo-image/raw/master/master/20181108105900423.png) Printf会把%d转义,Println不会。 operator3 c = 200; 后面加分号了没有报错,编译器会删除或自动补充;分号。 # 7.条件语句 if if……else if嵌套语句 switch语句 select语句 **select** 语句类似于 **switch** 语句,但是select**会随机执行一个可运行的case**。如果没有case可运行,它将阻塞,直到有case可运行。 *注意:Go 没有三目运算符,所以不支持* **?:** *形式的条件判断。* # 8.循环语句 syntax error: non-declaration statement outside function body 语法错误:函数体外的非声明语句 ## 8.1 for ```go func main() { sum := 0 for i := 0; i <= 10; i++ { sum += i } fmt.Println(sum) } ``` ## 8.2 For- range 循环 ```go package mainimport "fmt"func main() { strings := []string{"google", "runoob"} for i, s := range strings { fmt.Println(i, s) } numbers := [6]int{1, 2, 3, 5} for i,x:= range numbers { fmt.Printf("第 %d 位 x 的值 = %d\n", i,x) } } ``` 以上实例运行输出结果为: ``` 0 google1 runoob第 0 位 x 的值 = 1第 1 位 x 的值 = 2第 2 位 x 的值 = 3第 3 位 x 的值 = 5第 4 位 x 的值 = 0第 5 位 x 的值 = 0 ``` # 9.函数 ## 9.1 函数参数 函数如果使用参数,该变量可称为函数的形参。 形参就像定义在函数体内的局部变量。 调用函数,可以通过两种方式来传递参数: | 传递类型 | 描述 | | -------- | ------------------------------------------------------------ | | 值传递 | 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样**在函数中如果对参数进行修改,将不会影响到实际参数。** | | 引用传递 | 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么**在函数中对参数所进行的修改,将影响到实际参数。** | 默认情况下,go语言使用的是值传递,即在调用过程中不会影响到实际参数。 值传递浅拷贝,引用传递会影响到实际参数。 ```go func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 fmt.Printf("交换前,a 的值 : %d\n", a ) fmt.Printf("交换前,b 的值 : %d\n", b ) /* 调用 swap() 函数 * &a 指向 a 指针,a 变量的地址 * &b 指向 b 指针,b 变量的地址 */ swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n", a ) fmt.Printf("交换后,b 的值 : %d\n", b )}func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址上的值 */ *x = *y /* 将 y 值赋给 x */ *y = temp /* 将 temp 值赋给 y */} ``` ## 9.2 函数作为实参? 看不懂 ```go package mainimport "fmt"// 声明一个函数类型type cb func(int) int//函数作为参数传递,实现回调。func main() { testCallBack(1, callBack) testCallBack(2, func(x int) int { fmt.Printf("我是回调,x:%d\n", x) return x })}func testCallBack(x int, f cb) { f(x)}func callBack(x int) int { fmt.Printf("我是回调,x:%d\n", x) return x}//我是回调,x:1// 我是回调,x:2 ``` # 10.变量作用域 作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。 Go 语言中变量可以在三个地方声明: - 函数内定义的变量称为局部变量 - 函数外定义的变量称为全局变量 - 函数定义中的变量称为形式参数 局部变量 ```go //声明局部变量var a, b, c int//初始化参数a = 20b = 11c = a + b ``` 全局变量 在函数体外声明的变量,可以在整个包甚至外部包使用。 ```go package mainimport "fmt"/* 声明全局变量 */var g intfunc main() { /* 声明局部变量 */ var a, b int /* 初始化参数 */ a = 10 b = 20 g = a + b fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)} ``` ```go package mainimport "fmt"//声明全局变量var g int = 20func main() { //声明局部变量 var g int = 10 fmt.Printf ("结果: g = %d\n", g)} ``` 以上实例执行输出结果为: ``` 结果: g = 10 ``` 形式参数 形式参数会作为函数的局部变量来使用。 ```go package mainimport "fmt"//声明全局变量var a int = 20func main() { //main函数中声明局部变量 var a int = 10 var b int = 20 var c int = 0 fmt.Printf("main()函数中 a = %d\n", a); c = sum( a, b); fmt.Printf("main()函数中 c = %d\n", c);}//函数定义-两数之和func sum(a,b int) int { fmt.Printf("sum() 函数中 a = %d\n", a); fmt.Printf("sum() 函数中 b = %d\n", b); return a + b;} ``` a= 初始化局部变量和全局变量 | 数据类型 | 初始化默认值 | | -------- | ------------ | | int | 0 | | float | 0 | | pointer | nil | # 11.数组 float32默认精度为6( 后面保留6位小数) balance2[0] = 1000.000000 ```go package mainimport "fmt"func main() { var i,j,k int //声明数组的同时快速初始化数组 balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 55.12} //输出数组元素 for i = 0; i < 5; i++ { fmt.Printf("balance[%d] = %f\n", i, balance[i]) } fmt.Println("--------------") balance2 := [...]float32{1000.02, 20.22, 3.33, 7.11, 50.00} //输出每个数组元素的值 for j = 0; j < 5; j++ { fmt.Printf("balance2[%d] = %f\n", j, balance2[j] ) } fmt.Println("--------------") //将索引为1和3的元素初始化 balance3 := [5]float32{1:2.22,3:4.04} for k = 0; k < 5; k++ { fmt.Printf("balance3[%d] = %f\n", k, balance3[k] ) }} ``` ## 二维数组 ```go package mainimport "fmt"func main() { // //创建二维数组 // sites := [2][2]string{} // //向二维数组添加元素 // sites[0][0] = "hello" // sites[0][1] = "google" // sites[1][0] = "ling" // sites[1][1] = "yunjivision" // fmt.Println(sites) //-------------------------------------------- // //数组5行2列 // var a = [5][2]int{{0,0},{1,2},{2,4},{3,6},{4,8}} // var i, j int // //输出数组元素 // for i = 0; i < 5; i++ { // for j = 0; j < 2; j++ { // fmt.Printf("a[%d][%d] = %d\n", i, j, a[i][j] ) // } // } //-------------------------------------------- //创建监控的二维数组 animals := [][]string{} //创建三个一维数组,各数组长度不同 row1 := []string{"fish", "shark", "eel" } row2 := []string{"bird" } row3 := []string{"google", "ling" } //使用append()函数将一维数组添加到二维数组中 animals = append(animals, row1 ) animals = append(animals, row2 ) animals = append(animals, row3 ) //循环输出 for i := range animals { fmt.Printf("row: %v\n", i) fmt.Println(animals[i]) }} ``` for i := range animals { ... } 和java的foreach(增强for循环 for(int x:arr){...})有点相似。 数字可以用_下划线分割 fmt.Println(float64(c) / 1_000_000 ) # 12.指针 一个指针变量指向一个值的内存地址。 ```go int a int = 20 //声明实际变量var ip *int //声明指针变量ip = &afmt.Printf("a 变量的地址是:%x\n", &a)//指针变量的储存地址fmt.Printf("ip 变量存储的指针地址:%x\n", ip)//使用指针访问值fmt.Printf("ip 变量的值:%d\n", *ip) ``` 以上实例执行输出结果为: ``` a 变量的地址是: 20818a220ip 变量储存的指针地址: 20818a220*ip 变量的值: 20 ``` go指针不能进行偏移和运算,是安全指针。 3个概念:指针地址、指针类型和指针取值 go语言中的函数传参都是值拷贝,传递数据使用指针,而无须拷贝数据。2个符号:&(取地址)和*(根据地址取值)。 值类型:int、float、bool、string、array、struct ### 空指针 - 当一个指针被定义后没有分配到任何变量时,它的值为 nil ```go var p *string fmt.Println(p) fmt.Printf("p的值是%v\n", p) if p != nil { fmt.Println("非空") } else{ fmt.Println("空值") ``` ## 指向指针的指针 ```go package mainimport "fmt"func main() { var a int var ptr *int var pptr **int a = 3000 // 指针 ptr 地址 ptr = &a //指向 ptr 地址 pptr = &ptr fmt.Printf("变量 a = %d\n", a ) fmt.Printf("指针变量 *ptr = %d\n", *ptr ) fmt.Printf("指向指针的指针变量 *pptr = %d\n", **pptr )} ``` ## new和make ## new 内建函数,用于分配内存,第一个参数是类型,返回值是**类型的指针**(返回值是一个指向新分配类型零值的指针),其值被初始化为“零” ```go package mainimport "fmt" func main() { id := new(int) name := new(string) flag := new(bool) fmt.Printf("id type: %T value: %v\n", id, *id) fmt.Printf("name type: %T value: %v\n", name, *name) fmt.Printf("flag type: %T value: %v\n", flag, *flag)} ``` 输出: ```Go id type: *int value: 0name type: *string value: flag type: *bool value: false ``` 从上述例子可以看到,初始化的“零”值根据类型不同而不同,整数初始化为 0,字符串初始化为空,bool类型初始化为 false。 ## make 内建函数,仅用于分配和初始化slice、map以及channel类型的对象,三种类型都是结构。返回值为类型(具体传入的类型),而不是指针。 ```go package mainimport ( "fmt")func main() { // new() 用于分配内存,返回值是类型的指针 id := new(int) name := new(string) flag := new(bool) fmt.Printf("id type: %T value: %v\n", id, *id) fmt.Printf("name type: %T value: %v\n", name, *name) fmt.Printf("flag type: %T value: %v\n", flag, *flag) // make()仅用于分配和初始化slice、map以及channel类型的对象, // 三种类型都是结构。返回值为类型,而不是指针 //map fmt.Println("map:") var nameId = make(map[string]int, 0) fmt.Printf("nameId \ntype:%#v\n", nameId) nameId["golang"] = 1 nameId["cpp"] = 2 nameId["java"] = 3 for name, id := range nameId { fmt.Printf("name = %v,id = %v\n", name, id) } // slice var hobby = make([]string, 2, 100) //其中2是长度,100是容量 hobby[0] = "打篮球" hobby[1] = "羽毛球" fmt.Println("\nslice:") fmt.Printf("length = %v caps = %v\n", len(hobby), cap(hobby)) for _, name := range hobby { fmt.Println(name) } // chan ch := make(chan int, 3) ch <- 1 ch <- 33 ch <- 12 close(ch) fmt.Println("\nchannel:") for v := range ch { fmt.Println(v) }} ``` 输出 ```go [root@localhost test]# go run main.go map:nameId type: map[string]int{}name = Golang, id = 1name = C++, id = 2name = PHP, id = 3 slice:length = 2 caps = 100打篮球乒乓球 channel:128[root@localhost test]# ``` ## new和make的区别 1. 都是用于分配内存; 2. 都是在堆上分配内存; 3. new对指针类型分配内存,返回值是分配类型的指针,new不能直接对slice、map、channel分配内存; 4. make仅用于slice、map和channel的初始化,返回值为类型本身,而不是指针; # 13.结构体 %d就是普通的输出了 %2d是将数字按宽度为2,采用右对齐方式输出,若数据位数不到2位,则左边补空格 %02d,和%2d差不多,只不过左边补0 %.2d没见过,但从执行效果来看,和%02d一样 # 14.切片? ### 1.1.5. 超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。? ```go package mainimport ( "fmt")func main() { data := [...]int{0, 1, 2, 3, 4, 10: 0} s := data[:2:3] s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。 fmt.Println(s, data) // 重新分配底层数组,与原数组无关。 fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。} ``` 输出结果: ``` [0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0] 0xc4200160f0 0xc420070060 ``` 从输出结果可以看出,append 后的 s 重新分配了底层数组,并复制数据。如果只追加一个值,则不会超过 s.cap 限制,也就不会重新分配。 通常以 2 倍容量重新分配底层数组。在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制开销。或初始化足够长的 len 属性,改用索引号进行操作。及时释放不再使用的 slice 对象,避免持有过期数组,造成 GC 无法回收。 `len()`可以用来查看数组或slice的长度 `cap()`可以用来查看数组或slice的容量 ### 1.1.6. slice中cap重新分配规律:? ```go package mainimport ( "fmt")func main() { s := make([]int, 0, 1) c := cap(s) for i := 0; i < 50; i++ { s = append(s, i) if n := cap(s); n > c { fmt.Printf("cap: %d -> %d\n", c, n) c = n } }} ``` 输出结果: ``` cap: 1 -> 2 cap: 2 -> 4 cap: 4 -> 8 cap: 8 -> 16 cap: 16 -> 32 cap: 32 -> 64 ``` 英文字符byte 中间字符rune ```go str := "Hello world" s := []byte(str) //中文字符需要用[]rune(str) ``` ```go str := "你好,世界!hello world!" s := []rune(str) ``` golang slice data[:6:8] 两个冒号的理解 常规slice , data[6:8],从第6位到第8位(返回6, 7),长度len为2, 最大可扩充长度cap为4(6-9) 另一种写法: data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8 a[x:y:z] 切片内容 [x:y] 切片长度: y-x 切片容量:z-x # 15.map key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串 # 16.异常处理 Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。 # 17.网络编程 # 18.并发编程 协程:**独立**的**栈空间**,**共享堆空间**,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。 线程:一个**线程**上可以跑**多个协程**,**协程是轻量级的线程**。 goroutine runtitme.Gosched() 让出CPU时间片,重新等待安排任务 runtime.Goexit() 退出当前协程 ### runtime.GOMAXPROCS Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上 Go语言中的操作系统线程和goroutine的关系: - 1.一个操作系统线程对应用户态多个goroutine。 - 2.go程序可以同时使用多个操作系统线程。 - 3.goroutine和OS线程是多对多的关系,即m:n。 once sync.Once uartMtx sync.Mutex # 19.面对对象 ## 结构体 - Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位 - 基于struct来实现OOP特性的 - 继承是通过匿名字段来实现 结构体是值类型,默认为值拷贝。 .的优先级比*高。 继承 ```go type A struct { }type B struct { A} ``` ### 结构体使用的注意事项 1. 结构体的所有字段在内存中是连续的。 2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)。 3. 结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是相互间可以强转。 ```go type integer intfunc main(){ var i integer = 10 var j int 20 j=int(i)} ``` ## 方法 Default方法和ImageTarget类型绑定 ```go // ImageTarget 热成像图像目标参数type ImageTarget struct { Enable bool `description:"使能"` Emissivity float32 `description:"发射率"` ReflectedTemp float32 `description:"反射温度"` AtmosphereTemp float32 `description:"大气温度"` Distance float32 `description:"目标距离"` Transmittance float32 `description:"大气透过率"`}// (it *ImageTarget) // Default方法和ImageTarget类型绑定func (it *ImageTarget) Default() { it.Enable = false it.AtmosphereTemp = 298.2 it.Emissivity = 1 it.Distance = 0.5 it.ReflectedTemp = 298.2 it.Transmittance = 1} ``` 函数和方法的区别: 1. 调用方式不同 函数的调用方式: 函数名(实参列表) 方法的调用方式: 变量.方法名(实参列表) 2. 对于普通函数,接受者为值类型时,不能将指针类型的数据直接传递,反之亦然。 3. 对于方法(如struct的方法),接受者为值类型时,可以直接用指针类型的变量调用方法,反之亦然。 总结: 1. 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和那个类型绑定。 2. 如果是值类型,比如(p Person),则是值拷贝;如果是指针类型,比如是(p *Person)则是地址拷贝。 ## 工厂模式 没有构造函数,使用场景:当结构体开头为小写,在别的地方要调用。 model student.go ```go type student struct { Name string Score float64}//因为student结构体首字母小写,因此是只能在model十三//我们通过工厂模式来解决func NewStudent(n string, s float64) *student{ return &student{ Name:n, Score:s, }} ``` main.go ```go func main(){ var stu = model.NewStudent("tom~", 88.8) fmt.Println(*stu) //&{...} fmt.Println("name=",stu.Name,"score=", stu.Score)} ``` 如果字段Score小写,score,私有方法公有属性。 ```go func (s *student) GetScore() float64{ reture s.score} ``` ## 封装 封装把抽象出的字段和字段的操作封装在一起,数据被保护在内部;程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。 ### 理解和好处: 隐藏实现细节,可以对数据进行验证,保证安全合理。 ### 如何体现封装 1. 对结构体中的属性进行封装 2. 通过方法,包 实现封装 ## 继承 如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承的特性。 ```go type Goods struct{ Name string Price int}type Book struct{ Goods //这里就是嵌套匿名结构体Goods Writer string} ``` 模糊(ambiguous)不能确定值 ```go type A struct{ Name string age int}type B struct{ Name string Score float64}type C struct{ A B //Name string c.Name 可以直接找到}func main(){ var c C c.A.name = "ling"//如果不A会报错} ``` 没有重载 # 20.interface 把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。 高内聚低耦合,体现**多态**。接口更加灵活。 多态参数,多态数组。 继承是满足is-a的关系,接口满足like-a的关系。 ## 注意事项和细节: 1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例) 2. 接口中所有的方法都没有方法体,即都是没有实现的方法。 3. 空接口interface{}没有任何方法,所有所有类型都实现了空接口,即我们可以把任何一个变量付给空接口。 # 类型断言 类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言。 ![image-20210802200548433](https://gitee.com/ling66611/picgo-image/raw/master/master/image-20210802200548433.png) ```go var x interface{}var b float32 = 1.1x=b //空接口可以接受任意类型//x=》float32 [使用类型断言]y := x.(float32)fmt.Printf("y的类型是%T 值是 %v",y,y) ``` 对上面代码的说明:在进行类型断言时,如果类型不匹配就会报panic,因此进行类型断言是,要确保原来的空接口指向的就是断言的类型。 ## 类型断言(带判断的) ```go var x interface{} var b float32 = 1.1 x = b //空接口可以接受任意类型 //x=》float32 [使用类型断言] if y, ok := x.(float32); ok { fmt.Println("convert success") fmt.Printf("y的类型是%T 值是 %v", y, y) } else { fmt.Println("convert fail") } ``` items... interface{} 可变参数 func TypeJudge(items... interface{}){ } # nil 可以理解为空指针错误信息,error类型,指向error的指针,但是实际没有指向。 强类型语言,golang中有多种引用类型:pointer、interface、slice、map,channel, function; Failed to build the application: go: finding module for package github.com/astaxie/beego/server/web 这个是因为最新版的go启用了go.mod模式,也就是包管理工具,而管理包的目录未安装相应的模块 解决方案,关掉go.mod ``` go env -w GO111MODULE=off ``` Kind代表Type类型值表示的具体分类。零值表示非法分类。 # Golang的逗号OK模式 即:"**, OK**" 使用场景:在一个表达式返回2个参数的时候使用,第一个参数是一个值或者`nil`,第二个参数是`true`/`false`或者一个错误`error` # 深拷贝浅拷贝 深拷贝 内容一样,改变其中一个对象的值时,另一个不会变化。 浅拷贝 内容和内存地址一样,改变其中一个对象的值时,另一个同时变化。 ```go package deepCopyimport ( "fmt")// 定义一个Robot结构体type Robot struct { Name string Color string Model string}func main() { fmt.Println("深拷贝 内容一样,改变其中一个对象的值时,另一个不会变化。") robot1 := Robot{ Name: "小白-X型-V1.0", Color: "白色", Model: "小型", } robot2 := robot1 fmt.Printf("Robot 1:%s\t内存地址:%p \n", robot1, &robot1) fmt.Printf("Robot 2:%s\t内存地址:%p \n", robot2, &robot2) fmt.Println("修改Robot1的Name属性值") robot1.Name = "小白-X型-V1.1" fmt.Printf("Robot 1:%s\t内存地址:%p \n", robot1, &robot1) fmt.Printf("Robot 2:%s\t内存地址:%p \n", robot2, &robot2)} ``` 深拷贝:robot2 := robot1 浅拷贝:robot2 := &robot1 fmt.Println("浅拷贝 使用new方式") robot1 := new(Robot) ```go package mainimport ( "fmt")// 定义一个Robot结构体type Robot struct { Name string Color string Model string}func main() { fmt.Println("浅拷贝 内容和内存地址一样,改变其中一个对象的值时,另一个同时变化。") robot1 := Robot{ Name: "小白-X型-V1.0", Color: "白色", Model: "小型", } robot2 := &robot1 fmt.Printf("Robot 1:%s\t内存地址:%p \n", robot1, &robot1) fmt.Printf("Robot 2:%s\t内存地址:%p \n", robot2, robot2) fmt.Println("在这里面修改Robot1的Name和Color属性") robot1.Name = "小黑-X型-V1.1" robot1.Color = "黑色" fmt.Printf("Robot 1:%s\t内存地址:%p \n", robot1, &robot1) fmt.Printf("Robot 2:%s\t内存地址:%p \n", robot2, robot2)} ``` # 21.日期 ```go //godate工具类,go get github.com/kofoworola/godate//https://github.com/kofoworola/godatet := time.Now()date, _ := godate.Parse("2006-01-02", "2019-05-01")date.Year(int(t.Year())).Month(int(t.Month())).Day(int(t.Day())). Hour(int(t.Hour())).Minute(int(t.Minute())).Second(int(t.Second()))fmt.Println(date.Format("20060102150405")) //2008-10-30 11:03:12t2 := time.Now()t2.Format("20060102150405")fmt.Printf("当前年月日---- %d%d%d%d%d%d \n", t2.Year(), t2.Month(), t2.Day(), t2.Hour(), t2.Minute(), t2.Second())fmt.Println("1111111111111111111")fmt.Println(t2.Format("20060102150405"))fmt.Println("t3--------------")t3 := time.Now()fmt.Println(t3.Format("20060102150405")) ``` # 22.json ```go func (c *TimeController) Get() { t := time.Now() fmt.Println(t.Format("20060102150405")) //从配置文件中读取VIIDServerID VIIDServerID := beego.AppConfig.String("VIIDServerID") time := SystemTimeObject{ VIIDServerID: VIIDServerID, TimeMode: "22222", LocalTime: t.Format("20060102150405"), TimeZone: "TimeZone时区", } mapData := make(map[string]interface{}) mapData["SystemTimeObject"] = time c.Data["json"] = mapData c.ServeJSON() // c.Ctx.WriteString("api接口---------" + t.Format("20060102150405"))} ``` golang解析json数据,去传入参数 https://studygolang.com/articles/15882

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

282 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传