createdtime 20210917
updatedtime 20210929
author venki.chen
- golang中没有while和do while循环。
- 生成随机数。
rand.Seed(time.Now().UnixNano())
n := rand.Intn(100) + 1 随机数范围[0,100]
-
函数可以用函数值,也可以不用函数值。
-
go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
-
包的注意事项和细节:
- 包名尽可能与所在文件夹名字保持一致;
- package指令在文件的第一行;
- 引入包的路径,是从src下面开始的;
- 注意首字母大小写的区别;
- 引入的包名可以取别名
import "util go_code/20210922/aaa"
,一旦取了别名,就需要使用别名; - 同一个文件中(同一个包中,相当于同一个命名空间)不能定义同一个函数;
- 多个返回值时,必须用括号,单个则不需要;
-
golang不支持重载。
-
golang函数支持返回值命名(这样做的好处就是,return时不用强调顺序)。
func getSumAndSub(n1 int, n2 int) (sum int, sub int) {
sub = n1 - n2 // 注意不是这样的 :=
sum = n1 + n2
return
}
- 函数支持可变参数(支持0到多个和1到多个)。
# 求出1到多个,注意形参的位置是固定的,可变参数必须放到形参列表最后
func sum(n1 int, args ...int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
- go文件中如果同时存在全局变量定义,init函数以及main函数,那么执行顺序是:全局变量定义->init函数->main函数。
- init函数主要功能就是完成初始化工作,类似于PHP中的构造函数
construct
。 - 匿名函数:如果我们只是希望某个函数使用一次,那就需要用到匿名函数。
- 闭包:
func addUpper() func(int) int {
var n int = 10// 匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构造闭包。n只会初始化一次。
return func(x int) int {
n += x
return n
}
}
- defer,当程序遇到defer时,会将defer后面的语句压入栈中,待程序执行完毕后,按照先入后出的方式出栈,在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。
- 函数传参用值拷贝还是引用拷贝取决于数据大小,小可以用值拷贝,大可以用引用拷;值拷贝:基本数据类型,int系列,float系列,bool,string,数据和结构体struct;引用传递:指针,slice切片,map,管道chan,interface等。
- 声明全局变量时,切记不要用类型推到
var age int = 20// 正确
name := "venki.chen"// 错误,类型推到相当于:var name string name = "venki.chen"
-
golang的编码统一为utf-8,ASCII码的字符(字母和数字)占一个字节,汉字占3个字节。
-
比较字符串是否相当,如果是用==,那么注意要区分大小写,如果用strings.EqualFold,那么就不用区分大小写。
-
字符串函数可以做到:
- 指定字符首次出现的位置(下标从0开始);
- 指定字符最后一次出现的位置(下标从0开始);
- 字符串替换(strings.Replace());
- 字符串变为数组(strings.Split());
- 字符串大小写转换(strings.ToLower()||strings.ToUpper());
- 字符串去掉左右两边的空格(strings.TrimSpace);
- 去掉字符串左右两边指定的字符(strings.Trim);
- 去掉左边或者右边的指定字符(strings.TrimLeft||strings.TrimRight);
- 判断字符串是否是指定字符串开头的(strings.HasPrefix());
- 判断字符串是否是指定字符串结尾的(strings.HasSuffix());
-
时间和日期相关的函数:
# 获取年
now := time.Now()
year := now.Year()
# 获取月(注意格式,返回来可能是因为的,需要强转下)
month := now.Month() || month := int(now.Month())
# 获取日
day := now.Day()
# 获取时
hour := now.Hour()
# 获取分
minute := now.Minute()
# 获取秒
second := now.Second()
# 格式化时间
printf("当前的时间是:%d/%d/%d %d:%d:%d",now.Year(),int(now.Month()),
now.Day(),now.Hour(),now.Minute(),now.Second()
)
# 另一种格式时间的方法
fmt.Printf("当前时间是:%v\n", now.Format("2006/01/02 15:04:05"))
fmt.Printf("当前时间是:%v\n", now.Format("2006-01-02 15:04:05"))
fmt.Printf("当前时间是:%v\n", now.Format("15:04:05"))
fmt.Printf("当前时间是:%v\n", now.Format("15-04-05"))
Printf
和Sprintf
的区别,前者,直接格式化输出,后者可以返回字符串用于变量接受后,方便接下来的使用。- 时间常量
new
主要用来给值类型的值进行内存分配的,make
主要用来的给引用类型的值分配内存的。- 异常错误处理
func errorTest() {
defer func() {
err := recover()
if err != nil {
fmt.Printf("err=%v\n", err)
}
}()
num1 := 100
num2 := 0
str := num1 / num2
fmt.Println(str)
}
-
数组是值类型。
-
数组的注意事项
- 数组是多个相同类型数据的组合;
- 长度一旦固定,则不允许越界;
- 数组内部的元素可以是任何数据类型(值类型或者引用类型),但是不能混用;
-
切片:当一组数据不确定时可以用切片存储,比如学生的数量,切片是数组的一个引用,切片可以理解为一个结构体,本质映射到一个数组。
-
切片注意细节:
- 切片初始化不能越界;
- 切片只定义是没有空间分配的;
- var slice = arr[:];
- 切片还可以切片;
- 切片可以追加切片
append
append(slice,slice…),不要少了三个点;
-
append原理:先创建一个切片,然后将原来的切片数据copy过去,再重新指向即可。
-
copy()切片时,当空间大小不足时,优先copy排在前面的:
// copy切片
var slice2 []int = []int{1, 2, 3, 4, 5}
var slice3 []int = make([]int, 2)
copy(slice3, slice2)
fmt.Println(slice2)
fmt.Println(slice3)// 1,2
- 引用类型的变量传参时传递的是地址,不管是形参还是实参。
- string底层是一个byte数组,所以可以进行切片处理;string本身不可变,即str[0] = “2”,不可行,可以通过先将字符串转换成[]byte切片,在修改,然后再转换成字符串。
- string转换成[]byte后,只能处理英文和数字,但是不能处理中文,原因是[]byte字节处理,而一个汉字是三个字节,解决方案:将string转换成[]rune即可,因为[]rune是按字符处理兼容汉字。
- slice、map、还有function不可以作为map的key,通常来说map的key最多的数据类型是,string和int,值value通常也是int和string,map的值可以是map。
- map声明后是不会分配内存的,只有make或者赋值后才开辟内存。
- map中的key是不可以重复,但是值可以重复,map时无序的。
- map案例:
// 案例;学生姓名和性别
students := make(map[string]map[string]string)
students["stu01"] = make(map[string]string, 3)
students["stu01"]["name"] = "陈文小超1"
students["stu01"]["sex"] = "男"
students["stu02"] = make(map[string]string, 3)
students["stu02"]["name"] = "陈文小超2"
students["stu02"]["sex"] = "男"
students["stu03"] = make(map[string]string, 3)
students["stu03"]["name"] = "陈文小超3"
students["stu03"]["sex"] = "男"
fmt.Println(students)
- map是不能保证有序的,如要排序通常转换成切片。
- map可以动态扩容。
- 可以认为,golang是基于struct实现面向对象的特性的;golang中面向接口编程是非常重要的。
- 基于结构体创建声明一个变量时,就已经分配了内存(即存在默认值)。
- 结构体实际上是自己定义的一类结构体,结构体天生支持面向对象的特性。
- 结构体是一个值类型。
- 引用类型(切片,map)声明时没有空间分配,不然值是nil。
- 两个结构体相互转换时,必须具备完全一样的内容(名字,个数,类型)。
- 结构体可以打tag。
- 自定义类型都可以用方法。
- 结构体中下面的写法也是可以的:
type Person {
Name string
}
func (person *Person) test() string {
return (*person).Name // 或者person.name go底层做了判断,自行处理
}
func main () {
var person Person
person.Name = "venki.chen"
&person.test() // 也可以换成person.test(),因为go底层处理,判断person的数据类型
}
- 如果实现了string()方法,那么fmt.Println()会自动调用string的方法。
- 注意下面:对于方法(如:struct方法),接收者为值类型时,可以直接用指针类的变量调用方法,反过来同样也可以。
type Person struct {
Name string
}
func (person Person) test {
}
func main () {
var p Person
p.test() // 也可以(&p).test()// 但是&p的调用还是值拷贝,并不是引用,主要取决于test的绑定类型。
}