一、基本操作
小tip
变量
- :=
:=为短变量声明,只能用于函数内
Println与Printf
Println里不能出现%s %d之类的,但可以直接打印字符串,如:
fmt.Println("numbers ==", numbers)
匿名函数
使用场景:
- 由于函数内部无法申明函数,因此使用先声明函数,再使用函数
func main() {
f1 := func(x, y int) {
fmt.Println(x + y)
}
f1(10, 20)
}
- 立即执行函数。
func main() {
func (x,y int) {
fmt.Println(x+y)
}(100,300) //马上就传参数
}
切片
- Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
数组初始化:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//字面量方式:
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
切片初始化
var identifier []type
//字面量
s :=[] int {1,2,3 }
//make方式
slice1 := make([]type, len)
//可指定容量:make([]T, length, capacity)
s :=make([]int,len,cap)
切片的规则:(和Python的字符串基本一样)
如:
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
append和copy函数
var numbers []int //空切片
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
3.集合
定义:
/* 使用map关键字声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)
使用range访问map:得到的是key。
判断是否key存在:
/* 创建map */
countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
capital, ok := countryCapitalMap["India"] /*如果确定是真实的,则存在,否则不存在 */
fmt.Println(capital) //若不存在,则为空;若存在返回value
fmt.Println(ok) //true or false
删除集合元素:delete()
delete(countryCapitalMap, "France")
defer
应用场景:多用于资源释放
- 把defer后面的语句延迟到函数即将返回的时候再执行。
- 若有多个defer,则按照栈的结构,先进后出执行defer的内容。
defer执行时机:
return分为两步:赋值和真正返回指令
package main
import "fmt"
func f1() int {
x := 5
defer func() {
x++ //修改的是x不是返回值
}()
return x
}
func f2() (x int) {
defer func() {
x++ //
}()
return 5
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x //返回值=y=x=5
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
}
//执行结果:
5
6
5
5
GO语言接口
接口:一种数据类型。
panic
package main
import "fmt"
func f1() {
fmt.Println("A")
}
func f2() {
defer func() {
fmt.Println("出错了,后面不执行了...")
}() //defer一定要在panic申明之前定义
panic("有错,终止...")
fmt.Println("我会被执行吗") //不会
}
func f3() {
fmt.Println("看看我..")
}
func main() {
f1()
f2()
f3()
}
自定义类型和类型别名
type myint int //自定义类型
type yourint=int //类型别名
自定义类型永久的将数据类型改成自己定义的了,但类型别名本质还是原本的数据类型。
结构体
//基本形式
type person struct {
name, city string
age int8
}
func main() {
//实例化
var p1 person
p1.name = "Chongqing"
p1.age = 19
fmt.Printf("p1=%v\n", p1) //p1={Chongqing 19}
fmt.Printf("p1=%#v\n", p1)//p1=main.person{name:"Chongqing", city:"", age:19}
}
用new
创建指针类型结构体
var p2 = new(person)
fmt.Printf("%T\n", p2) //*main.person 一个结构体指针
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}
//下面写法等价于用new
p3 := &person{}
注:空结构体是不占用空间的。
为结构体创建方法:
func (p person) speak(){
fmt.Println("嘿嘿嘿")
}
method与receiver
receiver的概念就类似于其他语言中的this或者 self。
//定义
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
// 使用值接收者
func (p Person) SetAge2(newAge int8) {
p.age = newAge
}
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {
p.age = newAge
}
什么时候应该使用指针类型接收者
- 需要修改接收者中的值
- 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。
包的用法
- 一个包可以简单理解为一个存放.go文件的文件夹。该文件夹下面的所有.go文件都要在非注释的第一行添加如下声明,声明该文件归属的包。
package name //可以不和文件夹的名称相同
- 包名为
main
的包是应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件。
标识符可见性
在Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)的。在一个包内部只有首字母大写的标识符才是对外可见的。(包括变量和函数名都是只有大写可以被外部访问)
init函数
- 不接收任何参数也没有任何返回值
- 当程序启动的时候,init函数会按照它们声明的顺序自动执行。
- 每一个包初始化的时候都是先执行依赖的包中声明的init函数再执行当前包中声明的init函数。
GO module
接口
- 是一个类型
//定义
type speaker interface{
speak() //只要用speak()方法的对象和类型,都可看做是speaker类型。
}
//写函数
func sp(obj speaker){
obj.speak() //接收的x是一个接口,不用管他到底是什么类型,执行x的speak方法
}
//调用
func main(){
var p person
p.name="niu"
sp(p) //等价于执行了p.speak(). sp函数可以传任意有speak方法的类型
//法二 先定义一个接口类型变量
var s1 speaker
p1:=person{
name:"cat"
}
s1=p1
sp(s1) //调用
}
注意,一种类型的方法需要有所有interface中的方法
值接收者实现接口
**还可以使用指针接收实现接口的方法:**只需要在接口对应的函数方法接收参数处,加个*即可
类型与接口关系
多个类型对应一个接口
- 先定义多接口
// Mover 接口
type Mover interface {
Move()
}
- 定义
type Dog struct {
Name string
}
// 实现Mover接口
func (d Dog) Move() {
fmt.Printf("%s会动\n", d.Name)
}
// Car 汽车结构体类型
type Car struct {
Brand string
}
// Move Car类型实现Mover接口
func (c Car) Move() {
fmt.Printf("%s速度70迈\n", c.Brand)
}
- 调用接口
var obj Mover
obj = Dog{Name: "旺财"}
obj.Move()
obj = Car{Brand: "宝马"}
obj.Move()
空接口
空接口是指没有定义任何方法的接口类型,空接口类型的变量可以存储任意类型的值。
// Any 不包含任何方法的空接口类型
type Any interface{}
//或者:
var x interface{} // 声明一个空接口类型变量x
作用
- 空接口作为函数的参数,接收任意类型的函数参数。
// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
- 使用空接口实现可以保存任意值的字典。(不用事先声明)
// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "沙河娜扎"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
接口值
类型断言
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败
var n Mover = &Dog{Name: "旺财"}
v, ok := n.(*Dog) //看这里的用法
if ok {
fmt.Println("类型断言成功")
v.Name = "富贵" // 变量v是*Dog类型
} else {
fmt.Println("类型断言失败")
}
error接口
1、基本定义类型
Go 语言中使用一个名为 error 接口来表示错误类型。默认零值为nil
type error interface {
Error() string
}
error 接口只包含一个方法——Error,这个函数需要返回一个描述错误信息的字符串。通常将调用函数返回的错误与nil进行比较,以此来判断函数是否返回错误。如:
file, err := os.Open("./xx.go")
if err != nil {
fmt.Println("打开文件失败,err:", err)
return
}
二、自定义错误
- 使用errors包提供的New函数:errors.New
- 定义一个错误变量
var EOF = errors.New("EOF")
- 用于函数返回
func queryById(id int64) (*Info, error) {
if id <= 0 {
return nil, errors.New("无效的id")
}
}