GoLang语言学习记录(三)


createdtime 20210917

updatedtime 20210929

author venki.chen


  1. golang中没有while和do while循环。
  2. 生成随机数。
rand.Seed(time.Now().UnixNano())

n := rand.Intn(100) + 1 随机数范围[0,100]
  1. 函数可以用函数值,也可以不用函数值。

  2. go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。

  3. 包的注意事项和细节:

    • 包名尽可能与所在文件夹名字保持一致;
    • package指令在文件的第一行;
    • 引入包的路径,是从src下面开始的;
    • 注意首字母大小写的区别;
    • 引入的包名可以取别名 import "util go_code/20210922/aaa",一旦取了别名,就需要使用别名;
    • 同一个文件中(同一个包中,相当于同一个命名空间)不能定义同一个函数;
    • 多个返回值时,必须用括号,单个则不需要;
  4. golang不支持重载。

  5. golang函数支持返回值命名(这样做的好处就是,return时不用强调顺序)。

func getSumAndSub(n1 int, n2 int) (sum int, sub int) {
	sub = n1 - n2 // 注意不是这样的 :=
	sum = n1 + n2
	return
}
  1. 函数支持可变参数(支持0到多个和1到多个)。
# 求出1到多个,注意形参的位置是固定的,可变参数必须放到形参列表最后
func sum(n1 int, args ...int) int {
	sum := n1
	for i := 0; i < len(args); i++ {
		sum += args[i]
	}

	return sum
}
  1. go文件中如果同时存在全局变量定义,init函数以及main函数,那么执行顺序是:全局变量定义->init函数->main函数。
  2. init函数主要功能就是完成初始化工作,类似于PHP中的构造函数construct
  3. 匿名函数:如果我们只是希望某个函数使用一次,那就需要用到匿名函数。
  4. 闭包:
func addUpper() func(int) int {
	var n int = 10// 匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构造闭包。n只会初始化一次。
	return func(x int) int {
		n += x
		return n
	}
}
  1. defer,当程序遇到defer时,会将defer后面的语句压入栈中,待程序执行完毕后,按照先入后出的方式出栈,在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。
  2. 函数传参用值拷贝还是引用拷贝取决于数据大小,小可以用值拷贝,大可以用引用拷;值拷贝:基本数据类型,int系列,float系列,bool,string,数据和结构体struct;引用传递:指针,slice切片,map,管道chan,interface等。
  3. 声明全局变量时,切记不要用类型推到
var age int = 20// 正确
name := "venki.chen"// 错误,类型推到相当于:var name string name = "venki.chen"
  1. golang的编码统一为utf-8,ASCII码的字符(字母和数字)占一个字节,汉字占3个字节。

  2. 比较字符串是否相当,如果是用==,那么注意要区分大小写,如果用strings.EqualFold,那么就不用区分大小写。

  3. 字符串函数可以做到:

    • 指定字符首次出现的位置(下标从0开始);
    • 指定字符最后一次出现的位置(下标从0开始);
    • 字符串替换(strings.Replace());
    • 字符串变为数组(strings.Split());
    • 字符串大小写转换(strings.ToLower()||strings.ToUpper());
    • 字符串去掉左右两边的空格(strings.TrimSpace);
    • 去掉字符串左右两边指定的字符(strings.Trim);
    • 去掉左边或者右边的指定字符(strings.TrimLeft||strings.TrimRight);
    • 判断字符串是否是指定字符串开头的(strings.HasPrefix());
    • 判断字符串是否是指定字符串结尾的(strings.HasSuffix());
  4. 时间和日期相关的函数:

# 获取年
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"))
  1. PrintfSprintf的区别,前者,直接格式化输出,后者可以返回字符串用于变量接受后,方便接下来的使用。
  2. 时间常量
  3. new主要用来给值类型的值进行内存分配的,make主要用来的给引用类型的值分配内存的。
  4. 异常错误处理
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)
}
  1. 数组是值类型。

  2. 数组的注意事项

    • 数组是多个相同类型数据的组合;
    • 长度一旦固定,则不允许越界;
    • 数组内部的元素可以是任何数据类型(值类型或者引用类型),但是不能混用;
  3. 切片:当一组数据不确定时可以用切片存储,比如学生的数量,切片是数组的一个引用,切片可以理解为一个结构体,本质映射到一个数组。

  4. 切片注意细节:

    • 切片初始化不能越界;
    • 切片只定义是没有空间分配的;
    • var slice = arr[:];
    • 切片还可以切片;
    • 切片可以追加切片append append(slice,slice…),不要少了三个点;
  5. append原理:先创建一个切片,然后将原来的切片数据copy过去,再重新指向即可。

  6. 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
  1. 引用类型的变量传参时传递的是地址,不管是形参还是实参。
  2. string底层是一个byte数组,所以可以进行切片处理;string本身不可变,即str[0] = “2”,不可行,可以通过先将字符串转换成[]byte切片,在修改,然后再转换成字符串。
  3. string转换成[]byte后,只能处理英文和数字,但是不能处理中文,原因是[]byte字节处理,而一个汉字是三个字节,解决方案:将string转换成[]rune即可,因为[]rune是按字符处理兼容汉字。
  4. slice、map、还有function不可以作为map的key,通常来说map的key最多的数据类型是,string和int,值value通常也是int和string,map的值可以是map。
  5. map声明后是不会分配内存的,只有make或者赋值后才开辟内存。
  6. map中的key是不可以重复,但是值可以重复,map时无序的。
  7. 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)
  1. map是不能保证有序的,如要排序通常转换成切片。
  2. map可以动态扩容。
  3. 可以认为,golang是基于struct实现面向对象的特性的;golang中面向接口编程是非常重要的。
  4. 基于结构体创建声明一个变量时,就已经分配了内存(即存在默认值)。
  5. 结构体实际上是自己定义的一类结构体,结构体天生支持面向对象的特性。
  6. 结构体是一个值类型。
  7. 引用类型(切片,map)声明时没有空间分配,不然值是nil。
  8. 两个结构体相互转换时,必须具备完全一样的内容(名字,个数,类型)。
  9. 结构体可以打tag。
  10. 自定义类型都可以用方法。
  11. 结构体中下面的写法也是可以的:
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的数据类型
}
  1. 如果实现了string()方法,那么fmt.Println()会自动调用string的方法。
  2. 注意下面:对于方法(如:struct方法),接收者为值类型时,可以直接用指针类的变量调用方法,反过来同样也可以。
type Person struct {
    Name string
}

func (person Person) test {
    
}

func main () {
    var p Person
    p.test() // 也可以(&p).test()// 但是&p的调用还是值拷贝,并不是引用,主要取决于test的绑定类型。
}

猜你喜欢

转载自blog.csdn.net/qq_38721452/article/details/120554173
今日推荐