1.环境搭建
1.1 Go 语言环境安装
如果打不开可以使用这个地址:https://golang.google.cn/dl/。
VMware中Ubuntu18.04安装 VMware Tools
Ubuntu18.04.3虚拟机安装步骤(图文教程,非常详细!!!)
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&request_id=162572775816780262561696&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-107190041.first_rank_v2_pc_rank_v29_1&utm_term=ubuntu%E5%AE%89%E8%A3%85go&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
package main
import "fmt"
func main(){
fmt.Println("hello,ling")
}
开发工具使用vscode
1.2语言结构
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
2.基础语法
每个语句不需要以分号;结尾,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(字符串拼接)
// %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 等等。
虚数的定义:虚数的平方是负数。如:
但是,正数的平方是正数、负数的平方也是正数,也就是一个数的平方永远是正数或零。
复数是实数和虚数的组合:
如: 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:
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声明全局变量
全局变量允许声明但不使用的。
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
4.2值类型和引用类型
值类型包括:int、float、bool 、string 、数组和结构体;
值类型:变量直接存储值,内存通常在栈中分配;
引用类型包括:指针、slice切片、map、管道chan、interface等;
引用类型:变量存储一个地址,地址对应的空间才真正存储数据(值),内存通常在堆上分配。当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收;
当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝:
定义的变量、导入的包如果没有使用会报错。
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
5.常量
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
- 显式类型定义:
const b string = "abc"
- 隐式类型定义:
const b = "abc"
常量还可以用作枚举:
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 可以简写为如下形式:
const (
a = iota
b
c
)
6.运算符
6.1 位运算符
6.2 其他运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量储存地址 | &a;将给出变量的实际地址 |
* | 指针变量 | *a;是一个指针变量 |
Go语言的格式化输出中%d%T%v%b等的含义
Printf会把%d转义,Println不会。
operator3 c = 200; 后面加分号了没有报错,编译器会删除或自动补充;分号。
7.条件语句
if
if……else
if嵌套语句
switch语句
select语句
select是Go中的一个控制结构,类似于用于通信的 switch 语句。每个case必须是一个通信操作,要么是发送要么是接收。
select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。
注意:Go 没有三目运算符,所以不支持 ?: 形式的条件判断。
8.循环语句
syntax error: non-declaration statement outside function body
语法错误:函数体外的非声明语句
8.1 for
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
}
8.2 For- range 循环
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语言使用的是值传递,即在调用过程中不会影响到实际参数。
值传递浅拷贝,引用传递会影响到实际参数。
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 函数作为实参?
看不懂
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 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
局部变量
//声明局部变量var a, b, c int//初始化参数a = 20b = 11c = a + b
全局变量
在函数体外声明的变量,可以在整个包甚至外部包使用。
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)}
package mainimport "fmt"//声明全局变量var g int = 20func main() { //声明局部变量 var g int = 10 fmt.Printf ("结果: g = %d\n", g)}
以上实例执行输出结果为:
结果: g = 10
形式参数
形式参数会作为函数的局部变量来使用。
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
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] ) }}
二维数组
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.指针
一个指针变量指向一个值的内存地址。
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
var p *string fmt.Println(p) fmt.Printf("p的值是%v\n", p) if p != nil { fmt.Println("非空") } else{ fmt.Println("空值")
指向指针的指针
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
内建函数,用于分配内存,第一个参数是类型,返回值是类型的指针(返回值是一个指向新分配类型零值的指针),其值被初始化为“零”
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)}
输出:
id type: *int value: 0name type: *string value: flag type: *bool value: false
从上述例子可以看到,初始化的“零”值根据类型不同而不同,整数初始化为 0,字符串初始化为空,bool类型初始化为 false。
make
内建函数,仅用于分配和初始化slice、map以及channel类型的对象,三种类型都是结构。返回值为类型(具体传入的类型),而不是指针。
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) }}
输出
[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的区别
- 都是在堆上分配内存;
- new对指针类型分配内存,返回值是分配类型的指针,new不能直接对slice、map、channel分配内存;
- make仅用于slice、map和channel的初始化,返回值为类型本身,而不是指针;
13.结构体
%d就是普通的输出了
%2d是将数字按宽度为2,采用右对齐方式输出,若数据位数不到2位,则左边补空格
%02d,和%2d差不多,只不过左边补0
%.2d没见过,但从执行效果来看,和%02d一样
14.切片?
1.1.5. 超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。?
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重新分配规律:?
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
str := "Hello world" s := []byte(str) //中文字符需要用[]rune(str)
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.并发编程
18.1 goroutine
进程---》线程---》协程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程,占用CPU资源少。
并发:在一个CPU上,比如有10个线程,每个线程执行10毫秒(进行轮询操作),从人的角度看,好像这10个线程都在运行,但是从微观上看,在某一个时间点看,其实只有一个线程在执行。
并行:在多个CPU上(10个),比如有10个线程,每个线程执行10毫秒(各自在不同CPU上执行),从人的角度看,这10个线程都在运行,但是从微观上看,在某个时间点看,也同时有10个线程在执行。
go协程特点:
- 有独立的栈空间
- 共享程序的堆控件
- 调度由用户控制
- 协程是轻量级的线程
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。
18.2 goroutine的调度模型---MGP
- M:操作系统的主线程(物理线程)
- P:协程执行需要的上下文
- G:协程
18.3 管道channel
介绍
- 协程之间的通信
- 本质是数据结构-队列,FIFO
- 线程安全,多个goroutine访问时,不需要加锁,就是说channel本身就是线程安全的
- channel有类型的,int类型的只能存放int数据类型
- 引用类型,必须初始化才能写入数据,make后才能使用
管道读写,只读,只写
- 读写:var chan1 chan int
- 只读:var chan2 chan<- int
- 只写:var chan3 <-chan int
<- 前只写,后只读
18.4 协程安全
once sync.Once
uartMtx sync.Mutex 互斥锁(自旋锁)
cfgCurrentMutex sync.RWMutex //当前配置文件读写锁
19.面对对象
结构体
- Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位
- 基于struct来实现OOP特性的
- 继承是通过匿名字段来实现
结构体是值类型,默认为值拷贝。
.的优先级比*高。
继承
type A struct { }type B struct { A}
结构体使用的注意事项
-
结构体的所有字段在内存中是连续的。
-
结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)。
-
结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是相互间可以强转。
type integer intfunc main(){ var i integer = 10 var j int 20 j=int(i)}
方法
Default方法和ImageTarget类型绑定
// ImageTarget 热成像图像目标参数
type ImageTarget struct {
Enable bool `description:"使能"`
Emissivity float32 `description:"发射率"`
ReflectedTemp float32 `description:"反射温度"`
AtmosphereTemp float32 `description:"大气温度"`
Distance float32 `description:"目标距离"`
Transmittance float32 `description:"大气透过率"`
}
// (it *ImageTarget)
// 1.Default方法和ImageTarget类型绑定
// 2.类的实现方法
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
}
函数和方法的区别:
-
调用方式不同
函数的调用方式: 函数名(实参列表)
方法的调用方式: 变量.方法名(实参列表)
-
对于普通函数,接受者为值类型时,不能将指针类型的数据直接传递,反之亦然。
-
对于方法(如struct的方法),接受者为值类型时,可以直接用指针类型的变量调用方法,反之亦然。
总结:
- 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和那个类型绑定。
- 如果是值类型,比如(p Person),则是值拷贝;如果是指针类型,比如是(p *Person)则是地址拷贝。
工厂模式
没有构造函数,使用场景:当结构体开头为小写,在别的地方要调用。
model student.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
func main(){ var stu = model.NewStudent("tom~", 88.8) fmt.Println(*stu) //&{...} fmt.Println("name=",stu.Name,"score=", stu.Score)}
如果字段Score小写,score,私有方法公有属性。
func (s *student) GetScore() float64{ reture s.score}
封装
封装把抽象出的字段和字段的操作封装在一起,数据被保护在内部;程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。
理解和好处:
隐藏实现细节,可以对数据进行验证,保证安全合理。
如何体现封装
- 对结构体中的属性进行封装
- 通过方法,包 实现封装
继承
如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承的特性。
type Goods struct{ Name string Price int}type Book struct{ Goods //这里就是嵌套匿名结构体Goods Writer string}
模糊(ambiguous)不能确定值
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的关系。
注意事项和细节:
- 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
- 接口中所有的方法都没有方法体,即都是没有实现的方法。
- 空接口interface{}没有任何方法,所有所有类型都实现了空接口,即我们可以把任何一个变量付给空接口。
类型断言
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言。
var x interface{}var b float32 = 1.1x=b //空接口可以接受任意类型//x=》float32 [使用类型断言]y := x.(float32)fmt.Printf("y的类型是%T 值是 %v",y,y)
对上面代码的说明:在进行类型断言时,如果类型不匹配就会报panic,因此进行类型断言是,要确保原来的空接口指向的就是断言的类型。
类型断言(带判断的)
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
深拷贝浅拷贝
深拷贝 内容一样,改变其中一个对象的值时,另一个不会变化。
浅拷贝 内容和内存地址一样,改变其中一个对象的值时,另一个同时变化。
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)
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.日期
//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
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